[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle:  LLVM\nAlignAfterOpenBracket: AlwaysBreak\nAllowShortFunctionsOnASingleLine: Inline\nBinPackArguments: false\nBinPackParameters: false\nBraceWrapping:\n  AfterFunction:   true\n  SplitEmptyFunction: false\nBreakBeforeBraces: Custom\nBreakStringLiterals: false\nColumnLimit: 128\nConstructorInitializerAllOnOneLineOrOnePerLine: true\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"OpenTelemetry Network Dev\",\n  \"image\": \"otel/opentelemetry-network-build-tools:latest\",\n  \"features\": {\n    \"ghcr.io/yonch/devcontainer-features/codex:1\": {}\n  },\n  \"workspaceFolder\": \"/workspaces/${localWorkspaceFolderBasename}\",\n  \"mounts\": [\n    \"source=${localEnv:HOME}/.codex/auth.json,target=/home/user/.codex/auth.json,readonly,type=bind\",\n    \"source=${localEnv:HOME}/.ccache,target=/home/user/.ccache,type=bind\"\n  ],\n  \"customizations\": {\n    \"vscode\": {\n      \"settings\": {},\n      \"extensions\": [\n        \"eamodio.gitlens\"\n      ]\n    }\n  },\n  \"remoteUser\": \"root\"\n}\n"
  },
  {
    "path": ".git/HEAD",
    "content": "ref: refs/heads/main\n"
  },
  {
    "path": ".git/config",
    "content": "[core]\n\trepositoryformatversion = 1\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n[remote \"origin\"]\n\turl = https://github.com/open-telemetry/opentelemetry-network\n\ttagOpt = --no-tags\n\tfetch = +refs/heads/main:refs/remotes/origin/main\n\tpromisor = true\n\tpartialclonefilter = blob:limit=1048576\n[branch \"main\"]\n\tremote = origin\n\tmerge = refs/heads/main\n"
  },
  {
    "path": ".git/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": ".git/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ncommitmsg=\"$(git rev-parse --git-path hooks/commit-msg)\"\ntest -x \"$commitmsg\" && exec \"$commitmsg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": ".git/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": ".git/hooks/fsmonitor-watchman.sample",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse IPC::Open2;\n\n# An example hook script to integrate Watchman\n# (https://facebook.github.io/watchman/) with git to speed up detecting\n# new and modified files.\n#\n# The hook is passed a version (currently 2) and last update token\n# formatted as a string and outputs to stdout a new update token and\n# all files that have been modified since the update token. Paths must\n# be relative to the root of the working tree and separated by a single NUL.\n#\n# To enable this hook, rename this file to \"query-watchman\" and set\n# 'git config core.fsmonitor .git/hooks/query-watchman'\n#\nmy ($version, $last_update_token) = @ARGV;\n\n# Uncomment for debugging\n# print STDERR \"$0 $version $last_update_token\\n\";\n\n# Check the hook interface version\nif ($version ne 2) {\n\tdie \"Unsupported query-fsmonitor hook version '$version'.\\n\" .\n\t    \"Falling back to scanning...\\n\";\n}\n\nmy $git_work_tree = get_working_dir();\n\nmy $retry = 1;\n\nmy $json_pkg;\neval {\n\trequire JSON::XS;\n\t$json_pkg = \"JSON::XS\";\n\t1;\n} or do {\n\trequire JSON::PP;\n\t$json_pkg = \"JSON::PP\";\n};\n\nlaunch_watchman();\n\nsub launch_watchman {\n\tmy $o = watchman_query();\n\tif (is_work_tree_watched($o)) {\n\t\toutput_result($o->{clock}, @{$o->{files}});\n\t}\n}\n\nsub output_result {\n\tmy ($clockid, @files) = @_;\n\n\t# Uncomment for debugging watchman output\n\t# open (my $fh, \">\", \".git/watchman-output.out\");\n\t# binmode $fh, \":utf8\";\n\t# print $fh \"$clockid\\n@files\\n\";\n\t# close $fh;\n\n\tbinmode STDOUT, \":utf8\";\n\tprint $clockid;\n\tprint \"\\0\";\n\tlocal $, = \"\\0\";\n\tprint @files;\n}\n\nsub watchman_clock {\n\tmy $response = qx/watchman clock \"$git_work_tree\"/;\n\tdie \"Failed to get clock id on '$git_work_tree'.\\n\" .\n\t\t\"Falling back to scanning...\\n\" if $? != 0;\n\n\treturn $json_pkg->new->utf8->decode($response);\n}\n\nsub watchman_query {\n\tmy $pid = open2(\\*CHLD_OUT, \\*CHLD_IN, 'watchman -j --no-pretty')\n\tor die \"open2() failed: $!\\n\" .\n\t\"Falling back to scanning...\\n\";\n\n\t# In the query expression below we're asking for names of files that\n\t# changed since $last_update_token but not from the .git folder.\n\t#\n\t# To accomplish this, we're using the \"since\" generator to use the\n\t# recency index to select candidate nodes and \"fields\" to limit the\n\t# output to file names only. Then we're using the \"expression\" term to\n\t# further constrain the results.\n\tmy $last_update_line = \"\";\n\tif (substr($last_update_token, 0, 1) eq \"c\") {\n\t\t$last_update_token = \"\\\"$last_update_token\\\"\";\n\t\t$last_update_line = qq[\\n\"since\": $last_update_token,];\n\t}\n\tmy $query = <<\"\tEND\";\n\t\t[\"query\", \"$git_work_tree\", {$last_update_line\n\t\t\t\"fields\": [\"name\"],\n\t\t\t\"expression\": [\"not\", [\"dirname\", \".git\"]]\n\t\t}]\n\tEND\n\n\t# Uncomment for debugging the watchman query\n\t# open (my $fh, \">\", \".git/watchman-query.json\");\n\t# print $fh $query;\n\t# close $fh;\n\n\tprint CHLD_IN $query;\n\tclose CHLD_IN;\n\tmy $response = do {local $/; <CHLD_OUT>};\n\n\t# Uncomment for debugging the watch response\n\t# open ($fh, \">\", \".git/watchman-response.json\");\n\t# print $fh $response;\n\t# close $fh;\n\n\tdie \"Watchman: command returned no output.\\n\" .\n\t\"Falling back to scanning...\\n\" if $response eq \"\";\n\tdie \"Watchman: command returned invalid output: $response\\n\" .\n\t\"Falling back to scanning...\\n\" unless $response =~ /^\\{/;\n\n\treturn $json_pkg->new->utf8->decode($response);\n}\n\nsub is_work_tree_watched {\n\tmy ($output) = @_;\n\tmy $error = $output->{error};\n\tif ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {\n\t\t$retry--;\n\t\tmy $response = qx/watchman watch \"$git_work_tree\"/;\n\t\tdie \"Failed to make watchman watch '$git_work_tree'.\\n\" .\n\t\t    \"Falling back to scanning...\\n\" if $? != 0;\n\t\t$output = $json_pkg->new->utf8->decode($response);\n\t\t$error = $output->{error};\n\t\tdie \"Watchman: $error.\\n\" .\n\t\t\"Falling back to scanning...\\n\" if $error;\n\n\t\t# Uncomment for debugging watchman output\n\t\t# open (my $fh, \">\", \".git/watchman-output.out\");\n\t\t# close $fh;\n\n\t\t# Watchman will always return all files on the first query so\n\t\t# return the fast \"everything is dirty\" flag to git and do the\n\t\t# Watchman query just to get it over with now so we won't pay\n\t\t# the cost in git to look up each individual file.\n\t\tmy $o = watchman_clock();\n\t\t$error = $output->{error};\n\n\t\tdie \"Watchman: $error.\\n\" .\n\t\t\"Falling back to scanning...\\n\" if $error;\n\n\t\toutput_result($o->{clock}, (\"/\"));\n\t\t$last_update_token = $o->{clock};\n\n\t\teval { launch_watchman() };\n\t\treturn 0;\n\t}\n\n\tdie \"Watchman: $error.\\n\" .\n\t\"Falling back to scanning...\\n\" if $error;\n\n\treturn 1;\n}\n\nsub get_working_dir {\n\tmy $working_dir;\n\tif ($^O =~ 'msys' || $^O =~ 'cygwin') {\n\t\t$working_dir = Win32::GetCwd();\n\t\t$working_dir =~ tr/\\\\/\\//;\n\t} else {\n\t\trequire Cwd;\n\t\t$working_dir = Cwd::cwd();\n\t}\n\n\treturn $working_dir;\n}\n"
  },
  {
    "path": ".git/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": ".git/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\nprecommit=\"$(git rev-parse --git-path hooks/pre-commit)\"\ntest -x \"$precommit\" && exec \"$precommit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": ".git/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=$(git hash-object -t tree /dev/null)\nfi\n\n# If you want to allow non-ASCII filenames set this variable to true.\nallownonascii=$(git config --type=bool hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ASCII filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff-index --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\tcat <<\\EOF\nError: Attempt to add a non-ASCII file name.\n\nThis can cause problems if you want to work with people on other platforms.\n\nTo be portable it is advisable to rename the file.\n\nIf you know what you are doing you can disable this check using:\n\n  git config hooks.allownonascii true\nEOF\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": ".git/hooks/pre-merge-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git merge\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message to\n# stderr if it wants to stop the merge commit.\n#\n# To enable this hook, rename this file to \"pre-merge-commit\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n        exec \"$GIT_DIR/hooks/pre-commit\"\n:\n"
  },
  {
    "path": ".git/hooks/pre-push.sample",
    "content": "#!/bin/sh\n\n# An example hook script to verify what is about to be pushed.  Called by \"git\n# push\" after it has checked the remote status, but before anything has been\n# pushed.  If this script exits with a non-zero status nothing will be pushed.\n#\n# This hook is called with the following parameters:\n#\n# $1 -- Name of the remote to which the push is being done\n# $2 -- URL to which the push is being done\n#\n# If pushing without using a named remote those arguments will be equal.\n#\n# Information about the commits which are being pushed is supplied as lines to\n# the standard input in the form:\n#\n#   <local ref> <local oid> <remote ref> <remote oid>\n#\n# This sample shows how to prevent push of commits where the log message starts\n# with \"WIP\" (work in progress).\n\nremote=\"$1\"\nurl=\"$2\"\n\nzero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')\n\nwhile read local_ref local_oid remote_ref remote_oid\ndo\n\tif test \"$local_oid\" = \"$zero\"\n\tthen\n\t\t# Handle delete\n\t\t:\n\telse\n\t\tif test \"$remote_oid\" = \"$zero\"\n\t\tthen\n\t\t\t# New branch, examine all commits\n\t\t\trange=\"$local_oid\"\n\t\telse\n\t\t\t# Update to existing branch, examine new commits\n\t\t\trange=\"$remote_oid..$local_oid\"\n\t\tfi\n\n\t\t# Check for WIP commit\n\t\tcommit=$(git rev-list -n 1 --grep '^WIP' \"$range\")\n\t\tif test -n \"$commit\"\n\t\tthen\n\t\t\techo >&2 \"Found WIP commit in $local_ref, not pushing\"\n\t\t\texit 1\n\t\tfi\n\tfi\ndone\n\nexit 0\n"
  },
  {
    "path": ".git/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up to date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\n<<\\DOC_END\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n\nDOC_END\n"
  },
  {
    "path": ".git/hooks/pre-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to make use of push options.\n# The example simply echoes all push options that start with 'echoback='\n# and rejects all pushes when the \"reject\" push option is used.\n#\n# To enable this hook, rename this file to \"pre-receive\".\n\nif test -n \"$GIT_PUSH_OPTION_COUNT\"\nthen\n\ti=0\n\twhile test \"$i\" -lt \"$GIT_PUSH_OPTION_COUNT\"\n\tdo\n\t\teval \"value=\\$GIT_PUSH_OPTION_$i\"\n\t\tcase \"$value\" in\n\t\techoback=*)\n\t\t\techo \"echo from the pre-receive-hook: ${value#*=}\" >&2\n\t\t\t;;\n\t\treject)\n\t\t\texit 1\n\t\tesac\n\t\ti=$((i + 1))\n\tdone\nfi\n"
  },
  {
    "path": ".git/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples. The first one removes the\n# \"# Please enter the commit message...\" help message.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\nCOMMIT_MSG_FILE=$1\nCOMMIT_SOURCE=$2\nSHA1=$3\n\n/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' \"$COMMIT_MSG_FILE\"\n\n# case \"$COMMIT_SOURCE,$SHA1\" in\n#  ,|template,)\n#    /usr/bin/perl -i.bak -pe '\n#       print \"\\n\" . `git diff --cached --name-status -r`\n# \t if /^#/ && $first++ == 0' \"$COMMIT_MSG_FILE\" ;;\n#  *) ;;\n# esac\n\n# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# git interpret-trailers --in-place --trailer \"$SOB\" \"$COMMIT_MSG_FILE\"\n# if test -z \"$COMMIT_SOURCE\"\n# then\n#   /usr/bin/perl -i.bak -pe 'print \"\\n\" if !$first_line++' \"$COMMIT_MSG_FILE\"\n# fi\n"
  },
  {
    "path": ".git/hooks/push-to-checkout.sample",
    "content": "#!/bin/sh\n\n# An example hook script to update a checked-out tree on a git push.\n#\n# This hook is invoked by git-receive-pack(1) when it reacts to git\n# push and updates reference(s) in its repository, and when the push\n# tries to update the branch that is currently checked out and the\n# receive.denyCurrentBranch configuration variable is set to\n# updateInstead.\n#\n# By default, such a push is refused if the working tree and the index\n# of the remote repository has any difference from the currently\n# checked out commit; when both the working tree and the index match\n# the current commit, they are updated to match the newly pushed tip\n# of the branch. This hook is to be used to override the default\n# behaviour; however the code below reimplements the default behaviour\n# as a starting point for convenient modification.\n#\n# The hook receives the commit with which the tip of the current\n# branch is going to be updated:\ncommit=$1\n\n# It can exit with a non-zero status to refuse the push (when it does\n# so, it must not modify the index or the working tree).\ndie () {\n\techo >&2 \"$*\"\n\texit 1\n}\n\n# Or it can make any necessary changes to the working tree and to the\n# index to bring them to the desired state when the tip of the current\n# branch is updated to the new commit, and exit with a zero status.\n#\n# For example, the hook can simply run git read-tree -u -m HEAD \"$1\"\n# in order to emulate git fetch that is run in the reverse direction\n# with git push, as the two-tree form of git read-tree -u -m is\n# essentially the same as git switch or git checkout that switches\n# branches while keeping the local changes in the working tree that do\n# not interfere with the difference between the branches.\n\n# The below is a more-or-less exact translation to shell of the C code\n# for the default behaviour for git's push-to-checkout hook defined in\n# the push_to_deploy() function in builtin/receive-pack.c.\n#\n# Note that the hook will be executed from the repository directory,\n# not from the working tree, so if you want to perform operations on\n# the working tree, you will have to adapt your code accordingly, e.g.\n# by adding \"cd ..\" or using relative paths.\n\nif ! git update-index -q --ignore-submodules --refresh\nthen\n\tdie \"Up-to-date check failed\"\nfi\n\nif ! git diff-files --quiet --ignore-submodules --\nthen\n\tdie \"Working directory has unstaged changes\"\nfi\n\n# This is a rough translation of:\n#\n#   head_has_history() ? \"HEAD\" : EMPTY_TREE_SHA1_HEX\nif git cat-file -e HEAD 2>/dev/null\nthen\n\thead=HEAD\nelse\n\thead=$(git hash-object -t tree --stdin </dev/null)\nfi\n\nif ! git diff-index --quiet --cached --ignore-submodules $head --\nthen\n\tdie \"Working directory has staged changes\"\nfi\n\nif ! git read-tree -u -m \"$commit\"\nthen\n\tdie \"Could not update working tree to new HEAD\"\nfi\n"
  },
  {
    "path": ".git/hooks/sendemail-validate.sample",
    "content": "#!/bin/sh\n\n# An example hook script to validate a patch (and/or patch series) before\n# sending it via email.\n#\n# The hook should exit with non-zero status after issuing an appropriate\n# message if it wants to prevent the email(s) from being sent.\n#\n# To enable this hook, rename this file to \"sendemail-validate\".\n#\n# By default, it will only check that the patch(es) can be applied on top of\n# the default upstream branch without conflicts in a secondary worktree. After\n# validation (successful or not) of the last patch of a series, the worktree\n# will be deleted.\n#\n# The following config variables can be set to change the default remote and\n# remote ref that are used to apply the patches against:\n#\n#   sendemail.validateRemote (default: origin)\n#   sendemail.validateRemoteRef (default: HEAD)\n#\n# Replace the TODO placeholders with appropriate checks according to your\n# needs.\n\nvalidate_cover_letter () {\n\tfile=\"$1\"\n\t# TODO: Replace with appropriate checks (e.g. spell checking).\n\ttrue\n}\n\nvalidate_patch () {\n\tfile=\"$1\"\n\t# Ensure that the patch applies without conflicts.\n\tgit am -3 \"$file\" || return\n\t# TODO: Replace with appropriate checks for this patch\n\t# (e.g. checkpatch.pl).\n\ttrue\n}\n\nvalidate_series () {\n\t# TODO: Replace with appropriate checks for the whole series\n\t# (e.g. quick build, coding style checks, etc.).\n\ttrue\n}\n\n# main -------------------------------------------------------------------------\n\nif test \"$GIT_SENDEMAIL_FILE_COUNTER\" = 1\nthen\n\tremote=$(git config --default origin --get sendemail.validateRemote) &&\n\tref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&\n\tworktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&\n\tgit worktree add -fd --checkout \"$worktree\" \"refs/remotes/$remote/$ref\" &&\n\tgit config --replace-all sendemail.validateWorktree \"$worktree\"\nelse\n\tworktree=$(git config --get sendemail.validateWorktree)\nfi || {\n\techo \"sendemail-validate: error: failed to prepare worktree\" >&2\n\texit 1\n}\n\nunset GIT_DIR GIT_WORK_TREE\ncd \"$worktree\" &&\n\nif grep -q \"^diff --git \" \"$1\"\nthen\n\tvalidate_patch \"$1\"\nelse\n\tvalidate_cover_letter \"$1\"\nfi &&\n\nif test \"$GIT_SENDEMAIL_FILE_COUNTER\" = \"$GIT_SENDEMAIL_FILE_TOTAL\"\nthen\n\tgit config --unset-all sendemail.validateWorktree &&\n\ttrap 'git worktree remove -ff \"$worktree\"' EXIT &&\n\tvalidate_series\nfi\n"
  },
  {
    "path": ".git/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to block unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --type=bool hooks.allowunannotated)\nallowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --type=bool hooks.denycreatebranch)\nallowdeletetag=$(git config --type=bool hooks.allowdeletetag)\nallowmodifytag=$(git config --type=bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": ".git/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": ".git/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 0a078f5207323d2be24cbdede1de3abb19bc55a4 appuser <appuser@7c99e0e64a07.(none)> 1778556387 +0000\tclone: from https://github.com/open-telemetry/opentelemetry-network\n"
  },
  {
    "path": ".git/logs/refs/heads/main",
    "content": "0000000000000000000000000000000000000000 0a078f5207323d2be24cbdede1de3abb19bc55a4 appuser <appuser@7c99e0e64a07.(none)> 1778556387 +0000\tclone: from https://github.com/open-telemetry/opentelemetry-network\n"
  },
  {
    "path": ".git/logs/refs/remotes/origin/HEAD",
    "content": "0000000000000000000000000000000000000000 0a078f5207323d2be24cbdede1de3abb19bc55a4 appuser <appuser@7c99e0e64a07.(none)> 1778556387 +0000\tclone: from https://github.com/open-telemetry/opentelemetry-network\n"
  },
  {
    "path": ".git/objects/pack/pack-71fb82fb82ed2e298be25eaa90192ef76cf2859f.promisor",
    "content": "0a078f5207323d2be24cbdede1de3abb19bc55a4 refs/heads/main\n"
  },
  {
    "path": ".git/packed-refs",
    "content": "# pack-refs with: peeled fully-peeled sorted \n0a078f5207323d2be24cbdede1de3abb19bc55a4 refs/remotes/origin/main\n"
  },
  {
    "path": ".git/refs/heads/main",
    "content": "0a078f5207323d2be24cbdede1de3abb19bc55a4\n"
  },
  {
    "path": ".git/refs/remotes/origin/HEAD",
    "content": "ref: refs/remotes/origin/main\n"
  },
  {
    "path": ".git/shallow",
    "content": "0a078f5207323d2be24cbdede1de3abb19bc55a4\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "5ac6cdf70d1b8f407e873a7a3fcf0bec07232c75\n"
  },
  {
    "path": ".github/.actionlint.yaml",
    "content": "self-hosted-runner:\n  labels:\n    - ubuntu-24.04-arm\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "#####################################################\n#\n# List of approvers for OpenTelemetry Collector Contrib\n#\n#####################################################\n#\n# Learn about membership in OpenTelemetry community:\n#  https://github.com/open-telemetry/community/blob/main/community-membership.md\n#\n#\n# Learn about CODEOWNERS file format:\n# https://help.github.com/en/articles/about-code-owners\n#\n# NOTE: Lines should be entered in the following format:\n# <component_path_relative_from_project_root>/<min_1_space><owner_1><space><owner_2><space>..<owner_n>\n# reducer/logging/                 @open-telemetry/ebpf-approvers @bjandras @jimwsplk\n# Path separator and minimum of 1 space between component path and owners is\n# important for validation steps\n#\n\n* @open-telemetry/network-approvers"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: Bug report\ndescription: Create a report to help us improve\nlabels: [\"bug\", \"needs triage\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report! Please make sure to fill out the entire form below, providing as much context as you can in order to help us triage and track down your bug as quickly as possible.\n\n        Before filing a bug, please be sure you have searched through [existing bugs](https://github.com/open-telemetry/opentelemetry-ebpf/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug) to see if an existing issue covers your bug.\n  - type: textarea\n    attributes:\n      label: What happened?\n      description: Please provide as much detail as you reasonably can.\n      value: |\n        ## Description\n\n        ## Steps to Reproduce\n\n        ## Expected Result\n\n        ## Actual Result\n\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: eBPF Collector version\n      description: What version did you use? (e.g., `v0.4.0`, `1eb551b`, etc)\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Environment information\n      description: Please provide any additional information about your installation.\n      value: |\n        ## Environment\n        OS: (e.g., \"Ubuntu 20.04\")\n        Compiler(if manually compiled): (e.g., \"go 14.2\")\n\n  - type: textarea\n    attributes:\n      label: eBPF Collector configuration\n      description: Please provide the configuration you are using (e.g. the YAML config file).\n      placeholder:\n      render: yaml\n  - type: textarea\n    attributes:\n      label: Log output\n      description: |\n        Please copy and paste any relevant log output.\n      render: shell\n  - type: textarea\n    attributes:\n      label: Additional context\n      description: Any additional information you think may be relevant to this issue.\n  - type: dropdown\n    attributes:\n      label: Tip\n      description: This element is static, used to render a helpful sub-heading for end-users and community members to help prioritize issues. Please leave as is.\n      options:\n        - <sub>[React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/).</sub>\n      default: 0\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: eBPF Collector Slack Channel\n    url: https://cloud-native.slack.com/archives/C02AB15583A\n    about: Please ask and answer questions here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: Feature request\ndescription: Suggest an idea for this project\nlabels: [\"enhancement\", \"needs triage\"]\nbody:\n  - type: textarea\n    attributes:\n      label: Is your feature request related to a problem? Please describe.\n      description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the solution you'd like\n      description: A clear and concise description of what you want to happen.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe alternatives you've considered\n      description: A clear and concise description of any alternative solutions or features you've considered.\n  - type: textarea\n    attributes:\n      label: Additional context\n      description: Add any other context or screenshots about the feature request here.\n  - type: dropdown\n    attributes:\n      label: Tip\n      description: This element is static, used to render a helpful sub-heading for end-users and community members to help prioritize issues. Please leave as is.\n      options:\n        - <sub>[React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/).</sub>\n      default: 0\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.yaml",
    "content": "name: Other issue\ndescription: Create a new issue to help us improve the collector\nlabels: [\"needs triage\"]\nbody:\n  - type: textarea\n    attributes:\n      label: Describe the issue you're reporting\n      description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n    validations:\n      required: true\n  - type: dropdown\n    attributes:\n      label: Tip\n      description: This element is static, used to render a helpful sub-heading for end-users and community members to help prioritize issues. Please leave as is.\n      options:\n        - <sub>[React](https://github.blog/news-insights/product-news/add-reactions-to-pull-requests-issues-and-comments/) with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. Learn more [here](https://opentelemetry.io/community/end-user/issue-participation/).</sub>\n      default: 0\n"
  },
  {
    "path": ".github/actions/build-tools-single-stage/action.yml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nname: 'Build and Push Container'\ndescription: 'Build and push a Docker container with dependency management and registry caching'\n\ninputs:\n  directory:\n    description: 'Directory name to build'\n    required: true\n  registry:\n    description: 'Container registry to use'\n    required: true\n    default: 'ghcr.io'\n  registry_username:\n    description: 'Registry username'\n    required: true\n  registry_password:\n    description: 'Registry password/token'\n    required: true\n  image_prefix:\n    description: 'Prefix for image names'\n    required: false\n    default: 'benv'\n  ref:\n    description: 'Git ref to checkout'\n    required: false\n    default: 'main'\n  force_rebuild:\n    description: 'Force rebuild and push even if image exists in registry'\n    required: false\n    default: 'false'\n  arch:\n    description: 'Target architecture (amd64 or arm64)'\n    required: false\n    default: 'amd64'\n\noutputs:\n  image-tag:\n    description: 'The computed image tag'\n    value: ${{ steps.compute-recursive-tags.outputs.image-tag }}\n  full-image-tag:\n    description: 'The full image tag with registry'\n    value: ${{ steps.compute-recursive-tags.outputs.full-image-tag }}\n  image-exists:\n    description: 'Whether the image already exists in registry'\n    value: ${{ steps.check-exists.outputs.exists }}\n  build-needed:\n    description: 'Whether a build was needed'\n    value: ${{ steps.check-exists.outputs.exists == 'false' }}\n\nruns:\n  using: 'composite'\n  steps:\n    - name: Checkout sources\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        ref: ${{ inputs.ref }}\n        fetch-depth: 0\n\n    - name: Compute recursive tags for all directories\n      id: compute-recursive-tags\n      shell: bash\n      env:\n        DOCKER_TAG_PREFIX: ${{ github.repository_owner }}/\n      run: |\n        DIRECTORY=\"build-tools/${{ inputs.directory }}\"\n        BASE_DIRECTORY=\"${{ inputs.directory }}\"\n        ARCH=\"${{ inputs.arch }}\"\n        \n        # Define dependency mapping based on CMakeLists.txt\n        declare -A DEPS\n        DEPS[\"base\"]=\"\"\n        DEPS[\"libuv\"]=\"base\"\n        DEPS[\"cpp_misc\"]=\"base\"\n        DEPS[\"libmaxminddb\"]=\"base\"\n        DEPS[\"libbpf\"]=\"base\"\n        DEPS[\"aws_sdk\"]=\"base\"\n        DEPS[\"final\"]=\"base libuv aws_sdk cpp_misc libmaxminddb libbpf\"\n        \n        # Compute direct hashes for all directories upfront\n        declare -A DIRECT_HASHES\n        ALL_DIRS=\"base libuv cpp_misc libmaxminddb libbpf aws_sdk final\"\n        \n        echo \"Computing direct hashes...\" >&2\n        for dir in $ALL_DIRS; do\n          direct_hash=$(git log -1 --format=%h \"build-tools/${dir}\")\n          DIRECT_HASHES[$dir]=$direct_hash\n          echo \"Direct hash for $dir: $direct_hash\" >&2\n        done\n        \n        # Function to compute dependency closure (all transitive dependencies)\n        compute_closure() {\n          local target=\"$1\"\n          local visited_key=\"VISITED_$target\"\n          \n          # Check for circular dependency\n          if [[ -n \"${!visited_key:-}\" ]]; then\n            echo \"ERROR: Circular dependency detected for $target\" >&2\n            exit 1\n          fi\n          \n          # Mark as visiting\n          declare -g \"$visited_key=1\"\n          \n          # Start with direct dependencies\n          local deps=\"${DEPS[$target]:-}\"\n          local closure_set=\"\"\n          \n          # Add direct dependencies\n          for dep in $deps; do\n            closure_set=\"$closure_set $dep\"\n            \n            # Recursively add their closures\n            local dep_closure=$(compute_closure \"$dep\")\n            closure_set=\"$closure_set $dep_closure\"\n          done\n          \n          # Remove duplicates by converting to array and back\n          local unique_closure=($(echo $closure_set | tr ' ' '\\n' | sort -u | tr '\\n' ' '))\n          \n          # Unmark visiting\n          unset \"$visited_key\"\n          \n          echo \"${unique_closure[@]}\"\n        }\n        \n        # Function to compute recursive hash using closure approach\n        compute_recursive_hash() {\n          local dir=\"$1\"\n          \n          # Get the full dependency closure\n          local closure=$(compute_closure \"$dir\")\n          \n          # Include the directory itself in the hash computation\n          local all_dirs_for_hash=\"$dir $closure\"\n          \n          # Sort all directories\n          local sorted_dirs=($(echo $all_dirs_for_hash | tr ' ' '\\n' | sort -u | tr '\\n' ' '))\n          \n          # Concatenate their direct hashes with dashes\n          local hash_input=\"\"\n          for d in \"${sorted_dirs[@]}\"; do\n            if [[ -n \"$d\" ]]; then\n              if [[ -n \"$hash_input\" ]]; then\n                hash_input=\"$hash_input-${DIRECT_HASHES[$d]}\"\n              else\n                hash_input=\"${DIRECT_HASHES[$d]}\"\n              fi\n            fi\n          done\n          \n          # Use the dash-separated hashes directly as the tag\n          local final_hash=\"$hash_input\"\n          \n          echo \"Closure for $dir: ${sorted_dirs[@]}\" >&2\n          echo \"Final hash for $dir: $final_hash\" >&2\n          \n          echo \"$final_hash\"\n        }\n        \n        # Compute recursive hash for target directory\n        RECURSIVE_HASH=$(compute_recursive_hash \"$BASE_DIRECTORY\")\n        \n        # Create image tag\n        IMAGE_TAG=\"${{ github.repository_owner }}/opentelemetry-network-build-tools-cache:${BASE_DIRECTORY}-${ARCH}-${RECURSIVE_HASH}\"\n        FULL_IMAGE_TAG=\"${{ inputs.registry }}/${IMAGE_TAG}\"\n        \n        echo \"image-tag=${IMAGE_TAG}\" >> $GITHUB_OUTPUT\n        echo \"full-image-tag=${FULL_IMAGE_TAG}\" >> $GITHUB_OUTPUT\n        echo \"recursive-hash=${RECURSIVE_HASH}\" >> $GITHUB_OUTPUT\n        \n        echo \"Computed recursive image tag: ${IMAGE_TAG}\" >&2\n        echo \"Full image tag: ${FULL_IMAGE_TAG}\" >&2\n        echo \"Recursive hash: ${RECURSIVE_HASH}\" >&2\n        \n        # Compute all dependency tags for build args\n        echo \"Computing all dependency tags...\" >&2\n        \n        for dir in $ALL_DIRS; do\n          if [[ \"$dir\" != \"$BASE_DIRECTORY\" ]]; then\n            dir_hash=$(compute_recursive_hash \"$dir\")\n            dir_image_tag=\"${{ github.repository_owner }}/opentelemetry-network-build-tools-cache:${dir}-${ARCH}-${dir_hash}\"\n            dir_full_tag=\"${{ inputs.registry }}/${dir_image_tag}\"\n            \n            # Export as environment variable for use in build args\n            export \"${dir}_IMAGE_TAG=${dir_full_tag}\"\n            echo \"${dir}_IMAGE_TAG=${dir_full_tag}\" >> $GITHUB_OUTPUT\n            \n            echo \"Dependency: ${dir} -> ${dir_full_tag}\" >&2\n          fi\n        done\n\n    - name: Check if image exists in registry\n      id: check-exists\n      shell: bash\n      run: |\n        FULL_IMAGE_TAG=\"${{ steps.compute-recursive-tags.outputs.full-image-tag }}\"\n        \n        if [[ \"${{ inputs.force_rebuild }}\" == \"true\" ]]; then\n          echo \"exists=false\" >> $GITHUB_OUTPUT\n          echo \"Force rebuild enabled - will rebuild ${FULL_IMAGE_TAG} regardless of registry state\"\n        elif docker manifest inspect \"${FULL_IMAGE_TAG}\" >/dev/null 2>&1; then\n          echo \"exists=true\" >> $GITHUB_OUTPUT\n          echo \"Image ${FULL_IMAGE_TAG} already exists in registry\"\n        else\n          echo \"exists=false\" >> $GITHUB_OUTPUT\n          echo \"Image ${FULL_IMAGE_TAG} does not exist in registry\"\n        fi\n\n    - name: Initialize directory submodules\n      if: steps.check-exists.outputs.exists == 'false'\n      shell: bash\n      run: |\n        DIRECTORY=\"build-tools/${{ inputs.directory }}\"\n        echo \"Initializing submodules for directory: ${DIRECTORY}\"\n        \n        # Initialize submodules for the specific directory path  \n        git submodule update --init --recursive -- \"${DIRECTORY}/\"\n\n\n    - name: Log in to Container Registry\n      if: steps.check-exists.outputs.exists == 'false'\n      uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0\n      with:\n        registry: ${{ inputs.registry }}\n        username: ${{ inputs.registry_username }}\n        password: ${{ inputs.registry_password }}\n\n    - name: Build and push image\n      if: steps.check-exists.outputs.exists == 'false'\n      shell: bash\n      run: |\n        DIRECTORY=\"build-tools/${{ inputs.directory }}\"\n        FULL_IMAGE_TAG=\"${{ steps.compute-recursive-tags.outputs.full-image-tag }}\"\n        \n        # Start building the docker command\n        BUILD_ARGS=\"--build-arg NPROC=$(nproc)\"\n        \n        # Add all dependency image tags as build args using outputs from compute-recursive-tags step\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg base_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.base_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg libuv_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.libuv_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg cpp_misc_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.cpp_misc_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg libmaxminddb_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.libmaxminddb_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg libbpf_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.libbpf_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg aws_sdk_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.aws_sdk_IMAGE_TAG }}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg final_IMAGE_TAG=${{ steps.compute-recursive-tags.outputs.final_IMAGE_TAG }}\"\n        \n        # Add environment-specific build args if they exist\n        if [ -n \"${BENV_BASE_IMAGE_DISTRO}\" ]; then\n          BUILD_ARGS=\"${BUILD_ARGS} --build-arg BENV_BASE_IMAGE_DISTRO=${BENV_BASE_IMAGE_DISTRO}\"\n        fi\n        \n        if [ -n \"${BENV_BASE_IMAGE_VERSION}\" ]; then\n          BUILD_ARGS=\"${BUILD_ARGS} --build-arg BENV_BASE_IMAGE_VERSION=${BENV_BASE_IMAGE_VERSION}\"\n        fi\n        \n        # Add CMAKE_BUILD_TYPE (defaults to Release if not set)\n        CMAKE_BUILD_TYPE=\"${CMAKE_BUILD_TYPE:-Release}\"\n        BUILD_ARGS=\"${BUILD_ARGS} --build-arg CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\"\n        \n        # Add BUILD_CFLAGS based on build type\n        if [ \"${CMAKE_BUILD_TYPE}\" = \"Debug\" ]; then\n          BUILD_ARGS=\"${BUILD_ARGS} --build-arg BUILD_CFLAGS='-O0 -g'\"\n        fi\n        \n        # Build the image\n        echo \"Building image: ${FULL_IMAGE_TAG}\"\n        echo \"Build args: ${BUILD_ARGS}\"\n        \n        docker build -t \"${FULL_IMAGE_TAG}\" ${BUILD_ARGS} \"${DIRECTORY}/\"\n        \n        # Always push intermediate builds to cache registry (dry_run only affects final Docker Hub push)\n        echo \"Pushing image to cache registry: ${FULL_IMAGE_TAG}\"\n        docker push \"${FULL_IMAGE_TAG}\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "**Description:** <Describe what has changed.>\n<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.\nEx. Adding a feature - Explain what this achieves.-->\n\n**Link to tracking Issue:** <Issue number if applicable>\n\n**Testing:** <Describe what testing was performed and which tests were added.>\n\n**Documentation:** <Describe the documentation added.>"
  },
  {
    "path": ".github/renovate.json5",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\",\n    \"helpers:pinGitHubActionDigestsToSemver\"\n  ],\n  \"packageRules\": [\n    {\n      \"groupName\": \"all patch versions\",\n      \"matchUpdateTypes\": [\"patch\"],\n      \"schedule\": [\"before 8am every weekday\"]\n    },\n    {\n      \"matchUpdateTypes\": [\"minor\", \"major\"],\n      \"schedule\": [\"before 8am on Monday\"]\n    }\n  ],\n  \"labels\": [\n    \"dependencies\"\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/build-and-release.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nname: build-and-release\nrun-name: Publishing a release\n\non:\n  workflow_dispatch:\n    inputs:\n      release_type:\n        description: \"Release type\"\n        required: true\n        type: choice\n        options:\n          - public\n          - unofficial\n        default: public\n      ref:\n        description: \"Tag, branch or SHA to checkout\"\n        required: true\n        type: string\n        default: \"main\"\n      image_prefix:\n        description: \"Prefix to use for destination image name\"\n        required: false\n        type: string\n        default: \"opentelemetry-ebpf-\"\n      additional_tag:\n        description: \"Additional tag to use when pushing to docker repository\"\n        required: false\n        type: string\n      dry_run:\n        description: \"Build everything but don't actually push to repository\"\n        required: false\n        type: boolean\n        default: false\n  workflow_call:\n    inputs:\n      release_type:\n        description: \"Release type\"\n        required: true\n        type: string\n      ref:\n        description: \"Tag, branch or SHA to checkout\"\n        required: true\n        type: string\n      image_prefix:\n        description: \"Prefix to use for destination image name\"\n        required: false\n        type: string\n        default: \"opentelemetry-ebpf-\"\n      additional_tag:\n        description: \"Additional tag to use when pushing to docker repository\"\n        required: false\n        type: string\n      dry_run:\n        description: \"Build everything but don't actually push to repository\"\n        required: false\n        type: boolean\n        default: false\n\npermissions:\n  contents: write\n  packages: write\n\nenv:\n  BENV_IMAGE: ${{ vars.BENV_IMAGE || 'docker.io/otel/opentelemetry-network-build-tools' }}\n  DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}\n  DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}\n  DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}\n  DOCKER_NAMESPACE: ${{ vars.DOCKER_NAMESPACE }}\n  IMAGE_PREFIX: ${{ inputs.image_prefix || 'opentelemetry-ebpf-' }}\n  RELEASE_TYPE: ${{ inputs.release_type || 'public' }}\n  DRY_RUN: ${{ inputs.dry_run && 'true' || 'false' }}\n\njobs:\n  compute-version:\n    name: Compute version\n    runs-on: ubuntu-24.04\n    outputs:\n      git_short_hash: ${{ steps.version.outputs.git_short_hash }}\n      short_version_number: ${{ steps.version.outputs.short_version_number }}\n      full_version_number: ${{ steps.version.outputs.full_version_number }}\n      github_tag: ${{ steps.version.outputs.github_tag }}\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ inputs.ref }}\n          fetch-depth: 0\n      - name: Compute version numbers\n        id: version\n        run: |\n          source ./version.sh\n          git_short_hash=$(git rev-parse --short=8 HEAD)\n          short_version_number=\"${EBPF_NET_MAJOR_VERSION}.${EBPF_NET_MINOR_VERSION}\"\n          full_version_number=\"${EBPF_NET_MAJOR_VERSION}.${EBPF_NET_MINOR_VERSION}.${EBPF_NET_PATCH_VERSION}\"\n          if [[ \"${RELEASE_TYPE}\" == \"public\" ]]; then\n            github_tag=v${full_version_number}\n          else\n            github_tag=v${full_version_number}-${git_short_hash}\n          fi\n          echo \"git_short_hash=${git_short_hash}\" >> \"$GITHUB_OUTPUT\"\n          echo \"short_version_number=${short_version_number}\" >> \"$GITHUB_OUTPUT\"\n          echo \"full_version_number=${full_version_number}\" >> \"$GITHUB_OUTPUT\"\n          echo \"github_tag=${github_tag}\" >> \"$GITHUB_OUTPUT\"\n\n  build-per-arch:\n    name: Build ${{ matrix.arch }} artifacts\n    needs: compute-version\n    strategy:\n      matrix:\n        include:\n          - arch: amd64\n            runner: ubuntu-24.04\n          - arch: arm64\n            runner: ubuntu-24.04-arm\n    runs-on: ${{ matrix.runner }}\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ inputs.ref }}\n          fetch-depth: 0\n          submodules: false\n          path: src\n      - name: Checkout ext/ submodules\n        run: |\n          cd $GITHUB_WORKSPACE/src\n          git submodule update --init --recursive ext/\n      - name: Fetch build environment\n        run: |\n          docker pull $BENV_IMAGE\n      - name: Create and fix permissions for output directory\n        run: |\n          mkdir -p $GITHUB_WORKSPACE/out\n          sudo chown -R 1000:1000 $GITHUB_WORKSPACE/out\n      - name: Build artifacts\n        run: |\n          docker run -d -p 5000:5000 --name registry docker.io/library/registry:2\n          docker run -t --rm \\\n            --mount \"type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock\" \\\n            --mount \"type=bind,source=$GITHUB_WORKSPACE/src,destination=/home/user/src,readonly\" \\\n            --mount \"type=bind,source=$GITHUB_WORKSPACE/out,destination=/home/user/out\" \\\n            --env EBPF_NET_SRC_ROOT=/home/user/src \\\n            --network host \\\n            --privileged \\\n            $BENV_IMAGE \\\n            ./build.sh pipeline-docker-registry\n          docker pull localhost:5000/reducer\n          docker pull localhost:5000/kernel-collector  \n          docker pull localhost:5000/cloud-collector\n          docker pull localhost:5000/k8s-collector\n      - name: Build packages\n        run: |\n          docker run -t --rm \\\n            --mount \"type=bind,source=$GITHUB_WORKSPACE/src,destination=/home/user/src,readonly\" \\\n            --mount \"type=bind,source=$GITHUB_WORKSPACE/out,destination=/home/user/out\" \\\n            --env EBPF_NET_SRC_ROOT=/home/user/src \\\n            --workdir /home/user/out \\\n            $BENV_IMAGE \\\n            cpack -G 'RPM;DEB'\n      - name: Log-in to container registry\n        run: |\n          docker login --username=\"$DOCKER_USERNAME\" --password-stdin $DOCKER_REGISTRY <<< \"$DOCKER_PASSWORD\"\n      - name: Tag and push arch-specific images\n        run: |\n          docker_registry=$(sed -e 's,^https://,,' -e 's,/*$,,' <<< $DOCKER_REGISTRY)\n          images=(reducer kernel-collector cloud-collector k8s-collector)\n          for image in ${images[@]}; do\n            image_name=\"${IMAGE_PREFIX}${image}\"\n            image_path=\"${docker_registry}/${DOCKER_NAMESPACE}/${image_name}\"\n            docker tag localhost:5000/$image ${image_path}:${{ matrix.arch }}-${{ needs.compute-version.outputs.git_short_hash }}\n            if [[ \"${DRY_RUN}\" == \"false\" ]]; then\n              docker push ${image_path}:${{ matrix.arch }}-${{ needs.compute-version.outputs.git_short_hash }}\n            fi\n          done\n          docker stop registry || true\n          docker rm registry || true\n      - name: Upload packages\n        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n        with:\n          name: packages-${{ matrix.arch }}\n          path: |\n            out/opentelemetry-ebpf-*.rpm\n            out/opentelemetry-ebpf-*.deb\n\n  create-manifests:\n    name: Create multi-arch manifests\n    needs: [compute-version, build-per-arch]\n    runs-on: ubuntu-24.04\n    if: ${{ inputs.dry_run != true }}\n    steps:\n      - name: Log-in to container registry\n        run: |\n          docker login --username=\"$DOCKER_USERNAME\" --password-stdin $DOCKER_REGISTRY <<< \"$DOCKER_PASSWORD\"\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n      - name: Create and push multi-arch manifests\n        run: |\n          docker_registry=$(sed -e 's,^https://,,' -e 's,/*$,,' <<< $DOCKER_REGISTRY)\n          git_short_hash=\"${{ needs.compute-version.outputs.git_short_hash }}\"\n          short_version=\"${{ needs.compute-version.outputs.short_version_number }}\"\n          full_version=\"${{ needs.compute-version.outputs.full_version_number }}\"\n          \n          if [[ \"${RELEASE_TYPE}\" == \"public\" ]]; then\n            tags=(latest latest-v${short_version} v${full_version})\n          else\n            tags=(v${full_version}-${git_short_hash})\n          fi\n          \n          if [[ \"${{ inputs.additional_tag }}\" != \"\" ]]; then\n            tags=(${tags[@]} \"${{ inputs.additional_tag }}\")\n          fi\n          \n          images=(reducer kernel-collector cloud-collector k8s-collector)\n          \n          for image in ${images[@]}; do\n            image_name=\"${IMAGE_PREFIX}${image}\"\n            image_path=\"${docker_registry}/${DOCKER_NAMESPACE}/${image_name}\"\n            amd64_tag=\"${image_path}:amd64-${git_short_hash}\"\n            arm64_tag=\"${image_path}:arm64-${git_short_hash}\"\n            \n            for tag in ${tags[@]}; do\n              docker buildx imagetools create --tag \"${image_path}:${tag}\" \\\n                \"${amd64_tag}\" \\\n                \"${arm64_tag}\"\n              echo \"Created multi-arch manifest: ${image_path}:${tag}\"\n            done\n          done\n\n  upload-release:\n    name: Upload release artifacts\n    needs: [compute-version, build-per-arch]\n    runs-on: ubuntu-24.04\n    if: ${{ inputs.dry_run != true }}\n    steps:\n      - name: Download all packages\n        uses: actions/download-artifact@v4\n        with:\n          pattern: packages-*\n          merge-multiple: true\n          path: packages\n      - name: Upload packages to Release\n        uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe\n        with:\n          tag_name: ${{ needs.compute-version.outputs.github_tag }}\n          prerelease: ${{ env.RELEASE_TYPE != 'public' }}\n          files: packages/*\n"
  },
  {
    "path": ".github/workflows/build-and-test.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nname: build-and-test\nrun-name: ${{ github.actor }} is running  GitHub Actions\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    paths:\n\npermissions:\n  contents: read\n\nenv:\n  BENV_IMAGE: ${{ vars.BENV_IMAGE || 'docker.io/otel/opentelemetry-network-build-tools' }}\n\n# concurrency:\n#   group: build-and-test-${{ github.event.pull_request_number || github.ref }}\n#   cancel-in-progress: true\n\njobs:\n  clang-format-check:\n    runs-on: ubuntu-24.04\n    name: clang-format-check\n\n    steps:\n    - name: Print github workspace\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        echo \"pr.ref = ${{github.event.pull_request.head.ref}}\"\n        echo \"github.ref = ${{ github.ref }}\"\n        echo \"$GITHUB_CONTEXT\"\n\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n\n    - name: Get current date\n      id: date\n      run: echo \"date=$(date +'%Y-%m-%d')\" >> $GITHUB_OUTPUT\n\n    - name: Runs format checker\n      run: |\n        # disable man page updates for faster apt install\n        echo \"set man-db/auto-update false\" | sudo debconf-communicate || true\n        sudo dpkg-reconfigure man-db\n\n        sudo apt update\n        sudo apt install -y --no-install-recommends clang-format-19\n        cd ${{ github.workspace }}\n        ./.github/workflows/scripts/check-clang-format.sh\n\n    outputs:\n      date: ${{ steps.date.outputs.date }}\n\n  cargo-test:\n    name: cargo-test\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Run cargo test\n      env:\n        PASS: ${{ secrets.DOCKER_PASSWORD }}\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n\n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        $BENV_IMAGE \\\n        ./build.sh cargo-test\n\n  build-reducer:\n    name: build-reducer\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-reducer\n        max-size: 1G\n        verbose: 2\n\n    - name: build-reducer\n      env:\n        PASS: ${{ secrets.DOCKER_PASSWORD }}\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        \n        # Start local registry for the build process\n        docker run -d -p 5000:5000 --name registry docker.io/library/registry:2\n        \n        # Build reducer with registry access\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n        \n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --network host \\\n        --privileged \\\n        $BENV_IMAGE \\\n        ./build.sh reducer-docker-registry\n        \n        # Export reducer container\n        mkdir -p container-exports\n        docker pull localhost:5000/reducer\n        docker save localhost:5000/reducer > container-exports/reducer.tar\n        \n        # Clean up registry\n        docker stop registry\n        docker rm registry\n\n    - name: Upload reducer container\n      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n      with:\n        name: reducer-container\n        path: container-exports/reducer.tar\n        if-no-files-found: error\n        retention-days: 1\n\n  build-kernel-collector:\n    name: build-kernel-collector\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-kernel-collector\n        max-size: 1G\n        verbose: 2\n\n    - name: build-kernel-collector\n      env:\n        PASS: ${{ secrets.DOCKER_PASSWORD }}\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        \n        # Start local registry for the build process\n        docker run -d -p 5000:5000 --name registry docker.io/library/registry:2\n        \n        # Build kernel-collector with registry access\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n        \n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --network host \\\n        --privileged \\\n        $BENV_IMAGE \\\n        ./build.sh kernel-collector-docker-registry\n        \n        # Export kernel-collector container\n        mkdir -p container-exports\n        docker pull localhost:5000/kernel-collector\n        docker save localhost:5000/kernel-collector > container-exports/kernel-collector.tar\n        \n        # Clean up registry\n        docker stop registry\n        docker rm registry\n\n    - name: Upload kernel collector container\n      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n      with:\n        name: kernel-collector-container\n        path: container-exports/kernel-collector.tar\n        if-no-files-found: error\n        retention-days: 1\n\n  build-kernel-collector-test:\n    name: build-kernel-collector-test\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-kernel-collector-test\n        max-size: 1G\n        verbose: 2\n\n    - name: build kernel-collector-test container\n      env:\n        PASS: ${{ secrets.DOCKER_PASSWORD }}\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        \n        # Start local registry for the build process\n        docker run -d -p 5000:5000 --name registry docker.io/library/registry:2\n        \n        # Build kernel-collector-test with registry access\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n        \n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --network host \\\n        --privileged \\\n        $BENV_IMAGE \\\n        ./build.sh kernel-collector-test-docker-registry\n        \n        # Export kernel-collector-test container\n        mkdir -p container-exports\n        docker pull localhost:5000/kernel-collector-test\n        docker save localhost:5000/kernel-collector-test > container-exports/kernel-collector-test.tar\n        \n        # Clean up registry\n        docker stop registry\n        docker rm registry\n\n    - name: Upload kernel-collector-test container\n      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n      with:\n        name: kernel-collector-test-container\n        path: container-exports/kernel-collector-test.tar\n        if-no-files-found: error\n        retention-days: 1\n\n  build-k8s-collector:\n    name: build-k8s-collector\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-k8s-collector\n        max-size: 1G\n        verbose: 2\n\n    - name: build k8s-collector\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        \n        # Start local registry for the build process\n        docker run -d -p 5000:5000 --name registry docker.io/library/registry:2\n        \n        # Build k8s-collector with registry access\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n\n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --network host \\\n        --privileged \\\n        $BENV_IMAGE \\\n        ./build.sh k8s-collector-docker-registry\n        \n        # Export k8s-collector container\n        mkdir -p container-exports\n        docker pull localhost:5000/k8s-collector\n        docker save localhost:5000/k8s-collector > container-exports/k8s-collector.tar\n        \n        # Clean up registry\n        docker stop registry\n        docker rm registry\n\n    - name: Upload k8s-collector container\n      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2\n      with:\n        name: k8s-collector-container\n        path: container-exports/k8s-collector.tar\n        if-no-files-found: error\n        retention-days: 1\n\n  build-cloud-collector:\n    name: build-cloud-collector\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-cloud-collector\n        max-size: 1G\n        verbose: 2\n\n    - name: build cloud-collector\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n\n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        $BENV_IMAGE \\\n        ./build.sh cloud-collector\n\n  build-run-unit-tests:\n    name: build-run-unit-tests\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n    \n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-run-unit-tests\n        max-size: 1G\n        verbose: 2\n    - name: run unit tests\n      run: |\n        echo \"github.workspace = ${{ github.workspace }}\"\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n\n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --env ARGS=\"--output-on-failure --repeat until-pass:3\" \\\n        --env SPDLOG_LEVEL=\"trace\" \\\n        $BENV_IMAGE \\\n        ./build.sh unit_tests test\n\n  build-run-unit-tests-with-asan-and-debug-flags:\n    name: build-run-unit-tests-with-asan-and-debug-flags\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-run-unit-tests-asan-debug\n        max-size: 1G\n        verbose: 2\n    - name: build unit tests with asan and debug flags on then run all tests\n      run: |\n        docker pull $BENV_IMAGE\n        git submodule update  --init --recursive ext/\n        # Ensure ccache directory is writable inside container\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n\n        docker run -t \\\n        --rm \\\n        --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n        --mount \"type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock\" \\\n        --mount \"type=bind,source=$(git rev-parse --show-toplevel),destination=/home/user/src,readonly\" \\\n        --env EBPF_NET_SRC_ROOT=/home/user/src \\\n        --env CCACHE_DIR=/ccache \\\n        --env ARGS=\"--output-on-failure --repeat until-pass:3 -E render_test\" \\\n        --env SPDLOG_LEVEL=\"trace\" \\\n        $BENV_IMAGE \\\n        ./build.sh --debug --asan unit_tests test\n\n  build-deb-rpm:\n    name: build-deb-rpm\n    runs-on: ubuntu-24.04\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Cache and install ccache\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: ccache\n        version: 1.0\n\n    - name: Setup ccache\n      uses: hendrikmuhs/ccache-action@v1.2\n      with:\n        key: ccache-build-packages\n        max-size: 1G\n        verbose: 2\n\n    - name: Build RPM/DEB packages\n      run: |\n        # Prepare output and ccache directories\n        mkdir -p \"${{ github.workspace }}/out\"\n        mkdir -p \"${{ github.workspace }}/.ccache\"\n        # Ensure the build container user (uid 1000) can write here\n        sudo chown -R 1000:1000 \"${{ github.workspace }}/out\" || true\n        chmod -R 777 \"${{ github.workspace }}/.ccache\" || true\n\n        docker pull $BENV_IMAGE\n        git submodule update --init --recursive ext/\n\n        # Configure and build the project into /home/user/out\n        docker run -t --rm \\\n          --mount \"type=bind,source=${{ github.workspace }}/.ccache,destination=/ccache\" \\\n          --mount \"type=bind,source=${{ github.workspace }},destination=/home/user/src,readonly\" \\\n          --mount \"type=bind,source=${{ github.workspace }}/out,destination=/home/user/out\" \\\n          --env EBPF_NET_SRC_ROOT=/home/user/src \\\n          --env CCACHE_DIR=/ccache \\\n          $BENV_IMAGE \\\n          ./build.sh pipeline\n\n        # Run CPack in the configured build directory\n        docker run -t --rm \\\n          --mount \"type=bind,source=${{ github.workspace }},destination=/home/user/src,readonly\" \\\n          --mount \"type=bind,source=${{ github.workspace }}/out,destination=/home/user/out\" \\\n          --env EBPF_NET_SRC_ROOT=/home/user/src \\\n          --workdir /home/user/out \\\n          $BENV_IMAGE \\\n          cpack -G 'RPM;DEB'\n\n  e2e-otel-jsonl:\n    name: e2e-otel-jsonl\n    needs: [build-reducer, build-kernel-collector, build-k8s-collector]\n    runs-on: ubuntu-24.04\n    timeout-minutes: 15\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Set up kind cluster\n      uses: helm/kind-action@v1.10.0\n      with:\n        version: v0.30.0\n        cluster_name: e2e-kind\n\n    - name: Download reducer container\n      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0\n      with:\n        name: reducer-container\n        path: ./container-exports\n\n    - name: Download kernel-collector container\n      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0\n      with:\n        name: kernel-collector-container\n        path: ./container-exports\n\n    - name: Download k8s-collector container\n      uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0\n      with:\n        name: k8s-collector-container\n        path: ./container-exports\n\n    - name: Load images and prepare config/output\n      run: |\n        set -euxo pipefail\n        docker load < container-exports/reducer.tar\n        docker load < container-exports/kernel-collector.tar\n        docker load < container-exports/k8s-collector.tar\n\n        mkdir -p e2e-out\n        # ensure otelcol (which runs non-root) can write here regardless of uid mapping\n        chmod 0777 e2e-out\n        cat > otel-e2e-config.yaml <<'EOF'\n        receivers:\n          otlp:\n            protocols:\n              grpc:\n                endpoint: 0.0.0.0:4317\n\n        processors:\n          batch: {}\n\n        exporters:\n          file:\n            path: /out/otel.jsonl\n            # keep default json lines, rotate small just in case\n            rotation:\n              max_megabytes: 5\n              max_days: 1\n            format: json\n\n        service:\n          pipelines:\n            metrics:\n              receivers: [otlp]\n              processors: [batch]\n              exporters: [file]\n            logs:\n              receivers: [otlp]\n              processors: [batch]\n              exporters: [file]\n        EOF\n\n    - name: Start OpenTelemetry Collector\n      run: |\n        set -euxo pipefail\n        # Use contrib distribution to ensure file exporter availability\n        docker pull otel/opentelemetry-collector-contrib:0.102.0\n        docker run -d --rm \\\n          --name otelcol-e2e \\\n          --network host \\\n          -v \"$(pwd)/otel-e2e-config.yaml:/etc/otelcol/config.yaml:ro\" \\\n          -v \"$(pwd)/e2e-out:/out\" \\\n          otel/opentelemetry-collector-contrib:0.102.0 \\\n          --config=/etc/otelcol/config.yaml\n        # Give the collector a moment to start\n        sleep 3\n        docker logs --tail 50 otelcol-e2e || true\n\n    - name: Start reducer\n      run: |\n        set -euxo pipefail\n        REDUCER_ID=$(docker run -d --rm \\\n          --name reducer-e2e \\\n          --network host \\\n          localhost:5000/reducer \\\n          --port 8000 \\\n          --prom 0.0.0.0:7000 \\\n          --partitions-per-shard 1 \\\n          --num-ingest-shards=1 \\\n          --num-matching-shards=1 \\\n          --num-aggregation-shards=1 \\\n          --enable-aws-enrichment \\\n          --enable-id-id \\\n          --enable-flow-logs \\\n          --enable-otlp-grpc-metrics \\\n          --otlp-grpc-metrics-host 127.0.0.1 \\\n          --otlp-grpc-metrics-port 4317 \\\n          --log-console \\\n          --debug \\\n          --log-whitelist-all)\n        echo \"Reducer started: ${REDUCER_ID}\"\n        # brief wait to ensure listener is up\n        sleep 5\n        docker logs --tail 100 reducer-e2e || true\n\n    - name: Start kernel collector\n      run: |\n        set -euxo pipefail\n        KERNEL_ID=$(docker run -d --rm \\\n          --name kernel-collector-e2e \\\n          --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n          --env EBPF_NET_INTAKE_PORT=\"8000\" \\\n          --env EBPF_NET_HOST_DIR=\"/hostfs\" \\\n          --privileged \\\n          --network host \\\n          --pid host \\\n          --volume /var/run/docker.sock:/var/run/docker.sock \\\n          --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup \\\n          --volume /usr/src:/hostfs/usr/src \\\n          --volume /lib/modules:/hostfs/lib/modules \\\n          --volume /etc:/hostfs/etc \\\n          --volume /var/cache:/hostfs/cache \\\n          localhost:5000/kernel-collector \\\n          --log-console)\n        echo \"Kernel collector started: ${KERNEL_ID}\"\n        # give it a moment to initialize BPF\n        sleep 15\n        docker logs --tail 50 kernel-collector-e2e || true\n\n    - name: Start k8s collector\n      run: |\n        set -euxo pipefail\n        KUBECONFIG_PATH=\"${KUBECONFIG:-$HOME/.kube/config}\"\n        if [ ! -f \"${KUBECONFIG_PATH}\" ]; then\n          echo \"KUBECONFIG not found at ${KUBECONFIG_PATH}\"\n          ls -la \"${HOME}\" || true\n          ls -la \"${HOME}/.kube\" || true\n          exit 1\n        fi\n\n        echo \"Using kubeconfig at ${KUBECONFIG_PATH}\"\n        kubectl config current-context || true\n\n        K8S_COLLECTOR_ID=$(docker run -d \\\n          --name k8s-collector-e2e \\\n          --network host \\\n          --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n          --env EBPF_NET_INTAKE_PORT=\"8000\" \\\n          --env RUST_LOG=\"info\" \\\n          --env KUBECONFIG=\"/kubeconfig\" \\\n          --volume \"${KUBECONFIG_PATH}:/kubeconfig:ro\" \\\n          localhost:5000/k8s-collector)\n        echo \"k8s-collector started: ${K8S_COLLECTOR_ID}\"\n        # allow it to connect and start watching\n        sleep 10\n        docker logs --tail 100 k8s-collector-e2e || true\n\n    - name: Generate traffic and wait for collection\n      run: |\n        set -euxo pipefail\n        # Deploy Kubernetes traffic generator in the kind cluster\n        KUBECONFIG_PATH=\"${KUBECONFIG:-$HOME/.kube/config}\"\n        if [ ! -f \"${KUBECONFIG_PATH}\" ]; then\n          echo \"KUBECONFIG not found at ${KUBECONFIG_PATH}\"\n          ls -la \"${HOME}\" || true\n          ls -la \"${HOME}/.kube\" || true\n          exit 1\n        fi\n\n        kubectl create namespace e2e-kind || true\n\n        cat > e2e-kind-traffic.yaml <<'EOF'\n        apiVersion: apps/v1\n        kind: Deployment\n        metadata:\n          name: e2e-wget\n          namespace: e2e-kind\n          labels:\n            app: e2e-wget\n            e2e-role: traffic\n        spec:\n          replicas: 10\n          selector:\n            matchLabels:\n              app: e2e-wget\n          template:\n            metadata:\n              labels:\n                app: e2e-wget\n                e2e-role: traffic\n            spec:\n              containers:\n              - name: wget\n                image: busybox:1.36\n                command:\n                  - /bin/sh\n                  - -c\n                  - |\n                    echo \"e2e-wget: starting in pod $(hostname)\"\n                    echo \"e2e-wget: uname: $(uname -a || echo 'uname failed')\"\n                    echo \"e2e-wget: /etc/resolv.conf:\"\n                    cat /etc/resolv.conf || echo \"e2e-wget: unable to read /etc/resolv.conf\"\n                    echo \"e2e-wget: testing DNS for example.com\"\n                    nslookup example.com || echo \"e2e-wget: nslookup example.com failed (command may be missing)\"\n                    TARGET_URL=\"http://example.com\"\n                    echo \"e2e-wget: running wget to ${TARGET_URL}\"\n                    if wget --timeout=15 --tries=3 -O /dev/null \"${TARGET_URL}\"; then\n                      echo \"e2e-wget: wget succeeded\"\n                    else\n                      ec=$?\n                      echo \"e2e-wget: wget failed with exit code ${ec}\"\n                    fi\n                    echo \"e2e-wget: finished, sleeping\"\n                    # keep the pod running long enough that it does not restart during the test\n                    sleep 600\n        EOF\n\n        kubectl apply -f e2e-kind-traffic.yaml\n        kubectl -n e2e-kind rollout status deploy/e2e-wget --timeout=180s\n        kubectl -n e2e-kind get pods -o wide || true\n\n        # Allow time for metrics to propagate and be exported through collectors\n        sleep 60\n\n    - name: Stop services and collect logs\n      if: always()\n      run: |\n        set -euxo pipefail\n        mkdir -p e2e-logs || true\n        docker ps || true\n        # Capture full container logs to files for later inspection\n        docker logs k8s-collector-e2e > e2e-logs/k8s-collector-e2e.log 2>&1 || true\n        docker logs kernel-collector-e2e > e2e-logs/kernel-collector-e2e.log 2>&1 || true\n        docker logs reducer-e2e > e2e-logs/reducer-e2e.log 2>&1 || true\n        docker logs otelcol-e2e > e2e-logs/otelcol-e2e.log 2>&1 || true\n        # Also emit a short tail to the job logs for quick debugging\n        docker logs --tail 200 k8s-collector-e2e || true\n        docker logs --tail 200 kernel-collector-e2e || true\n        docker logs --tail 200 reducer-e2e || true\n        docker logs --tail 200 otelcol-e2e || true\n        docker stop k8s-collector-e2e || true\n        docker rm k8s-collector-e2e || true\n        docker stop kernel-collector-e2e || true\n        docker stop reducer-e2e || true\n        docker stop otelcol-e2e || true\n        ls -la e2e-out || true\n        sudo chmod -R a+r e2e-out || true\n        # Show a small preview for debugging convenience\n        if [ -f e2e-out/otel.jsonl ]; then head -n 50 e2e-out/otel.jsonl || true; fi\n\n    - name: Upload OpenTelemetry JSONL\n      if: always()\n      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n      with:\n        name: e2e-otel-jsonl\n        path: |\n          e2e-out/*\n        if-no-files-found: warn\n        retention-days: 7\n\n    - name: Upload e2e container logs\n      if: always()\n      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2\n      with:\n        name: e2e-container-logs\n        path: |\n          e2e-logs/*\n        if-no-files-found: warn\n        retention-days: 7\n\n    - name: Show tcp.bytes metrics from JSONL\n      if: always()\n      run: |\n        set +e\n        if [ ! -f e2e-out/otel.jsonl ]; then\n          echo \"e2e-out/otel.jsonl not found; skipping tcp.bytes display\"\n          exit 0\n        fi\n\n        if ! command -v jq >/dev/null 2>&1; then\n          echo \"jq not found; attempting to install\"\n          sudo apt-get update && sudo apt-get install -y jq || true\n        fi\n\n        if ! command -v jq >/dev/null 2>&1; then\n          echo \"jq still not available; skipping tcp.bytes display\"\n          exit 0\n        fi\n\n        echo \"=== tcp.bytes metrics (pretty-printed) ===\"\n        jq '\n          .resourceMetrics[]?\n          | .scopeMetrics[]?\n          | .metrics[]?\n          | select(.name == \"tcp.bytes\")\n        ' e2e-out/otel.jsonl || true\n\n    - name: Show wget pod logs\n      if: always()\n      run: |\n        set +e\n        KUBECONFIG_PATH=\"${KUBECONFIG:-$HOME/.kube/config}\"\n        if [ ! -f \"${KUBECONFIG_PATH}\" ]; then\n          echo \"KUBECONFIG not found; skipping wget pod logs\"\n          exit 0\n        fi\n\n        echo \"=== e2e-kind namespace pods ===\"\n        kubectl -n e2e-kind get pods -o wide || true\n\n        echo \"=== e2e-wget pod logs (tail) ===\"\n        kubectl -n e2e-kind logs -l app=e2e-wget --tail=100 || true\n\n  run-kernel-collector-simple-tests:\n    name: run-kernel-collector-simple-tests\n    needs: [build-kernel-collector]\n    runs-on: ubuntu-24.04\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.4-20250721.013324'\n            description: 'Kernel 5.4'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.10-20250507.063028'\n            description: 'Kernel 5.10'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.15-20250507.063028'\n            description: 'Kernel 5.15'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.1-20250507.063028'\n            description: 'Kernel 6.1'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.6-20250507.063028'\n            description: 'Kernel 6.6'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.12-20250507.063028'\n            description: 'Kernel 6.12'\n    timeout-minutes: 10\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Download kernel-collector container\n      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0\n      with:\n        name: kernel-collector-container\n        path: ./container-exports\n\n    - name: Cache and install LVH host dependencies\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        # Match LVH action.yaml dependency_list exactly\n        packages: cpu-checker qemu-system-x86 libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager\n        version: lvh-deps-1\n\n    - name: Run kernel collector simple tests on ${{ matrix.description }}\n      uses: yonch/little-vm-helper@main\n      with:\n        test-name: kernel-collector-simple-test-${{ matrix.kernel }}\n        image: 'complexity-test'\n        image-version: ${{ matrix.kernel }}\n        host-mount: ./\n        images-folder-parent: \"/tmp\"\n        cpu: 2\n        mem: 2G\n        cpu-kind: 'host,pmu=on'\n        lvh-version: \"v0.0.23\"\n        install-dependencies: 'true'\n        verbose: 'true'\n        cmd: |\n          set -e  # Exit on any error\n          cd /host\n          \n          # Load container images\n          docker load < container-exports/kernel-collector.tar\n          \n          # Start nc listener\n          apt-get update && apt-get install -y netcat-openbsd\n          \n          echo \"Starting netcat listener on port 8000...\"\n          nc -vl 8000 &\n          nc_pid=$!\n          echo \"NC listener started with PID: $nc_pid\"\n          \n          # Wait a moment for nc to start\n          sleep 2\n          \n          # Test: Verify kernel collector loads successfully with libbpf\n          echo \"=== Kernel Collector Simple Test with libbpf ===\"\n          \n          # Run kernel collector and verify it starts successfully\n          container_id=$(docker create \\\n            --name \"test-kernel-collector-libbpf\" \\\n            --env EBPF_NET_INTAKE_PORT=\"8000\" \\\n            --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n            --env EBPF_NET_HOST_DIR=\"/hostfs\" \\\n            --privileged --pid host --network host \\\n            --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup \\\n            --volume /etc:/hostfs/etc \\\n            --volume /var/run/docker.sock:/var/run/docker.sock \\\n            localhost:5000/kernel-collector --log-console --debug)\n          \n          echo \"Starting kernel collector and running for 30 seconds...\"\n          docker start $container_id &\n          collector_pid=$!\n          \n          # Wait for 30 seconds\n          sleep 30\n          \n          # Check if container is still running\n          echo Checking if container is still running:\n          if docker ps --filter \"id=$container_id\" --filter \"status=running\" --quiet > /dev/null; then\n            echo \"✓ Kernel collector loaded successfully and ran for 30 seconds\"\n            echo \"---Kernel collector logs:\"\n            collector_logs=$(docker logs $container_id 2>&1 || true)\n            echo \"$collector_logs\"\n            \n            # Fail if a crash was detected in the kernel collector logs\n            if echo \"$collector_logs\" | grep -qi \"CRASH DETECTED\"; then\n              echo \"✗ Crash detected in kernel collector output - test failed\"\n              docker stop $container_id || true\n              docker rm $container_id || true\n              # Stop nc listener\n              kill $nc_pid || true\n              exit 1\n            fi\n            \n            # Check for error strings in the logs (exclude GCP metadata fetch errors which are expected)\n            if echo \"$collector_logs\" | grep -i \"error\" | grep -v \"Unable to fetch GCP metadata: error while fetching Google Cloud Platform instance metadata\" > /dev/null 2>&1; then\n              echo \"✗ Found 'error' in kernel collector output - test failed\"\n              docker stop $container_id || true\n              docker rm $container_id || true\n              # Stop nc listener\n              kill $nc_pid || true\n              exit 1\n            fi\n            \n            docker stop $container_id || true\n            docker rm $container_id || true\n            # Stop nc listener\n            kill $nc_pid || true\n            exit 0\n          else\n            echo \"✗ Kernel collector failed to run properly\"\n            echo \"---Kernel collector logs:\"\n            docker logs $container_id || true\n            docker rm $container_id || true\n            # Stop nc listener\n            kill $nc_pid || true\n            exit 1\n          fi\n\n    - name: Stop qemu\n      if: always()\n      run: |\n        sudo pkill -f qemu-system-x86_64 || true\n\n  run-kernel-collector-tests:\n    name: run-kernel-collector-tests\n    needs: [build-reducer, build-kernel-collector-test]\n    runs-on: ubuntu-24.04\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.4-20250721.013324'\n            description: 'Kernel 5.4'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.10-20250507.063028'\n            description: 'Kernel 5.10'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '5.15-20250507.063028'\n            description: 'Kernel 5.15'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.1-20250507.063028'\n            description: 'Kernel 6.1'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.6-20250507.063028'\n            description: 'Kernel 6.6'\n          # renovate: datasource=docker depName=quay.io/lvh-images/complexity-test\n          - kernel: '6.12-20250507.063028'\n            description: 'Kernel 6.12'\n    timeout-minutes: 10\n    steps:\n    - name: Check out the codebase\n      uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      with:\n        fetch-depth: 0\n\n    - name: Download reducer container\n      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0\n      with:\n        name: reducer-container\n        path: ./container-exports\n\n    - name: Download kernel-collector-test container\n      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0\n      with:\n        name: kernel-collector-test-container\n        path: ./container-exports\n\n    - name: Cache and install LVH host dependencies\n      uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        # Match LVH action.yaml dependency_list exactly\n        packages: cpu-checker qemu-system-x86 libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager\n        version: lvh-deps-1\n\n    - name: Run kernel collector tests on ${{ matrix.description }}\n      uses: yonch/little-vm-helper@main\n      with:\n        test-name: kernel-collector-test-${{ matrix.kernel }}\n        image: 'complexity-test'\n        image-version: ${{ matrix.kernel }}\n        host-mount: ./\n        images-folder-parent: \"/tmp\"\n        cpu: 2\n        mem: 2G\n        cpu-kind: 'host,pmu=on'\n        lvh-version: \"v0.0.23\"\n        install-dependencies: 'true'\n        verbose: 'true'\n        cmd: |\n          set -e  # Exit on any error\n          cd /host\n          \n          # Load container images\n          docker load < container-exports/reducer.tar\n          docker load < container-exports/kernel-collector-test.tar\n          \n          # Create data directory\n          mkdir -p data\n          \n          # Start reducer\n          reducer_id=$(docker run --detach --rm \\\n            --network=host \\\n            localhost:5000/reducer \\\n            --port 8000 \\\n            --prom 0.0.0.0:7000 \\\n            --partitions-per-shard 1 \\\n            --num-ingest-shards=1 \\\n            --num-matching-shards=1 \\\n            --num-aggregation-shards=1 \\\n            --enable-aws-enrichment \\\n            --enable-otlp-grpc-metrics \\\n            --log-console \\\n            --debug)\n          \n          echo \"Reducer started with ID: $reducer_id\"\n          \n          # Wait a moment for reducer to start\n          sleep 5\n          \n          # Run kernel collector test and capture real exit code\n          echo \"Starting kernel collector test...\"\n          set +e  # disable exit on error to capture exit status\n          docker run --name kernel-collector-test-$$ \\\n            --rm \\\n            --env EBPF_NET_HOST_DIR=\"/hostfs\" \\\n            --privileged \\\n            --network host \\\n            --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup \\\n            --volume /usr/src:/hostfs/usr/src \\\n            --volume /lib/modules:/hostfs/lib/modules \\\n            --volume /etc:/hostfs/etc \\\n            --volume /var/cache:/hostfs/cache \\\n            --volume /var/run/docker.sock:/var/run/docker.sock \\\n            --env EBPF_NET_KERNEL_HEADERS_AUTO_FETCH=\"true\" \\\n            --env EBPF_NET_EXPORT_BPF_SRC_FILE=\"/hostfs/data/bpf.src.c\" \\\n            --volume \"$(pwd)/data:/hostfs/data\" \\\n            localhost:5000/kernel-collector-test \\\n            --log-console\n          test_exit_code=$?\n          set -e  # re-enable exit on error\n          \n          # Stop reducer\n          docker stop $reducer_id || true\n          \n          echo \"Test completed with exit code: $test_exit_code\"\n          exit $test_exit_code\n\n    - name: Stop qemu\n      if: always()\n      run: |\n        sudo pkill -f qemu-system-x86_64 || true\n\n    - name: Upload kernel-collector dumps\n      if: always()\n      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n      with:\n        name: kernel-collector-dumps-${{ matrix.kernel }}\n        path: |\n          data/*.json\n        if-no-files-found: warn\n"
  },
  {
    "path": ".github/workflows/build-benv-multiarch.yaml",
    "content": "name: build-benv-multiarch\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ main ]\n    paths:\n      - 'build-tools/**'\n      - '.github/workflows/build-benv-multiarch.yaml'\n\npermissions:\n  contents: read\n  packages: write\n\njobs:\n  context:\n    runs-on: ubuntu-latest\n    outputs:\n      short-sha: ${{ steps.s.outputs.s }}\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      - name: Get short SHA\n        id: s\n        run: echo \"s=$(git rev-parse --short=7 ${{ github.sha }})\" >> \"$GITHUB_OUTPUT\"\n\n  build-amd64:\n    needs: context\n    uses: ./.github/workflows/build-benv-single-arch.yaml\n    permissions:\n      contents: read\n      packages: write\n    with:\n      runner: ubuntu-24.04\n      arch: amd64\n      ref: ${{ github.ref }}\n      force_rebuild: false\n      cache_registry: ghcr.io\n    secrets: inherit\n\n  build-arm64:\n    needs: context\n    uses: ./.github/workflows/build-benv-single-arch.yaml\n    permissions:\n      contents: read\n      packages: write\n    with:\n      runner: ubuntu-24.04-arm\n      arch: arm64\n      ref: ${{ github.ref }}\n      force_rebuild: false\n      cache_registry: ghcr.io\n    secrets: inherit\n\n  manifest:\n    needs: [context, build-amd64, build-arm64]\n    if: github.event_name != 'pull_request'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n    env:\n      GHCR_CACHE_REPO: ghcr.io/${{ github.repository_owner }}/opentelemetry-network-build-tools-cache\n      DOCKER_REPO: docker.io/${{ vars.DOCKER_NAMESPACE || 'otel' }}/opentelemetry-network-build-tools\n      SHORT_SHA: ${{ needs.context.outputs.short-sha }}\n      AMD64_SRC: ${{ needs.build-amd64.outputs.final_image }}\n      ARM64_SRC: ${{ needs.build-arm64.outputs.final_image }}\n    steps:\n      - name: Login to GHCR\n        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1\n\n      - name: Create GHCR multi-arch cache manifest\n        shell: bash\n        run: |\n          set -euo pipefail\n          CACHE_TAG=\"$GHCR_CACHE_REPO:final-multiarch-$SHORT_SHA\"\n          docker buildx imagetools create --tag \"$CACHE_TAG\" \"$AMD64_SRC\" \"$ARM64_SRC\"\n          docker buildx imagetools inspect \"$CACHE_TAG\"\n\n      - name: Login to Docker Hub\n        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0\n        with:\n          registry: docker.io\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Create Docker Hub multi-arch manifest\n        shell: bash\n        run: |\n          set -euo pipefail\n          MAIN_TAG=\"$DOCKER_REPO:git-$SHORT_SHA\"\n          docker buildx imagetools create --tag \"$MAIN_TAG\" \"$AMD64_SRC\" \"$ARM64_SRC\"\n          if [ \"${GITHUB_REF##*/}\" = \"main\" ]; then\n            docker buildx imagetools create --tag \"$DOCKER_REPO:latest\" \"$AMD64_SRC\" \"$ARM64_SRC\"\n          fi\n          docker buildx imagetools inspect \"$MAIN_TAG\"\n"
  },
  {
    "path": ".github/workflows/build-benv-single-arch.yaml",
    "content": "name: build-benv-single-arch\n\non:\n  workflow_call:\n    inputs:\n      runner:\n        description: Runner label (ubuntu-24.04 or ubuntu-24.04-arm)\n        required: true\n        type: string\n      arch:\n        description: Target architecture (amd64 or arm64)\n        required: true\n        type: string\n      ref:\n        description: Tag, branch or SHA to checkout\n        required: false\n        type: string\n        default: main\n      cache_registry:\n        description: Cache registry to use (GHCR)\n        required: false\n        type: string\n        default: ghcr.io\n      force_rebuild:\n        description: Force rebuild all containers (ignore cache)\n        required: false\n        type: boolean\n        default: false\n    outputs:\n      final_image:\n        description: Full image tag for the final stage in the cache registry\n        value: ${{ jobs.build-final.outputs['full-image-tag'] }}\n\npermissions:\n  contents: read\n  packages: write\n\nenv:\n  CACHE_REGISTRY: ${{ inputs.cache_registry || 'ghcr.io' }}\n  REF: ${{ inputs.ref || github.ref }}\n  FORCE_REBUILD: ${{ inputs.force_rebuild || false }}\n\njobs:\n  build-base:\n    name: Build base image\n    runs-on: ${{ inputs.runner }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push base image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: base\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n\n  build-libuv:\n    name: Build libuv image\n    runs-on: ${{ inputs.runner }}\n    needs: build-base\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push libuv image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: libuv\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n  build-cpp-misc:\n    name: Build cpp_misc image\n    runs-on: ${{ inputs.runner }}\n    needs: build-base\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push cpp_misc image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: cpp_misc\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n  build-libmaxminddb:\n    name: Build libmaxminddb image\n    runs-on: ${{ inputs.runner }}\n    needs: build-base\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push libmaxminddb image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: libmaxminddb\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n  build-libbpf:\n    name: Build libbpf image\n    runs-on: ${{ inputs.runner }}\n    needs: build-base\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push libbpf image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: libbpf\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n  build-aws-sdk:\n    name: Build aws_sdk image\n    runs-on: ${{ inputs.runner }}\n    needs: [build-base]\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push aws_sdk image\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: aws_sdk\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n\n  build-final:\n    name: Build final image\n    runs-on: ${{ inputs.runner }}\n    needs: [\n      build-base,\n      build-libuv,\n      build-aws-sdk,\n      build-cpp-misc,\n      build-libmaxminddb,\n      build-libbpf\n    ]\n    outputs:\n      image-tag: ${{ steps.build.outputs.image-tag }}\n      full-image-tag: ${{ steps.build.outputs.full-image-tag }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          ref: ${{ env.REF }}\n\n      - name: Build and push final image to cache registry\n        id: build\n        uses: ./.github/actions/build-tools-single-stage/\n        with:\n          directory: final\n          arch: ${{ inputs.arch }}\n          registry: ${{ env.CACHE_REGISTRY }}\n          registry_username: ${{ github.actor }}\n          registry_password: ${{ secrets.GITHUB_TOKEN }}\n          ref: ${{ env.REF }}\n          force_rebuild: ${{ env.FORCE_REBUILD }}\n"
  },
  {
    "path": ".github/workflows/fossa.yml",
    "content": "name: FOSSA scanning\n\non:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n\njobs:\n  fossa:\n    if: github.repository == 'open-telemetry/opentelemetry-network-build-tools'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n\n      - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0\n        with:\n          api-key: ${{secrets.FOSSA_API_KEY}}\n          team: OpenTelemetry\n"
  },
  {
    "path": ".github/workflows/k8s-collector-integration.yaml",
    "content": "name: k8s-collector-integration\n\non:\n  workflow_call:\n  push:\n    branches:\n      - main\n  pull_request:\n    paths:\n      - \"crates/k8s-collector/**\"\n      - \".github/workflows/k8s-collector-integration.yaml\"\n\njobs:\n  build-k8s-collector-tests:\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Check out the codebase\n        uses: actions/checkout@v4\n\n      - name: Cache Rust build\n        uses: Swatinem/rust-cache@v2\n        with:\n          prefix-key: k8s-collector-integration-${{ runner.os }}\n\n      - name: Install Rust toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n\n      - name: Build k8s-collector integration tests (no-run)\n        run: |\n          cargo test -p k8s-collector --tests --release --no-run\n\n      - name: Collect k8s-collector integration test binary\n        run: |\n          set -euxo pipefail\n          BIN=$(find target/release/deps -maxdepth 1 -type f -executable -name 'integration_test-*' | head -n 1)\n          test -n \"$BIN\" || { echo \"k8s-collector integration test binary not found\"; exit 1; }\n          cp \"$BIN\" k8s-collector-integration\n          chmod +x k8s-collector-integration\n\n      - name: Upload k8s-collector integration binary\n        uses: actions/upload-artifact@v4\n        with:\n          name: k8s-collector-integration\n          path: k8s-collector-integration\n          if-no-files-found: error\n\n  k8s-collector-e2e:\n    needs: build-k8s-collector-tests\n    runs-on: ubuntu-24.04\n    timeout-minutes: 30\n    steps:\n      - name: Check out the codebase\n        uses: actions/checkout@v4\n\n      - name: Set up kind cluster\n        uses: helm/kind-action@v1.10.0\n        with:\n          version: v0.30.0\n          cluster_name: k8s-collector-e2e\n\n      - name: Download k8s-collector integration binary\n        uses: actions/download-artifact@v4\n        with:\n          name: k8s-collector-integration\n          path: ./\n\n      - name: Prepare test binary\n        run: |\n          chmod +x ./k8s-collector-integration || true\n\n      - name: Run k8s-collector integration tests\n        env:\n          RUST_LOG: info\n        run: |\n          for i in {1..10}; do\n            echo \"k8s-collector integration test run ${i}/10\"\n            ./k8s-collector-integration --include-ignored --nocapture --test-threads=1 || exit 1\n          done\n"
  },
  {
    "path": ".github/workflows/ossf-scorecard.yml",
    "content": "name: OSSF Scorecard\n\non:\n  push:\n    branches:\n      - main\n  schedule:\n    - cron: \"50 10 * * 3\" # once a week\n  workflow_dispatch:\n\npermissions: read-all\n\njobs:\n  analysis:\n    if: github.repository == 'open-telemetry/opentelemetry-network-build-tools'\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed for Code scanning upload\n      security-events: write\n      # Needed for GitHub OIDC token if publish_results is true\n      id-token: write\n    steps:\n      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n        with:\n          persist-credentials: false\n\n      - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable\n      # uploads of run results in SARIF format to the repository Actions tab.\n      # https://docs.github.com/en/actions/advanced-guides/storing-workflow-data-as-artifacts\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard (optional).\n      # Commenting out will disable upload of results to your repo's Code Scanning dashboard\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v4.31.3\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".github/workflows/release-please.yml",
    "content": "name: release-please\n\non:\n  push:\n    branches: [ main ]\n  workflow_dispatch: {}\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  release-please:\n    runs-on: ubuntu-24.04\n    outputs:\n      release_created: ${{ steps.release.outputs.release_created }}\n      tag_name: ${{ steps.release.outputs.tag_name }}\n    steps:\n      - name: Run Release Please\n        id: release\n        uses: googleapis/release-please-action@v4\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          config-file: release-please-config.json\n          manifest-file: .release-please-manifest.json\n\n  build-and-release:\n    needs: release-please\n    if: ${{ needs.release-please.outputs.release_created == 'true' }}\n    uses: ./.github/workflows/build-and-release.yaml\n    with:\n      release_type: public\n      # Build from the tag that Release Please just created (e.g., v0.12.0).\n      ref: ${{ needs.release-please.outputs.tag_name }}\n      image_prefix: opentelemetry-ebpf-\n      additional_tag: ''\n      dry_run: false\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/scripts/check-clang-format.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nCLANG_FORMAT=\"clang-format-19\"\nif ! command -v ${CLANG_FORMAT}\nthen\n  echo \"ERROR: requires ${CLANG_FORMAT}\"\n  exit 1\nfi\n\nRC=0\nCMD=\"${CLANG_FORMAT} -Werror --dry-run -style=file\"\nfunction check_file\n{\n  if ! ${CMD} $1\n  then\n    RC=1\n  fi\n}\n\n# Check that C and C++ source files are properly clang-formatted\nFILES=$(find ./geoip ./reducer ./test ./collector/kernel ./common ./tools \\\n\t-type f                                                           \\\n\t\\( -name \"*.c\"                                                    \\\n\t-o -name \"*.cc\"                                                   \\\n\t-o -name \"*.h\"                                                    \\\n\t-o -name \"*.inl\" \\)                                               \\\n\t-print)\n\nfor FILE in ${FILES}\ndo\n  check_file ${FILE}\ndone\n\nexit ${RC}\n"
  },
  {
    "path": ".github/workflows/trivy-scans.yml",
    "content": "name: trivy scans\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    paths:\n      - '.github/workflows/trivy-scans.yml'\n      - '.trivyignore'\n\npermissions:\n  contents: read\n\njobs:\n  trivy-fs-scan:\n    if: github.repository == 'open-telemetry/opentelemetry-network-build-tools'\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1\n      - name: Run trivy filesystem scan\n        uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1\n        with:\n          scan-type: 'fs'\n          scan-ref: '.'\n          skip-dirs: 'docs,cmake,ext'\n          format: 'table'\n          exit-code: '1'\n          severity: 'CRITICAL,HIGH'\n          ignore-unfixed: true\n          vuln-type: 'os,library'\n          timeout: 10m\n\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.pyc\n*.bak\n*.so\n*.a\n*~\n!.*\n*.backup\n*.aux\n*.log\n*.out\n*.profile\n*.bbl\n*.blg\n*.brf\n.autotools\n.cproject\n.*.cmd\n.*.d\n.deps\n.dirstamp\n*.la\n*.lo\n.libs\n.pydevproject\n.Rhistory\n.settings\n.settings/\n.~lock*#\n*.xxd\npayload_trace.dat\ntrace_stats.csv\nexec_at\ngenerated/\n.DS_Store\n*.sw?\n*.orig\ntarget/\n\nMANIFEST\n.sconsign.dblite\n\nCMakeFiles/\ncmake_install.cmake\nCMakeCache.txt\nCTestTestfile.cmake\nTesting/\ninstall_manifest.txt\n\n.idea/\n\naclocal.m4\nconfigure\nautom4te.cache\nbuild-aux\ninstall-sh\nmissing\npy-compile\nrelease/\nout/\n\nstamp-h1\nsrc/Makefile\nconfig.h\nconfig.status\nlibtool\n\nperf.data\nperf.data.old\n\n# Visual Studio Code\n.vscode/settings.json\n# Visual C++ cache files\nipch/\n\n# Gnu Global\nGPATH\nGRTAGS\nGTAGS\n\n\n#local\nVagrantfile\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"ext/civetweb\"]\n\tpath = ext/civetweb\n\turl = https://github.com/civetweb/civetweb.git\n[submodule \"build-tools/libmaxminddb/libmaxminddb\"]\n\tpath = build-tools/libmaxminddb/libmaxminddb\n\turl = https://github.com/maxmind/libmaxminddb\n[submodule \"build-tools/libbpf/bpftool\"]\n\tpath = build-tools/libbpf/bpftool\n\turl = https://github.com/libbpf/bpftool.git\n[submodule \"build-tools/cpp_misc/lz4\"]\n\tpath = build-tools/cpp_misc/lz4\n\turl = https://github.com/lz4/lz4.git\n[submodule \"build-tools/aws_sdk/aws-sdk-cpp\"]\n\tpath = build-tools/aws_sdk/aws-sdk-cpp\n\turl = https://github.com/aws/aws-sdk-cpp\n[submodule \"build-tools/cpp_misc/json\"]\n\tpath = build-tools/cpp_misc/json\n\turl = https://github.com/nlohmann/json.git\n[submodule \"build-tools/libuv/libuv\"]\n\tpath = build-tools/libuv/libuv\n\turl = https://github.com/libuv/libuv.git\n[submodule \"build-tools/cpp_misc/spdlog\"]\n\tpath = build-tools/cpp_misc/spdlog\n\turl = https://github.com/gabime/spdlog.git\n[submodule \"build-tools/cpp_misc/args\"]\n\tpath = build-tools/cpp_misc/args\n\turl = https://github.com/Taywee/args.git\n[submodule \"build-tools/libbpf/libbpf\"]\n\tpath = build-tools/libbpf/libbpf\n\turl = https://github.com/libbpf/libbpf.git\n[submodule \"build-tools/cpp_misc/yaml-cpp\"]\n\tpath = build-tools/cpp_misc/yaml-cpp\n\turl = https://github.com/jbeder/yaml-cpp.git\n[submodule \"build-tools/cpp_misc/googletest\"]\n\tpath = build-tools/cpp_misc/googletest\n\turl = https://github.com/google/googletest.git\n[submodule \"ext/vmlinux.h\"]\n\tpath = ext/vmlinux.h\n\turl = https://github.com/libbpf/vmlinux.h.git\n"
  },
  {
    "path": ".release-please-manifest.json",
    "content": "{\n  \".\": \"0.11.0\"\n}\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file by Release Please.\n\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ncmake_minimum_required(VERSION 3.12)\n\n# Load container-friendly defaults (paths, build type, version vars)\ninclude(${CMAKE_CURRENT_LIST_DIR}/cmake/defaults.cmake)\n\nproject(\n  opentelemetry-ebpf\n  VERSION ${EBPF_NET_MAJOR_VERSION}.${EBPF_NET_MINOR_VERSION}.${EBPF_NET_PATCH_VERSION}\n)\n\ninclude(GNUInstallDirs)\n\nlist(\n  APPEND\n  CMAKE_MODULE_PATH\n    ${CMAKE_CURRENT_LIST_DIR}/cmake\n)\n\ninclude(FindPkgConfig)\n\nif(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)\n  # Building as a subproject (submodule).\n  set(EBPF_NET_SUBPROJECT TRUE)\nendif()\n\n# Custom modules\n#\ninclude(cpp-compiler)\ninclude(ccache)\ninclude(docker-utils)\ninclude(sanitizer)\ninclude(executable)\ninclude(xxd)\ninclude(shell)\ninclude(debug)\ninclude(lz4)\ninclude(openssl)\ninclude(civetweb)\ninclude(curl)\ninclude(curlpp)\ninclude(spdlog)\ninclude(aws-sdk)\ninclude(geoip)\ninclude(protobuf)\ninclude(llvm)\ninclude(clang)\ninclude(libelf)\ninclude(test)\ninclude(uv)\ninclude(abseil)\ninclude(yamlcpp)\ninclude(libbpf)\ninclude(render)\ninclude(cargo-test)\ninclude(rust_cxxbridge)\n\ninclude_directories(\n  ${PROJECT_SOURCE_DIR}\n  ${CMAKE_BINARY_DIR}\n  ${CMAKE_INSTALL_PREFIX}\n  ${CMAKE_INSTALL_PREFIX}/include\n)\n\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\nset(CONFIG_H_DIR ${CMAKE_CURRENT_BINARY_DIR})\n\nadd_custom_target(pipeline)\nadd_custom_target(pipeline-docker)\nadd_custom_target(pipeline-docker-registry)\n\nadd_subdirectory(renderc)\nadd_subdirectory(render)\nadd_subdirectory(config)\nadd_subdirectory(channel)\nadd_subdirectory(platform)\nadd_subdirectory(scheduling)\nadd_subdirectory(util)\nadd_subdirectory(geoip)\nadd_subdirectory(jitbuf)\nadd_subdirectory(collector)\nadd_subdirectory(reducer)\nadd_subdirectory(dev)\nadd_subdirectory(tools)\nadd_subdirectory(dist)\n\nconfigure_file(config.h.cmake_in config.h)\nconfigure_file(util/version_config.h.cmake_in util/version_config.h)\n\nadd_dependencies(pipeline collectors reducer)\nadd_dependencies(pipeline-docker collectors-docker)\nadd_dependencies(pipeline-docker-registry collectors-docker-registry)\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n  \"crates/kernel-collector-sys\",\n  \"crates/kernel-collector-bin\",\n  \"crates/reducer-sys\",\n  \"crates/reducer-bin\",\n  \"crates/cloud-collector-sys\",\n  \"crates/cloud-collector-bin\",\n  \"crates/k8s-relay-sys\",\n  \"crates/k8s-relay-bin\",\n  \"crates/reducer\",\n  \"crates/perfect_hash_map\",\n  \"crates/render_parser\",\n  \"crates/otlp_export\",\n  \"crates/element-queue\",\n  \"crates/timeslot\",\n  # Render encoders (ebpf_net)\n  \"crates/render/ebpf_net\",\n  \"crates/render/ebpf_net/agent_internal\",\n  \"crates/render/ebpf_net/aggregation\",\n  \"crates/render/ebpf_net/cloud_collector\",\n  \"crates/render/ebpf_net/ingest\",\n  \"crates/render/ebpf_net/kernel_collector\",\n  \"crates/render/ebpf_net/logging\",\n  \"crates/render/ebpf_net/matching\",\n  \"crates/k8s-collector\",\n  \"crates/k8s-collector-bin\",\n]\n\n[workspace.dependencies]\nrender_parser = { path = \"crates/render_parser\" }\notlp_export = { path = \"crates/otlp_export\" }\nreducer = { path = \"crates/reducer\" }\nreducer-sys = { path = \"crates/reducer-sys\" }\nrc-hashmap = { version = \"0.1\" }\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE.txt",
    "content": "OpenTelemetry eBPF\n==================\n\nhttps://github.com/open-telemetry/opentelemetry-ebpf\n\nCopyright The OpenTelemetry Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\nThird party software\n====================\n\n\n-------------------------------------------------------------------------------\nargs\n\nhttps://github.com/Taywee/args\n\nCopyright (c) 2016-2017 Taylor C. Richberger <taywee@gmx.com> and Pavel Belikov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nspdlog\n\nhttps://github.com/gabime/spdlog\n\nCopyright (c) 2016 Gabi Melman.                                       \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nares\n\nhttps://github.com/c-ares/c-ares\n\n/* Copyright 1998 by the Massachusetts Institute of Technology.\n * Copyright (C) 2007-2013 by Daniel Stenberg\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n\n-------------------------------------------------------------------------------\nbcc\n\nhttps://github.com/iovisor/bcc\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\nabseil\n\nhttps://github.com/abseil/abseil-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\nllvm\n\nhttps://github.com/llvm/llvm-project\n\nUniversity of Illinois/NCSA\nOpen Source License\n\nCopyright (c) 2003-2019 University of Illinois at Urbana-Champaign.\nAll rights reserved.\n\nDeveloped by:\n\n    LLVM Team\n\n    University of Illinois at Urbana-Champaign\n\n    http://llvm.org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal with\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimers.\n\n    * Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimers in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the names of the LLVM Team, University of Illinois at\n      Urbana-Champaign, nor the names of its contributors may be used to\n      endorse or promote products derived from this Software without specific\n      prior written permission.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nCONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE\nSOFTWARE.\n\n==============================================================================\nCopyrights and Licenses for Third Party Software Distributed with LLVM:\n==============================================================================\nThe LLVM software contains code written by third parties.  Such software will\nhave its own individual LICENSE.TXT file in the directory in which it appears.\nThis file will describe the copyrights, license, and restrictions which apply\nto that code.\n\nThe disclaimer of warranty in the University of Illinois Open Source License\napplies to all code in the LLVM Distribution, and nothing in any of the\nother licenses gives permission to use the names of the LLVM Team or the\nUniversity of Illinois to endorse or promote products derived from this\nSoftware.\n\nThe following pieces of software have additional or alternate copyrights,\nlicenses, and/or restrictions:\n\nProgram             Directory\n-------             ---------\nGoogle Test         llvm/utils/unittest/googletest\nOpenBSD regex       llvm/lib/Support/{reg*, COPYRIGHT.regex}\npyyaml tests        llvm/test/YAMLParser/{*.data, LICENSE.TXT}\nARM contributions   llvm/lib/Target/ARM/LICENSE.TXT\nmd5 contributions   llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h\n\n\n-------------------------------------------------------------------------------\njson\n\nhttps://github.com/nlohmann/json\n\nCopyright (c) 2013-2018 Niels Lohmann\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\n-------------------------------------------------------------------------------\nlookup3\n\nhttp://www.burtleburtle.net/bob/c/lookup3.c\n\nlookup3.c, by Bob Jenkins, May 2006, Public Domain.\n\n\n-------------------------------------------------------------------------------\nlibuv\n\nhttps://github.com/libuv/libuv\n\nlibuv is licensed for use as follows:\n\n====\nCopyright (c) 2015-present libuv project contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n====\n\nThis license applies to parts of libuv originating from the\nhttps://github.com/joyent/libuv repository:\n\n====\n\nCopyright Joyent, Inc. and other Node contributors. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n\n====\n\nThis license applies to all parts of libuv that are not externally\nmaintained libraries.\n\nThe externally maintained libraries used by libuv are:\n\n  - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license.\n\n  - inet_pton and inet_ntop implementations, contained in src/inet.c, are\n    copyright the Internet Systems Consortium, Inc., and licensed under the ISC\n    license.\n\n  - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three\n    clause BSD license.\n\n  - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB.\n    Three clause BSD license.\n\n  - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design\n    Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement\n    n° 289016). Three clause BSD license.\n\n\n-------------------------------------------------------------------------------\nopenssl\n\nhttps://github.com/openssl/openssl\n\n/* ====================================================================\n * Copyright (c) 1998-2019 The OpenSSL Project.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * 3. All advertising materials mentioning features or use of this\n *    software must display the following acknowledgment:\n *    \"This product includes software developed by the OpenSSL Project\n *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n *\n * 4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to\n *    endorse or promote products derived from this software without\n *    prior written permission. For written permission, please contact\n *    openssl-core@openssl.org.\n *\n * 5. Products derived from this software may not be called \"OpenSSL\"\n *    nor may \"OpenSSL\" appear in their names without prior written\n *    permission of the OpenSSL Project.\n *\n * 6. Redistributions of any form whatsoever must retain the following\n *    acknowledgment:\n *    \"This product includes software developed by the OpenSSL Project\n *    for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n *\n * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY\n * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR\n * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n * ====================================================================\n *\n * This product includes cryptographic software written by Eric Young\n * (eay@cryptsoft.com).  This product includes software written by Tim\n * Hudson (tjh@cryptsoft.com).\n *\n */\n\n Original SSLeay License\n -----------------------\n\n/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\n * All rights reserved.\n *\n * This package is an SSL implementation written\n * by Eric Young (eay@cryptsoft.com).\n * The implementation was written so as to conform with Netscapes SSL.\n *\n * This library is free for commercial and non-commercial use as long as\n * the following conditions are aheared to.  The following conditions\n * apply to all code found in this distribution, be it the RC4, RSA,\n * lhash, DES, etc., code; not just the SSL code.  The SSL documentation\n * included with this distribution is covered by the same copyright terms\n * except that the holder is Tim Hudson (tjh@cryptsoft.com).\n *\n * Copyright remains Eric Young's, and as such any Copyright notices in\n * the code are not to be removed.\n * If this package is used in a product, Eric Young should be given attribution\n * as the author of the parts of the library used.\n * This can be in the form of a textual message at program startup or\n * in documentation (online or textual) provided with the package.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. All advertising materials mentioning features or use of this software\n *    must display the following acknowledgement:\n *    \"This product includes cryptographic software written by\n *     Eric Young (eay@cryptsoft.com)\"\n *    The word 'cryptographic' can be left out if the rouines from the library\n *    being used are not cryptographic related :-).\n * 4. If you include any Windows specific code (or a derivative thereof) from\n *    the apps directory (application code) you must include an acknowledgement:\n *    \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n *\n * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * The licence and distribution terms for any publically available version or\n * derivative of this code cannot be changed.  i.e. this code cannot simply be\n * copied and put under another distribution licence\n * [including the GNU Public Licence.]\n */\n\n\n-------------------------------------------------------------------------------\ngrpc\n\nhttps://github.com/grpc/grpc\n\nCopyright 2014 gRPC authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n-------------------------------------------------------------------------------\ncurl\n\nhttps://github.com/curl/curl\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright (c) 1996 - 2017, Daniel Stenberg, <daniel@haxx.se>, and many\ncontributors, see the THANKS file.\n\nAll rights reserved.\n\nPermission to use, copy, modify, and distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright\nnotice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\nOR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall not\nbe used in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization of the copyright holder.\n\n\n-------------------------------------------------------------------------------\ncurlpp\n\nhttps://github.com/jpbarrette/curlpp\n\nCopyright (c) <2002-2004> <Jean-Philippe Barrette-LaPierre>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files \n(cURLpp), to deal in the Software without restriction, \nincluding without limitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of the Software,\nand to permit persons to whom the Software is furnished to do so, \nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. \nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY \nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, \nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\naws-sdk-cpp\n\nhttps://github.com/aws/aws-sdk-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\ngoogle-cloud-cpp\n\nhttps://github.com/googleapis/google-cloud-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\nccan\n\nhttps://github.com/rustyrussell/ccan\n\nThe list module is licensed under BSD-MIT:\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in\n  all copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n  THE SOFTWARE.\n\n\nThe build_assert, check_type, compiler, container_of and hash modules are\nlicensed under CC0 (Public domain):\n\n  Statement of Purpose\n\n  The laws of most jurisdictions throughout the world automatically confer\n  exclusive Copyright and Related Rights (defined below) upon the creator and\n  subsequent owner(s) (each and all, an \"owner\") of an original work of\n  authorship and/or a database (each, a \"Work\").\n\n  Certain owners wish to permanently relinquish those rights to a Work for the\n  purpose of contributing to a commons of creative, cultural and scientific\n  works (\"Commons\") that the public can reliably and without fear of later\n  claims of infringement build upon, modify, incorporate in other works, reuse\n  and redistribute as freely as possible in any form whatsoever and for any\n  purposes, including without limitation commercial purposes. These owners may\n  contribute to the Commons to promote the ideal of a free culture and the\n  further production of creative, cultural and scientific works, or to gain\n  reputation or greater distribution for their Work in part through the use and\n  efforts of others.\n\n  For these and/or other purposes and motivations, and without any expectation\n  of additional consideration or compensation, the person associating CC0 with a\n  Work (the \"Affirmer\"), to the extent that he or she is an owner of Copyright\n  and Related Rights in the Work, voluntarily elects to apply CC0 to the Work\n  and publicly distribute the Work under its terms, with knowledge of his or her\n  Copyright and Related Rights in the Work and the meaning and intended legal\n  effect of CC0 on those rights.\n\n  1. Copyright and Related Rights. A Work made available under CC0 may be\n  protected by copyright and related or neighboring rights (\"Copyright and\n  Related Rights\"). Copyright and Related Rights include, but are not limited\n  to, the following:\n\n    the right to reproduce, adapt, distribute, perform, display, communicate,\n    and translate a Work;\n\n    moral rights retained by the original author(s) and/or performer(s);\n\n    publicity and privacy rights pertaining to a person's image or likeness\n    depicted in a Work;\n\n    rights protecting against unfair competition in regards to a Work, subject\n    to the limitations in paragraph 4(a), below;\n\n    rights protecting the extraction, dissemination, use and reuse of data in a\n    Work;\n\n    database rights (such as those arising under Directive 96/9/EC of the\n    European Parliament and of the Council of 11 March 1996 on the legal\n    protection of databases, and under any national implementation thereof,\n    including any amended or successor version of such directive);\n\n    and other similar, equivalent or corresponding rights throughout the world\n    based on applicable law or treaty, and any national implementations thereof.\n\n  2. Waiver. To the greatest extent permitted by, but not in contravention of,\n  applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and\n  unconditionally waives, abandons, and surrenders all of Affirmer's Copyright\n  and Related Rights and associated claims and causes of action, whether now\n  known or unknown (including existing as well as future claims and causes of\n  action), in the Work (i) in all territories worldwide, (ii) for the maximum\n  duration provided by applicable law or treaty (including future time\n  extensions), (iii) in any current or future medium and for any number of\n  copies, and (iv) for any purpose whatsoever, including without limitation\n  commercial, advertising or promotional purposes (the \"Waiver\"). Affirmer makes\n  the Waiver for the benefit of each member of the public at large and to the\n  detriment of Affirmer's heirs and successors, fully intending that such Waiver\n  shall not be subject to revocation, rescission, cancellation, termination, or\n  any other legal or equitable action to disrupt the quiet enjoyment of the Work\n  by the public as contemplated by Affirmer's express Statement of Purpose.\n\n  3. Public License Fallback. Should any part of the Waiver for any reason be\n  judged legally invalid or ineffective under applicable law, then the Waiver\n  shall be preserved to the maximum extent permitted taking into account\n  Affirmer's express Statement of Purpose. In addition, to the extent the Waiver\n  is so judged Affirmer hereby grants to each affected person a royalty-free,\n  non transferable, non sublicensable, non exclusive, irrevocable and\n  unconditional license to exercise Affirmer's Copyright and Related Rights in\n  the Work (i) in all territories worldwide, (ii) for the maximum duration\n  provided by applicable law or treaty (including future time extensions), (iii)\n  in any current or future medium and for any number of copies, and (iv) for any\n  purpose whatsoever, including without limitation commercial, advertising or\n  promotional purposes (the \"License\"). The License shall be deemed effective as\n  of the date CC0 was applied by Affirmer to the Work. Should any part of the\n  License for any reason be judged legally invalid or ineffective under\n  applicable law, such partial invalidity or ineffectiveness shall not\n  invalidate the remainder of the License, and in such case Affirmer hereby\n  affirms that he or she will not (i) exercise any of his or her remaining\n  Copyright and Related Rights in the Work or (ii) assert any associated claims\n  and causes of action with respect to the Work, in either case contrary to\n  Affirmer's express Statement of Purpose.\n\n  4. Limitations and Disclaimers.\n\n    No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n\n    Affirmer offers the Work as-is and makes no representations or warranties of\n    any kind concerning the Work, express, implied, statutory or otherwise,\n    including without limitation warranties of title, merchantability, fitness\n    for a particular purpose, non infringement, or the absence of latent or\n    other defects, accuracy, or the present or absence of errors, whether or not\n    discoverable, all to the greatest extent permissible under applicable law.\n\n    Affirmer disclaims responsibility for clearing rights of other persons that\n    may apply to the Work or any use thereof, including without limitation any\n    person's Copyright and Related Rights in the Work. Further, Affirmer\n    disclaims responsibility for obtaining any necessary consents, permissions\n    or other rights required for any use of the Work.\n\n    Affirmer understands and acknowledges that Creative Commons is not a party\n    to this document and has no duty or obligation with respect to this CC0 or\n    use of the Work.\n\n\n-------------------------------------------------------------------------------\nlz4\n\nhttps://github.com/lz4/lz4\n\nThis repository uses 2 different licenses :\n- all files in the `lib` directory use a BSD 2-Clause license\n- all other files use a GPLv2 license, unless explicitly stated otherwise\n\nRelevant license is reminded at the top of each source file,\nand with presence of COPYING or LICENSE file in associated directories.\n\nThis model is selected to emphasize that\nfiles in the `lib` directory are designed to be included into 3rd party applications,\nwhile all other files, in `programs`, `tests` or `examples`,\nreceive more limited attention and support for such scenario.\n\n\n-------------------------------------------------------------------------------\nyaml-cpp\n\nhttps://github.com/jbeder/yaml-cpp\n\nCopyright (c) 2008-2015 Jesse Beder.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nlibrdkafka\n\nhttps://github.com/edenhill/librdkafka\n\nlibrdkafka - Apache Kafka C driver library\n\nCopyright (c) 2012-2020, Magnus Edenhill\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice,\n   this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n\n\n-------------------------------------------------------------------------------\nlibmaxminddb\n\nhttps://github.com/maxmind/libmaxminddb\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\ngradle\n\nhttps://github.com/gradle/gradle\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n\n-------------------------------------------------------------------------------\ndemangle\n\nhttps://github.com/juchem/demangle\n\nBSD 3-Clause License\n\nCopyright (c) 2016, Marcelo Juchem\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# OpenTelemetry eBPF #\n\nThe OpenTelemetry eBPF project develops components that collect and analyze\ntelemetry from the operating system, cloud, and container orchestrators. Its initial focus\nis on collecting network data to enable users to gain insight into their distributed \napplications.\n\nThe _kernel collector_ gathers low level telemetry straight from the Linux\nkernel using [eBPF](https://ebpf.io/). It does so with negligible compute and \nnetwork overheads. The _kubernetes collector_ and _cloud collector_ gather workload\nmetadata.\n\nThis telemetry is then sent to the _reducer_, which enriches and aggregates it.\nThe reducer outputs metrics to the OpenTelemetry collector.\n\n## Building ##\n\nFor instructions on how to build the repository, see the [Developer Guide](docs/developing.md).\n\n## Running ##\n\nFor instructions on how to get OpenTelemetry-eBPF up-and-running, check the documentation for\nthe individual components:\n- [reducer](docs/reducer.md)\n- [kernel-collector](docs/kernel-collector.md)\n- [cloud-collector](docs/cloud-collector.md)\n- [k8s-collector](docs/k8s-collector.md)\n\n## Contributing ##\n\nCheck out the [Developer Guide](docs/developing.md).\n\nSee the [Roadmap](docs/roadmap.md) for an overwiew of the project's goals.\n\n### Maintainers\n\n- [Borko Jandras](https://github.com/bjandras)\n- [Jim Wilson](https://github.com/jmw51798), DataDog\n- [Jonathan Perry](https://github.com/yonch)\n\nFor more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer).\n\n### Approvers\n\n- [Samiur Arif](https://github.com/samiura), Netbox Labs\n- Actively seeking approvers to review pull requests\n\nFor more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).\n\n### Triagers\n\n- Actively seeking contributors to triage issues\n\n### Emeritus Triagers\n\n- [Antoine Toulme](https://github.com/atoulme), Splunk\n\nFor more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager).\n\n## Questions ##\n\nYou can connect with us in our [slack channel](https://cloud-native.slack.com/archives/C02AB15583A).\n\nThe OpenTelemetry eBPF special interest group (SIG) meets regularly, and the meetting is held every \nweek on Tuesday at 09:00 Pacific time.\nSee the [eBPF Workgroup Meeting Notes](https://docs.google.com/document/d/13GK915hdDQ9sUYzUIWi4pOfJK68EE935ugutUgL3yOw) for a summary description of past meetings.\n"
  },
  {
    "path": "RELEASING.md",
    "content": "# Making a public release\nPublic builds are those intended to be used by the general audience.\n\n## Release procedure (Release Please)\n1. Ensure that changes on `main` use Conventional Commit messages so Release Please can determine the correct next version.\n1. Wait for the `release-please` workflow to open a Release PR, or trigger it manually from https://github.com/open-telemetry/opentelemetry-network/actions/workflows/release-please.yml.\n1. Review the Release PR:\n   - Confirm that `VERSION` and `CHANGELOG.md` are updated as expected.\n   - Make any edits you need to the release notes in the PR description.\n1. Merge the Release PR.\n1. Once merged, Release Please will:\n   - Create a Git tag `vMAJOR.MINOR.PATCH`.\n   - Publish a GitHub Release with the generated release notes.\n1. The `build-and-release` workflow will run automatically on the `release.published` event:\n   - It checks out the release tag, builds RPM/DEB packages and container images, and uploads the RPM/DEB packages to the existing GitHub Release.\n   - It pushes container images to the configured registry with tags:\n     - `latest`\n     - `latest-vMAJOR.MINOR`\n     - `vMAJOR.MINOR.PATCH`\n1. Verify the published GitHub Release at https://github.com/open-telemetry/opentelemetry-network/releases and confirm the assets.\n1. On the Docker registry (for example https://hub.docker.com/r/otel/), confirm that the following images are tagged with the new tags:\n    * opentelemetry-ebpf-reducer\n    * opentelemetry-ebpf-kernel-collector\n    * opentelemetry-ebpf-cloud-collector\n    * opentelemetry-ebpf-k8s-watcher\n    * opentelemetry-ebpf-k8s-relay\n\nIf needed, you can still trigger the `build-and-release` workflow manually from\nhttps://github.com/open-telemetry/opentelemetry-network/actions/workflows/build-and-release.yaml\nwith `release_type: public` to rebuild artifacts for an existing tag.\n\n\n## Unofficial builds\nUnofficial builds, while public, are not intended for the use by the general audience. Because of that, no unofficial build should be tagged in such a way that it gets used automatically – no tagging with latest or with a version tag.\n\nUse a personal fork of the opentelemetry-network repository on GitHub. Make sure that the following repository secrets and variables are set up for the fork (Settings -> Secrets and variables -> Actions):\n\nvariables:\n - DOCKER_REGISTRY: registry, such as quay.io\n - DOCKER_NAMESPACE: image name, such as o11ytest/network-explorer-debug\n\nsecrets:\n - DOCKER_USERNAME: username to login to the registry\n - DOCKER_PASSWORD: password to loging to the registry\n\n\nNavigate to Actions -> build-and-release, click the \"Run workflow\" button. Select unofficial for the release type. Use whichever commit SHA, tag or branch name you wish to make a build of.\n\nThe resulting docker images will be tagged with vMAJOR.MINOR.PATCH-GITHASH tag.\n"
  },
  {
    "path": "VERSION",
    "content": "0.11.0\n\n"
  },
  {
    "path": "build-tools/.gitignore",
    "content": "/Debug/\n/Release/\n"
  },
  {
    "path": "build-tools/.templates/dependency/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# DEPENDENCY_NAME\n\nARG base_IMAGE_TAG\n\nFROM $base_IMAGE_TAG\n\nARG NPROC\nWORKDIR $HOME\nCOPY DEPENDENCY_NAME DEPENDENCY_NAME\nWORKDIR $HOME/DEPENDENCY_NAME\n\n# add build/install commands here, e.g.:\n#RUN ./bootstrap\n#RUN ./configure --prefix=$HOME/install \\\n#    --enable-static\n#RUN nice make -j$NPROC && make install\n"
  },
  {
    "path": "build-tools/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ncmake_minimum_required (VERSION 3.5)\n\nproject (opentelemetry-ebpf-build-tools VERSION 0.1.0)\n\n# Architecture:\n#\n# The build is comprised of multiple directories, each making one Docker image.\n# Each such directory can use other docker images as substrates or for artefacts.\n# The build system should rebuild a container when:\n# 1. There was a more recent commit into the directory, or\n# 2. One of the dependencies was rebuilt, or\n# 3. The build was cleaned, or\n# 4. docker doesn't have the images (e.g., because a dev explicitly erased it)\n#\n# We maintain two files for each directory inside the build-status directory:\n# A. `missing`: touch'd if docker doesn't already have an image for the most recent\n#    commit into the directory\n# B. `built`: touch'd when we've successfully created a docker image\n#\n# Each directory's docker build depends on its `missing` and the `built` of its dependencies,\n# and outputs (touch's) its own `built`.\n#\n# On every run, the build system always checks if the most recent commit to the directory is in\n# docker, and if not touches `missing`. This handles (1), and (4). Furthermore, if `missing`\n# is itself not on the filesystem, then it is touched -- this handles (3). When one of the\n# dependencies is rebuilt, this causes a rebuild of the container, solving (2).\n\nset(STATUS_DIR \"build-status\")\n\n# a dummy target to force checks against docker\nadd_custom_command(\n\tOUTPUT \"dummy_target_to_force_rebuild\"\n\tCOMMAND true\n)\n\ninclude(ProcessorCount)\nProcessorCount(NPROCS)\n\nif(${NPROCS} GREATER 1)\n  # don't use up all the cores, leave at least one for other processes and\n  # scheduling to avoid trashing\n  math(EXPR NPROCS \"${NPROCS} - 1\")\nendif()\nmessage(STATUS \"using ${NPROCS} parallel jobs to build\")\n\noption(BENV_UNMINIMIZE \"whether or not to unminimize the benv image\" OFF)\n\nfunction(build_directory NAME)\n\tcmake_parse_arguments(P \"\" \"\" \"DEPENDS\" ${ARGN})\n\n\t# the missing filename\n\tset(MISSING_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${NAME}/missing)\n\n\t# update the `missing` file\n\tadd_custom_target(check_missing_${NAME}\n\t\t# OUTPUT\n\t\t# \t${MISSING_FILENAME} # our real output\n\t\tCOMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}\n\t\tCOMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${NAME}\n\t\tCOMMAND ${CMAKE_SOURCE_DIR}/check_missing.sh ${NAME} ${MISSING_FILENAME}\n\t\t# DEPENDS \t\t\t\"dummy_target_to_force_rebuild\" # fake, to force the check to always run\n\t)\n\n\n\t# for each dependent directory, make command line parameters to pass docker the\n\t#   directory's resulting docker tag\n\tset(DOCKER_PARAMS) # the image tags for the dependencies to pass to docker\n\tset(DEPENDS_FILES) # the `built` files of the dependencies\n\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"NPROC=${NPROCS}\")\n\n\tforeach(DEP ${P_DEPENDS})\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"${DEP}_IMAGE_TAG=$$(${CMAKE_SOURCE_DIR}/get_tag.sh\" \"${DEP})\")\n\t\tlist(APPEND DEPENDS_FILES \"${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${DEP}/built\")\n\tendforeach()\n\n\tif(DEFINED ENV{BENV_BASE_IMAGE_DISTRO})\n\t\tmessage(STATUS \"using $ENV{BENV_BASE_IMAGE_DISTRO} as base image distro for ${NAME}\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"BENV_BASE_IMAGE_DISTRO=$ENV{BENV_BASE_IMAGE_DISTRO}\")\n\tendif()\n\n\tif(DEFINED ENV{BENV_BASE_IMAGE_VERSION})\n\t\tmessage(STATUS \"using $ENV{BENV_BASE_IMAGE_VERSION} as base image version for ${NAME}\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"BENV_BASE_IMAGE_VERSION=$ENV{BENV_BASE_IMAGE_VERSION}\")\n\tendif()\n\n\tif(BENV_UNMINIMIZE)\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"BENV_UNMINIMIZE=true\")\n\tendif()\n\n\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\")\n\tif(CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"RESTRICTED_NPROC='1'\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"BUILD_CFLAGS='-O0 -g'\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"CONFIGURE_ENABLE_DEBUG='--enable-debug'\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"CONFIGURE_DEBUG='--debug'\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"CONFIGURE_RELEASE_DEBUG='--debug'\")\n\telse()\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"RESTRICTED_NPROC=${NPROCS}\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"GRPC_BUILD_CFLAGS='-Wno-error=class-memaccess -Wno-error=ignored-qualifiers -Wno-error=stringop-truncation'\")\n\t\tlist(APPEND DOCKER_PARAMS \"--build-arg\" \"CONFIGURE_RELEASE_DEBUG='--release'\")\n\tendif()\n\n\tadd_custom_command(\n\t\tOUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${NAME}/built\n\t\tCOMMAND docker build -t \"$$(${CMAKE_SOURCE_DIR}/get_tag.sh\" \"${NAME})\" ${DOCKER_PARAMS} ${CMAKE_SOURCE_DIR}/${NAME} \n\t\tCOMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${NAME}/built\n\t\tDEPENDS ${P_DEPENDS} check_missing_${NAME} ${MISSING_FILENAME} ${DEPENDS_FILES}\n\t)\n\n\tadd_custom_target(${NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${STATUS_DIR}/${NAME}/built)\nendfunction(build_directory)\n\nbuild_directory(base)\nbuild_directory(openssl DEPENDS base)\nbuild_directory(curl DEPENDS base openssl)\nbuild_directory(libuv DEPENDS base)\nbuild_directory(aws_sdk DEPENDS base openssl curl)\nbuild_directory(cpp_misc DEPENDS base)\nbuild_directory(grpc_cpp DEPENDS base abseil_cpp openssl)\nbuild_directory(gcp_cpp DEPENDS base openssl curl grpc_cpp)\nbuild_directory(abseil_cpp DEPENDS base)\nbuild_directory(libmaxminddb DEPENDS base)\nbuild_directory(libbpf DEPENDS base)\n#gen:dep-dir\n\nbuild_directory(\n  final\n  DEPENDS\n    base\n    openssl\n    curl\n    libuv\n    aws_sdk\n    cpp_misc\n    grpc_cpp\n    abseil_cpp\n    libmaxminddb\n    gcp_cpp\n    libbpf\n)\n\nif(CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n\tadd_custom_target(benv ALL DEPENDS final\n\t\tCOMMAND docker tag \"$$(${CMAKE_SOURCE_DIR}/get_tag.sh\" \"final)\" debug-build-env)\nelse()\n\tadd_custom_target(debug-benv ALL DEPENDS final\n\t\tCOMMAND docker tag \"$$(${CMAKE_SOURCE_DIR}/get_tag.sh\" \"final)\" build-env)\nendif()\n"
  },
  {
    "path": "build-tools/add_dependency.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -e\n\nif [ -z \"$2\" ]; then\n  echo \"usage: $0 dependency_name dependency_git_url\"\n  exit 1\nfi\n\ndep_name=\"$1\"\ndep_repo=\"$2\"\n\necho \"adding dependency $dep_name\"\necho \"  from repo $dep_repo\"\n\nmkdir \"$dep_name\"\nsed \"s/DEPENDENCY_NAME/$dep_name/g\" \\\n  \".templates/dependency/Dockerfile\" \\\n  > \"$dep_name/Dockerfile\"\ngit add \"$dep_name/Dockerfile\"\n\nsed -i \\\n  -e \"s/^#gen:dep-dir\\$/build_directory($dep_name DEPENDS base)\\n&/g\" \\\n  -e \"s/^\\(build_directory(final DEPENDS base .*\\))\\$/\\1 $dep_name)/g\" \\\n  CMakeLists.txt\ngit add CMakeLists.txt\n\nsed -i \\\n  -e \"s/^#gen:dep-arg\\$/ARG ${dep_name}_IMAGE_TAG\\n&/g\" \\\n  -e \"s/^#gen:dep-from\\$/FROM \\\\\\$${dep_name}_IMAGE_TAG as build-${dep_name}\\n&/g\" \\\n  -e \"s/^#gen:dep-copy\\$/COPY --from=build-${dep_name} \\\\\\$HOME\\/install \\\\\\$HOME\\/install\\n&/g\" \\\n  final/Dockerfile\ngit add final/Dockerfile\n\ngit submodule add \"$dep_repo\" \"$dep_name/$dep_name\"\n\ngit commit -m \"adding dependency $dep_name\"\n\necho\necho \"ACTION REQUIRED:\"\necho \"update file \\`$dep_name/Dockerfile\\` with proper build instructions then update commit with:\"\necho\necho \"  editor \\\"$dep_name/Dockerfile\\\" \\\\\"\necho \"    && git add \\\"$dep_name/Dockerfile\\\" \\\\\"\necho \"    && git commit --amend --no-edit\"\n"
  },
  {
    "path": "build-tools/aws_sdk/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# AWS SDK\n\nARG base_IMAGE_TAG\nARG CMAKE_BUILD_TYPE\n\nFROM $base_IMAGE_TAG AS build\n\nARG NPROC\n\nWORKDIR $HOME\n\nCOPY --chown=${UID}:${GID} aws-sdk-cpp aws-sdk-cpp\n\nWORKDIR $HOME/build/aws-sdk-cpp\n\nRUN cmake \\\n  -DCUSTOM_MEMORY_MANAGEMENT=0 \\\n  -DBUILD_SHARED_LIBS=OFF \\\n  -DBUILD_ONLY=\"ec2;s3\" \\\n  -DFORCE_CURL=ON \\\n  -DUSE_OPENSSL=ON \\\n  -DENABLE_TESTING=OFF \\\n  -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \\\n  -DCMAKE_INSTALL_PREFIX:PATH=$HOME/install \\\n  $HOME/aws-sdk-cpp\n\nRUN nice make -j${NPROC:-3}\nRUN nice make install\n\n# Runtime stage - copy only necessary artifacts\nFROM $base_IMAGE_TAG\nCOPY --from=build $HOME/install $HOME/install\n"
  },
  {
    "path": "build-tools/base/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nARG BENV_BASE_IMAGE_DISTRO=debian\nARG BENV_BASE_IMAGE_VERSION=trixie@sha256:01a723bf5bfb21b9dda0c9a33e0538106e4d02cce8f557e118dd61259553d598\nFROM ${BENV_BASE_IMAGE_DISTRO}:${BENV_BASE_IMAGE_VERSION} AS build-main\n\n################ DEPENDENCIES ################\n# fixes for some of the build bugs/warnings in docker\nRUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections\n\nENV BENV_BASE_IMAGE_DISTRO=${BENV_BASE_IMAGE_DISTRO}\nENV BENV_BASE_IMAGE_VERSION=${BENV_BASE_IMAGE_VERSION}\n\n# Package definitions\nARG PKG_CORE=\"wget curl git gnupg bc aptitude netcat-openbsd sudo\"\nARG PKG_TEXT=\"xxd sed ripgrep less jq\"\nARG PKG_COMPILERS=\"g++\"\nARG PKG_BUILD=\"ninja-build\"\nARG PKG_LINTERS=\"clang-format-19 clang-tidy-19 shellcheck\"\nARG PKG_MANAGERS=\"pkg-config rpm\"\nARG PKG_KERNEL=\"dkms build-essential\"\nARG PKG_DEV=\"gdb cgdb tmux strace\"\nARG PKG_LIBS=\"libc-ares-dev libelf-dev libssl-dev libzstd-dev libgrpc-dev libcurl4-openssl-dev libabsl-dev protobuf-compiler-grpc libcurlpp-dev libgrpc++-dev libprotobuf-dev\"\nARG PKG_PY_TEST=\"python3-pytest python3-dev python3-pip python3-setuptools python3-wheel pylint\"\nARG PKG_JAVA=\"default-jdk-headless\"\nARG PKG_LLVM=\"llvm-19-dev libclang-19-dev clang-19 libpolly-19-dev\"\nARG PKG_MAKE=\"cmake ccache autoconf autoconf-archive automake libtool make\"\nARG PKG_BCC=\"bison flex zip\"\nARG PKG_LIBBPF=\"zip pkg-config libelf-dev zlib1g-dev libbfd-dev libcap-dev\"\n\n# setup apt (add non-free, contrib and backports)\nRUN [ \"$BENV_BASE_IMAGE_DISTRO\" != 'debian' ] || [ \"$BENV_BASE_IMAGE_VERSION\" != 'sid' ] \\\n|| cat > /etc/apt/sources.list << EOF \\\ndeb http://deb.debian.org/debian/ $BENV_BASE_IMAGE_VERSION main non-free contrib \\\ndeb http://deb.debian.org/debian-security/ $BENV_BASE_IMAGE_VERSION/updates main non-free contrib \\\ndeb http://deb.debian.org/debian/ $BENV_BASE_IMAGE_VERSION-updates main non-free contrib \\\ndeb http://deb.debian.org/debian/ $BENV_BASE_IMAGE_VERSION-backports main non-free contrib \\\nEOF\n\n# Update, upgrade, and install all packages\nRUN apt-get -y update && \\\n    apt-get -y install --no-install-recommends apt-utils && \\\n    apt-get upgrade -y --no-install-recommends && \\\n    apt-get -y install --no-install-recommends \\\n        $PKG_CORE \\\n        $PKG_TEXT \\\n        $PKG_COMPILERS \\\n        $PKG_BUILD \\\n        $PKG_LINTERS \\\n        $PKG_MANAGERS \\\n        $PKG_KERNEL \\\n        $PKG_DEV \\\n        $PKG_LIBS \\\n        $PKG_PY_TEST \\\n        $PKG_JAVA \\\n        $PKG_LLVM \\\n        $PKG_LIBBPF && \\\n    apt-get -y install \\\n        $PKG_MAKE \\\n        $PKG_BCC && \\\n    apt-get upgrade -y --no-install-recommends && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n################ ENVIRONMENT ################\nARG UNAME=user\nARG UID=1000\nARG GNAME=user\nARG GID=1000\nRUN set -x; \\\n    # These commands are allowed to fail (it happens for root, for example).\n    # The result will be checked in the next RUN.\n    userdel -r `getent passwd ${UID} | cut -d : -f 1` > /dev/null 2>&1; \\\n    groupdel -f `getent group ${GID} | cut -d : -f 1` > /dev/null 2>&1; \\\n    groupadd -g ${GID} ${GNAME}; \\\n    useradd -u $UID -g $GID -G sudo -ms /bin/bash ${UNAME}; \\\n    echo \"${UNAME} ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers\n\nUSER ${UNAME}:${GNAME}\nENV HOME=/home/${UNAME}\nWORKDIR $HOME\n\nRUN set -ex; \\\n    id | grep \"uid=${UID}(${UNAME}) gid=${GID}(${GNAME})\" || (echo \"ERROR: User ID verification failed\" && exit 1); \\\n    sudo ls || (echo \"ERROR: sudo test failed\" && exit 1); \\\n    pwd | grep \"^/home/${UNAME}\" || (echo \"ERROR: Working directory verification failed\" && exit 1); \\\n    echo $HOME | grep \"^/home/${UNAME}\" || (echo \"ERROR: HOME directory verification failed\" && exit 1); \\\n    touch $HOME/test || (echo \"ERROR: File creation test failed\" && exit 1); \\\n    rm $HOME/test || (echo \"ERROR: File removal test failed\" && exit 1)\n\n# setup path in both ENV and shell profile\nENV PATH=\"$HOME/install/bin:/usr/local/go/bin:$PATH\"\nRUN echo 'export PATH=\"$HOME/install/bin:/usr/local/go/bin:$PATH\"' >> $HOME/.profile\n\n# set UID, GID\nENV UID=${UID}\nENV GID=${GID}\n# note: we do not export UNAME as it interferes with LZ4 builds\n\n"
  },
  {
    "path": "build-tools/benv/docker.d/cgroups",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --mount \"type=bind,source=/sys/fs/cgroup,destination=/sys/fs/cgroup,readonly\"\n)"
  },
  {
    "path": "build-tools/benv/docker.d/gdb",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --cap-add=SYS_PTRACE\n  --security-opt seccomp=unconfined\n)\n"
  },
  {
    "path": "build-tools/benv/docker.d/kernel-headers",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`,destination=/lib/modules/`uname --kernel-release`,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/build,destination=/lib/modules/`uname --kernel-release`/build,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/build/scripts,destination=/lib/modules/`uname --kernel-release`/build/scripts,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/build/tools,destination=/lib/modules/`uname --kernel-release`/build/tools,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/source,destination=/lib/modules/`uname --kernel-release`/source,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/source/scripts,destination=/lib/modules/`uname --kernel-release`/source/scripts,readonly\"\n  --mount \"type=bind,source=/lib/modules/`uname --kernel-release`/source/tools,destination=/lib/modules/`uname --kernel-release`/source/tools,readonly\"\n)\n"
  },
  {
    "path": "build-tools/benv/docker.d/pid-host",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --pid=host\n)\n"
  },
  {
    "path": "build-tools/benv/docker.d/privileged",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --privileged\n)\n"
  },
  {
    "path": "build-tools/benv/docker.d/vimrc",
    "content": "#!/bin/bash\n\ndocker_args+=(\n  --mount \"type=bind,source=$HOME/.vim,destination=/root/.vim,readonly\"\n  --mount \"type=bind,source=$HOME/.vimrc,destination=/root/.vimrc,readonly\"\n)\n"
  },
  {
    "path": "build-tools/build.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Builds the build environment -- a container image which is then used\n# to build the project in the main repo.\n\n# Call with `VERBOSE=1` for verbose output\n# e.g.: `./build.sh VERBOSE=1`\n\n# use env variables BENV_BASE_IMAGE_DISTRO and BENV_BASE_IMAGE_VERSION\n# to customize the base image used to build benv:\n#\n#   BENV_BASE_IMAGE_DISTRO=debian BENV_BASE_IMAGE_VERSION=testing ./build.sh\n\n# use command line argument -DBENV_UNMINIMIZE=ON to unminimize the benv image\n\n# Note: the `--jobs` flag requires git 2.8.0 or later\n\n# Call with 'debug' to build a debug version of the build-env\n\nif [[ \"$1\" == \"--help\" ]]; then\n  echo \"usage: $0 [{--help | debug}]\"\n  echo\n  echo \"  --help: shows this help message\"\n  echo \"  debug: builds benv with debug builds of 3rd party libraries\"\n  exit 0\nfi\n\nset -x\n\nnproc=\"$(./nproc.sh)\"\ngit submodule update --init --recursive --jobs \"${nproc}\"\n\n# If this is a debug build set some extra flags to rename the benv image\nif [[ \"$1\" == \"debug\" ]]; then\n  # Debug build\n  echo Enabling debug build\n  EXTRA_CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Debug\n  export DOCKER_TAG_PREFIX=debug-\n  shift\n  mkdir -p Debug\n  cd Debug\n  CMAKE_SOURCE_DIR=..\nelse\n  # Release build\n  echo Enabling release build\n  EXTRA_CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Release\n  mkdir -p Release\n  cd Release\n  CMAKE_SOURCE_DIR=..\nfi\n\ncmake $EXTRA_CMAKE_OPTIONS \"$@\" $CMAKE_SOURCE_DIR\n\nif [[ \"$1\" == \"--cmake-only\" ]]; then\n  echo \"==============================================\"\n  echo \"cmake completed - skipping make and docker tag\"\n  echo \"==============================================\"\n  exit 0\nfi\n\nmake\n\nif [[ -n \"${BENV_BASE_IMAGE_DISTRO}\" ]] && [[ -n \"${BENV_BASE_IMAGE_VERSION}\" ]]; then\n  echo docker tag ${DOCKER_TAG_PREFIX}build-env:latest \"${DOCKER_TAG_PREFIX}build-env:${BENV_BASE_IMAGE_DISTRO}-${BENV_BASE_IMAGE_VERSION}\"\n  docker tag ${DOCKER_TAG_PREFIX}build-env:latest \"${DOCKER_TAG_PREFIX}build-env:${BENV_BASE_IMAGE_DISTRO}-${BENV_BASE_IMAGE_VERSION}\"\nfi\n"
  },
  {
    "path": "build-tools/build_directory.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# This script gets the latest git modification hash for a specific directory (DIR=$1).\n# If docker doesn't have an image named ${BENV_PREFIX}-${DIR}:${VERSION_HASH},\n#  builds that image in DIR.\n\nSCRIPTDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nDIR=\"$1\"\nshift 1\n\nIMAGE_TAG=$(${SCRIPTDIR}/get_tag.sh ${DIR})\nEXISTING=$(docker images --filter \"reference=${IMAGE_TAG}\" -q)\n\n# if the docker image already exists, we're done\nif [ \"${EXISTING}\" != \"\" ]\nthen\n    echo ${IMAGE_TAG}: exists\n    exit 0\nfi\n\necho ${IMAGE_TAG}: does not exist, building\ndocker build ${DIR} -t ${IMAGE_TAG} $@\n"
  },
  {
    "path": "build-tools/check_missing.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# For a docker image directory DIR ($1), checks if that version is not in docker. If so,\n# touches FILENAME ($2). If FILENAME itself is missing, creates it.\n\nSCRIPTDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nDIR=\"$1\"\nFILENAME=\"$2\"\nIMAGE_TAG=$(${SCRIPTDIR}/get_tag.sh ${DIR})\nEXISTING=$(docker images --filter \"reference=${IMAGE_TAG}\" -q)\n\n# if the docker image doesn't exists, touch the file\nif [ \"${EXISTING}\" == \"\" ]\nthen\n    echo \"No existing image ${IMAGE_TAG}. Touching ${FILENAME}.\"\n    touch \"${FILENAME}\"\n    exit 0\nfi\n\n# if `missing` is itself not on the filesystem, create it\n# does the version file exist\nif [ ! -f \"${FILENAME}\" ]\nthen\n    echo \"File ${FILENAME} does not exist when checking for existing image ${IMAGE_TAG}. Touching. $(pwd)\"\n\n    # doesn't exist, just create it.\n    touch \"${FILENAME}\"\n\n    exit 0\nfi\n\n#echo \"${IMAGE_TAG} and ${FILENAME} exist. No change.\"\n"
  },
  {
    "path": "build-tools/cpp_misc/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# various c++ libraries:\n# - LZ4\n# - yaml-cpp\n# - args\n# - nlohmann_json\n# - spdlog\n# - ccan\n# - googletest\n\nARG base_IMAGE_TAG\nFROM $base_IMAGE_TAG AS build\n\nARG CMAKE_BUILD_TYPE\nARG BUILD_CFLAGS\nARG NPROC\n\n# LZ4\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} lz4 lz4\nWORKDIR $HOME/lz4\nRUN make prefix=$HOME/install install\n\n# yaml-cpp\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} yaml-cpp yaml-cpp\nWORKDIR $HOME/build/yaml-cpp\nRUN cmake \\\n    -G Ninja \\\n    -DCMAKE_INSTALL_PREFIX:PATH=$HOME/install \\\n    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \\\n\t$HOME/yaml-cpp\nRUN nice ninja && ninja install\n\n# args\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} args args\nWORKDIR $HOME/args\nRUN make DESTDIR=$HOME/install install\n\n# nlohmann_json\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} json json\nWORKDIR $HOME/json\nRUN cmake \\\n  \"-DCMAKE_INSTALL_PREFIX=$HOME/install\" \\\n  -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \\\n  -DJSON_BuildTests=OFF \\\n  .\nRUN nice cmake --build . --target install -j ${NPROC:-3} --config $CMAKE_BUILD_TYPE\n\n# spdlog\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} spdlog spdlog\nWORKDIR $HOME/spdlog\nRUN cmake \\\n  \"-DCMAKE_INSTALL_PREFIX=$HOME/install\" \\\n  -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \\\n  -DSPDLOG_BUILD_BENCH=OFF \\\n  -DSPDLOG_BUILD_EXAMPLES=OFF \\\n  -DSPDLOG_BUILD_TESTING=OFF \\\n  .\nRUN nice cmake --build . --target install -j ${NPROC:-3} --config $CMAKE_BUILD_TYPE\n\n# ccan\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} ccan ccan\nRUN tar -cf- ccan/**/*.h | tar -xvf- -C $HOME/install/include\n\n# googletest\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} googletest googletest\nWORKDIR $HOME/googletest\nRUN cmake \\\n  \"-DCMAKE_INSTALL_PREFIX=$HOME/install\" \\\n  -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \\\n  -DBUILD_GMOCK=ON \\\n  -DINSTALL_GTEST=OFF \\\n  .\nRUN nice cmake --build . -j ${NPROC:-3} --config $CMAKE_BUILD_TYPE\nRUN cp lib/*.a $HOME/install/lib\nRUN cp -R googletest/include/gtest $HOME/install/include\nRUN cp -R googlemock/include/gmock $HOME/install/include\n\n# Runtime stage - copy only necessary artifacts\nFROM $base_IMAGE_TAG\nCOPY --from=build $HOME/install $HOME/install\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/_info",
    "content": "#include <stdio.h>\n#include <string.h>\n#include \"config.h\"\n\n/**\n * build_assert - routines for build-time assertions\n *\n * This code provides routines which will cause compilation to fail should some\n * assertion be untrue: such failures are preferable to run-time assertions,\n * but much more limited since they can only depends on compile-time constants.\n *\n * These assertions are most useful when two parts of the code must be kept in\n * sync: it is better to avoid such cases if possible, but seconds best is to\n * detect invalid changes at build time.\n *\n * For example, a tricky piece of code might rely on a certain element being at\n * the start of the structure.  To ensure that future changes don't break it,\n * you would catch such changes in your code like so:\n *\n * Example:\n *\t#include <stddef.h>\n *\t#include <ccan/build_assert/build_assert.h>\n *\n *\tstruct foo {\n *\t\tchar string[5];\n *\t\tint x;\n *\t};\n *\n *\tstatic char *foo_string(struct foo *foo)\n *\t{\n *\t\t// This trick requires that the string be first in the structure\n *\t\tBUILD_ASSERT(offsetof(struct foo, string) == 0);\n *\t\treturn (char *)foo;\n *\t}\n *\n * License: CC0 (Public domain)\n * Author: Rusty Russell <rusty@rustcorp.com.au>\n */\nint main(int argc, char *argv[])\n{\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0)\n\t\t/* Nothing. */\n\t\treturn 0;\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/build_assert.h",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n#ifndef CCAN_BUILD_ASSERT_H\n#define CCAN_BUILD_ASSERT_H\n\n/**\n * BUILD_ASSERT - assert a build-time dependency.\n * @cond: the compile-time condition which must be true.\n *\n * Your compile will fail if the condition isn't true, or can't be evaluated\n * by the compiler.  This can only be used within a function.\n *\n * Example:\n *\t#include <stddef.h>\n *\t...\n *\tstatic char *foo_to_char(struct foo *foo)\n *\t{\n *\t\t// This code needs string to be at start of foo.\n *\t\tBUILD_ASSERT(offsetof(struct foo, string) == 0);\n *\t\treturn (char *)foo;\n *\t}\n */\n#define BUILD_ASSERT(cond) \\\n\tdo { (void) sizeof(char [1 - 2*!(cond)]); } while(0)\n\n/**\n * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.\n * @cond: the compile-time condition which must be true.\n *\n * Your compile will fail if the condition isn't true, or can't be evaluated\n * by the compiler.  This can be used in an expression: its value is \"0\".\n *\n * Example:\n *\t#define foo_to_char(foo)\t\t\t\t\t\\\n *\t\t ((char *)(foo)\t\t\t\t\t\t\\\n *\t\t  + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))\n */\n#define BUILD_ASSERT_OR_ZERO(cond) \\\n\t(sizeof(char [1 - 2*!(cond)]) - 1)\n\n#endif /* CCAN_BUILD_ASSERT_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/test/compile_fail-expr.c",
    "content": "#include <ccan/build_assert/build_assert.h>\n\nint main(int argc, char *argv[])\n{\n#ifdef FAIL\n\treturn BUILD_ASSERT_OR_ZERO(1 == 0);\n#else\n\treturn 0;\n#endif\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/test/compile_fail.c",
    "content": "#include <ccan/build_assert/build_assert.h>\n\nint main(int argc, char *argv[])\n{\n#ifdef FAIL\n\tBUILD_ASSERT(1 == 0);\n#endif\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/test/compile_ok.c",
    "content": "#include <ccan/build_assert/build_assert.h>\n\nint main(int argc, char *argv[])\n{\n\tBUILD_ASSERT(1 == 1);\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c",
    "content": "#include <ccan/build_assert/build_assert.h>\n#include <ccan/tap/tap.h>\n\nint main(int argc, char *argv[])\n{\n\tplan_tests(1);\n\tok1(BUILD_ASSERT_OR_ZERO(1 == 1) == 0);\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/_info",
    "content": "#include <stdio.h>\n#include <string.h>\n#include \"config.h\"\n\n/**\n * check_type - routines for compile time type checking\n *\n * C has fairly weak typing: ints get automatically converted to longs, signed\n * to unsigned, etc.  There are some cases where this is best avoided, and\n * these macros provide methods for evoking warnings (or build errors) when\n * a precise type isn't used.\n *\n * On compilers which don't support typeof() these routines are less effective,\n * since they have to use sizeof() which can only distiguish between types of\n * different size.\n *\n * License: CC0 (Public domain)\n * Author: Rusty Russell <rusty@rustcorp.com.au>\n */\nint main(int argc, char *argv[])\n{\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0) {\n#if !HAVE_TYPEOF\n\t\tprintf(\"ccan/build_assert\\n\");\n#endif\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/check_type.h",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n#ifndef CCAN_CHECK_TYPE_H\n#define CCAN_CHECK_TYPE_H\n\n#define HAVE_TYPEOF 1\n\n/**\n * check_type - issue a warning or build failure if type is not correct.\n * @expr: the expression whose type we should check (not evaluated).\n * @type: the exact type we expect the expression to be.\n *\n * This macro is usually used within other macros to try to ensure that a macro\n * argument is of the expected type.  No type promotion of the expression is\n * done: an unsigned int is not the same as an int!\n *\n * check_type() always evaluates to 0.\n *\n * If your compiler does not support typeof, then the best we can do is fail\n * to compile if the sizes of the types are unequal (a less complete check).\n *\n * Example:\n *\t// They should always pass a 64-bit value to _set_some_value!\n *\t#define set_some_value(expr)\t\t\t\\\n *\t\t_set_some_value((check_type((expr), uint64_t), (expr)))\n */\n\n/**\n * check_types_match - issue a warning or build failure if types are not same.\n * @expr1: the first expression (not evaluated).\n * @expr2: the second expression (not evaluated).\n *\n * This macro is usually used within other macros to try to ensure that\n * arguments are of identical types.  No type promotion of the expressions is\n * done: an unsigned int is not the same as an int!\n *\n * check_types_match() always evaluates to 0.\n *\n * If your compiler does not support typeof, then the best we can do is fail\n * to compile if the sizes of the types are unequal (a less complete check).\n *\n * Example:\n *\t// Do subtraction to get to enclosing type, but make sure that\n *\t// pointer is of correct type for that member.\n *\t#define container_of(mbr_ptr, encl_type, mbr)\t\t\t\\\n *\t\t(check_types_match((mbr_ptr), &((encl_type *)0)->mbr),\t\\\n *\t\t ((encl_type *)\t\t\t\t\t\t\\\n *\t\t  ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))\n */\n#if HAVE_TYPEOF\n#define check_type(expr, type)\t\t\t\\\n\t((typeof(expr) *)0 != (type *)0)\n\n#define check_types_match(expr1, expr2)\t\t\\\n\t((typeof(expr1) *)0 != (typeof(expr2) *)0)\n#else\n#include <ccan/build_assert/build_assert.h>\n/* Without typeof, we can only test the sizes. */\n#define check_type(expr, type)\t\t\t\t\t\\\n\tBUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type))\n\n#define check_types_match(expr1, expr2)\t\t\t\t\\\n\tBUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2))\n#endif /* HAVE_TYPEOF */\n\n#endif /* CCAN_CHECK_TYPE_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/test/compile_fail-check_type.c",
    "content": "#include <ccan/check_type/check_type.h>\n\nint main(int argc, char *argv[])\n{\n#ifdef FAIL\n\tcheck_type(argc, char);\n#endif\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/test/compile_fail-check_type_unsigned.c",
    "content": "#include <ccan/check_type/check_type.h>\n\nint main(int argc, char *argv[])\n{\n#ifdef FAIL\n#if HAVE_TYPEOF\n\tcheck_type(argc, unsigned int);\n#else\n\t/* This doesn't work without typeof, so just fail */\n#error \"Fail without typeof\"\n#endif\n#endif\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/test/compile_fail-check_types_match.c",
    "content": "#include <ccan/check_type/check_type.h>\n\nint main(int argc, char *argv[])\n{\n\tunsigned char x = argc;\n#ifdef FAIL\n\tcheck_types_match(argc, x);\n#endif\n\treturn x;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/check_type/test/run.c",
    "content": "#include <ccan/check_type/check_type.h>\n#include <ccan/tap/tap.h>\n\nint main(int argc, char *argv[])\n{\n\tint x = 0, y = 0;\n\n\tplan_tests(9);\n\n\tok1(check_type(argc, int) == 0);\n\tok1(check_type(&argc, int *) == 0);\n\tok1(check_types_match(argc, argc) == 0);\n\tok1(check_types_match(argc, x) == 0);\n\tok1(check_types_match(&argc, &x) == 0);\n\n\tok1(check_type(x++, int) == 0);\n\tok(x == 0, \"check_type does not evaluate expression\");\n\tok1(check_types_match(x++, y++) == 0);\n\tok(x == 0 && y == 0, \"check_types_match does not evaluate expressions\");\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/compiler/_info",
    "content": "#include <string.h>\n#include <stdio.h>\n#include \"config.h\"\n\n/**\n * compiler - macros for common compiler extensions\n *\n * Abstracts away some compiler hints.  Currently these include:\n * - COLD\n *\tFor functions not called in fast paths (aka. cold functions)\n * - PRINTF_FMT\n *\tFor functions which take printf-style parameters.\n * - CONST_FUNCTION\n *\tFor functions which return the same value for same parameters.\n * - NEEDED\n *\tFor functions and variables which must be emitted even if unused.\n * - UNNEEDED\n *\tFor functions and variables which need not be emitted if unused.\n * - UNUSED\n *\tFor parameters which are not used.\n * - IS_COMPILE_CONSTANT()\n *\tFor using different tradeoffs for compiletime vs runtime evaluation.\n *\n * License: CC0 (Public domain)\n * Author: Rusty Russell <rusty@rustcorp.com.au>\n *\n * Example:\n *\t#include <ccan/compiler/compiler.h>\n *\t#include <stdio.h>\n *\t#include <stdarg.h>\n *\n *\t// Example of a (slow-path) logging function.\n *\tstatic int log_threshold = 2;\n *\tstatic void COLD PRINTF_FMT(2,3)\n *\t\tlogger(int level, const char *fmt, ...)\n *\t{\n *\t\tva_list ap;\n *\t\tva_start(ap, fmt);\n *\t\tif (level >= log_threshold)\n *\t\t\tvfprintf(stderr, fmt, ap);\n *\t\tva_end(ap);\n *\t}\n *\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tif (argc != 1) {\n *\t\t\tlogger(3, \"Don't want %i arguments!\\n\", argc-1);\n *\t\t\treturn 1;\n *\t\t}\n *\t\treturn 0;\n *\t}\n */\nint main(int argc, char *argv[])\n{\n\t/* Expect exactly one argument */\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/compiler/compiler.h",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n#ifndef CCAN_COMPILER_H\n#define CCAN_COMPILER_H\n\n#ifndef COLD\n#if HAVE_ATTRIBUTE_COLD\n/**\n * COLD - a function is unlikely to be called.\n *\n * Used to mark an unlikely code path and optimize appropriately.\n * It is usually used on logging or error routines.\n *\n * Example:\n * static void COLD moan(const char *reason)\n * {\n *\tfprintf(stderr, \"Error: %s (%s)\\n\", reason, strerror(errno));\n * }\n */\n#define COLD __attribute__((cold))\n#else\n#define COLD\n#endif\n#endif\n\n#ifndef NORETURN\n#if HAVE_ATTRIBUTE_NORETURN\n/**\n * NORETURN - a function does not return\n *\n * Used to mark a function which exits; useful for suppressing warnings.\n *\n * Example:\n * static void NORETURN fail(const char *reason)\n * {\n *\tfprintf(stderr, \"Error: %s (%s)\\n\", reason, strerror(errno));\n *\texit(1);\n * }\n */\n#define NORETURN __attribute__((noreturn))\n#else\n#define NORETURN\n#endif\n#endif\n\n#ifndef PRINTF_FMT\n#if HAVE_ATTRIBUTE_PRINTF\n/**\n * PRINTF_FMT - a function takes printf-style arguments\n * @nfmt: the 1-based number of the function's format argument.\n * @narg: the 1-based number of the function's first variable argument.\n *\n * This allows the compiler to check your parameters as it does for printf().\n *\n * Example:\n * void PRINTF_FMT(2,3) my_printf(const char *prefix, const char *fmt, ...);\n */\n#define PRINTF_FMT(nfmt, narg) \\\n\t__attribute__((format(__printf__, nfmt, narg)))\n#else\n#define PRINTF_FMT(nfmt, narg)\n#endif\n#endif\n\n#ifndef CONST_FUNCTION\n#if HAVE_ATTRIBUTE_CONST\n/**\n * CONST_FUNCTION - a function's return depends only on its argument\n *\n * This allows the compiler to assume that the function will return the exact\n * same value for the exact same arguments.  This implies that the function\n * must not use global variables, or dereference pointer arguments.\n */\n#define CONST_FUNCTION __attribute__((const))\n#else\n#define CONST_FUNCTION\n#endif\n#endif\n\n#if HAVE_ATTRIBUTE_UNUSED\n#ifndef UNNEEDED\n/**\n * UNNEEDED - a variable/function may not be needed\n *\n * This suppresses warnings about unused variables or functions, but tells\n * the compiler that if it is unused it need not emit it into the source code.\n *\n * Example:\n * // With some preprocessor options, this is unnecessary.\n * static UNNEEDED int counter;\n *\n * // With some preprocessor options, this is unnecessary.\n * static UNNEEDED void add_to_counter(int add)\n * {\n *\tcounter += add;\n * }\n */\n#define UNNEEDED __attribute__((unused))\n#endif\n\n#ifndef NEEDED\n#if HAVE_ATTRIBUTE_USED\n/**\n * NEEDED - a variable/function is needed\n *\n * This suppresses warnings about unused variables or functions, but tells\n * the compiler that it must exist even if it (seems) unused.\n *\n * Example:\n *\t// Even if this is unused, these are vital for debugging.\n *\tstatic NEEDED int counter;\n *\tstatic NEEDED void dump_counter(void)\n *\t{\n *\t\tprintf(\"Counter is %i\\n\", counter);\n *\t}\n */\n#define NEEDED __attribute__((used))\n#else\n/* Before used, unused functions and vars were always emitted. */\n#define NEEDED __attribute__((unused))\n#endif\n#endif\n\n#ifndef UNUSED\n/**\n * UNUSED - a parameter is unused\n *\n * Some compilers (eg. gcc with -W or -Wunused) warn about unused\n * function parameters.  This suppresses such warnings and indicates\n * to the reader that it's deliberate.\n *\n * Example:\n *\t// This is used as a callback, so needs to have this prototype.\n *\tstatic int some_callback(void *unused UNUSED)\n *\t{\n *\t\treturn 0;\n *\t}\n */\n#define UNUSED __attribute__((unused))\n#endif\n#else\n#ifndef UNNEEDED\n#define UNNEEDED\n#endif\n#ifndef NEEDED\n#define NEEDED\n#endif\n#ifndef UNUSED\n#define UNUSED\n#endif\n#endif\n\n#ifndef IS_COMPILE_CONSTANT\n#if HAVE_BUILTIN_CONSTANT_P\n/**\n * IS_COMPILE_CONSTANT - does the compiler know the value of this expression?\n * @expr: the expression to evaluate\n *\n * When an expression manipulation is complicated, it is usually better to\n * implement it in a function.  However, if the expression being manipulated is\n * known at compile time, it is better to have the compiler see the entire\n * expression so it can simply substitute the result.\n *\n * This can be done using the IS_COMPILE_CONSTANT() macro.\n *\n * Example:\n *\tenum greek { ALPHA, BETA, GAMMA, DELTA, EPSILON };\n *\n *\t// Out-of-line version.\n *\tconst char *greek_name(enum greek greek);\n *\n *\t// Inline version.\n *\tstatic inline const char *_greek_name(enum greek greek)\n *\t{\n *\t\tswitch (greek) {\n *\t\tcase ALPHA: return \"alpha\";\n *\t\tcase BETA: return \"beta\";\n *\t\tcase GAMMA: return \"gamma\";\n *\t\tcase DELTA: return \"delta\";\n *\t\tcase EPSILON: return \"epsilon\";\n *\t\tdefault: return \"**INVALID**\";\n *\t\t}\n *\t}\n *\n *\t// Use inline if compiler knows answer.  Otherwise call function\n *\t// to avoid copies of the same code everywhere.\n *\t#define greek_name(g)\t\t\t\t\t\t\\\n *\t\t (IS_COMPILE_CONSTANT(greek) ? _greek_name(g) : greek_name(g))\n */\n#define IS_COMPILE_CONSTANT(expr) __builtin_constant_p(expr)\n#else\n/* If we don't know, assume it's not. */\n#define IS_COMPILE_CONSTANT(expr) 0\n#endif\n#endif\n\n#ifndef WARN_UNUSED_RESULT\n#if HAVE_WARN_UNUSED_RESULT\n/**\n * WARN_UNUSED_RESULT - warn if a function return value is unused.\n *\n * Used to mark a function where it is extremely unlikely that the caller\n * can ignore the result, eg realloc().\n *\n * Example:\n * // buf param may be freed by this; need return value!\n * static char *WARN_UNUSED_RESULT enlarge(char *buf, unsigned *size)\n * {\n *\treturn realloc(buf, (*size) *= 2);\n * }\n */\n#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n#else\n#define WARN_UNUSED_RESULT\n#endif\n#endif\n#endif /* CCAN_COMPILER_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/compiler/test/compile_fail-printf.c",
    "content": "#include <ccan/compiler/compiler.h>\n\nstatic void PRINTF_FMT(2,3) my_printf(int x, const char *fmt, ...)\n{\n}\n\nint main(int argc, char *argv[])\n{\n\tunsigned int i = 0;\n\n\tmy_printf(1, \"Not a pointer \"\n#ifdef FAIL\n\t\t  \"%p\",\n#if !HAVE_ATTRIBUTE_PRINTF\n#error \"Unfortunately we don't fail if !HAVE_ATTRIBUTE_PRINTF.\"\n#endif\n#else\n\t\t  \"%i\",\n#endif\n\t\t  i);\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/compiler/test/run-is_compile_constant.c",
    "content": "#include <ccan/compiler/compiler.h>\n#include <ccan/tap/tap.h>\n\nint main(int argc, char *argv[])\n{\n\tplan_tests(2);\n\n\tok1(!IS_COMPILE_CONSTANT(argc));\n#if HAVE_BUILTIN_CONSTANT_P\n\tok1(IS_COMPILE_CONSTANT(7));\n#else\n\tpass(\"If !HAVE_BUILTIN_CONSTANT_P, IS_COMPILE_CONSTANT always false\");\n#endif\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/_info",
    "content": "#include <stdio.h>\n#include <string.h>\n#include \"config.h\"\n\n/**\n * container_of - routine for upcasting\n *\n * It is often convenient to create code where the caller registers a pointer\n * to a generic structure and a callback.  The callback might know that the\n * pointer points to within a larger structure, and container_of gives a\n * convenient and fairly type-safe way of returning to the enclosing structure.\n *\n * This idiom is an alternative to providing a void * pointer for every\n * callback.\n *\n * Example:\n *\t#include <stdio.h>\n *\t#include <ccan/container_of/container_of.h>\n *\n *\tstruct timer {\n *\t\tvoid *members;\n *\t};\n *\n *\tstruct info {\n *\t\tint my_stuff;\n *\t\tstruct timer timer;\n *\t};\n *\n *\tstatic void register_timer(struct timer *timer)\n *\t{\n *\t\t//...\n *\t}\n *\n *\tstatic void my_timer_callback(struct timer *timer)\n *\t{\n *\t\tstruct info *info = container_of(timer, struct info, timer);\n *\t\tprintf(\"my_stuff is %u\\n\", info->my_stuff);\n *\t}\n *\n *\tint main(void)\n *\t{\n *\t\tstruct info info = { .my_stuff = 1 };\n *\n *\t\tregister_timer(&info.timer);\n *\t\t// ...\n *\t\treturn 0;\n *\t}\n *\n * License: CC0 (Public domain)\n * Author: Rusty Russell <rusty@rustcorp.com.au>\n */\nint main(int argc, char *argv[])\n{\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0) {\n\t\tprintf(\"ccan/check_type\\n\");\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/container_of.h",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n#ifndef CCAN_CONTAINER_OF_H\n#define CCAN_CONTAINER_OF_H\n#include <stddef.h>\n\n#include <ccan/check_type/check_type.h>\n\n/**\n * container_of - get pointer to enclosing structure\n * @member_ptr: pointer to the structure member\n * @containing_type: the type this member is within\n * @member: the name of this member within the structure.\n *\n * Given a pointer to a member of a structure, this macro does pointer\n * subtraction to return the pointer to the enclosing type.\n *\n * Example:\n *\tstruct foo {\n *\t\tint fielda, fieldb;\n *\t\t// ...\n *\t};\n *\tstruct info {\n *\t\tint some_other_field;\n *\t\tstruct foo my_foo;\n *\t};\n *\n *\tstatic struct info *foo_to_info(struct foo *foo)\n *\t{\n *\t\treturn container_of(foo, struct info, my_foo);\n *\t}\n */\n#define container_of(member_ptr, containing_type, member)\t\t\\\n\t ((containing_type *)\t\t\t\t\t\t\\\n\t  ((char *)(member_ptr)\t\t\t\t\t\t\\\n\t   - container_off(containing_type, member))\t\t\t\\\n\t  + check_types_match(*(member_ptr), ((containing_type *)0)->member))\n\n/**\n * container_off - get offset to enclosing structure\n * @containing_type: the type this member is within\n * @member: the name of this member within the structure.\n *\n * Given a pointer to a member of a structure, this macro does\n * typechecking and figures out the offset to the enclosing type.\n *\n * Example:\n *\tstruct foo {\n *\t\tint fielda, fieldb;\n *\t\t// ...\n *\t};\n *\tstruct info {\n *\t\tint some_other_field;\n *\t\tstruct foo my_foo;\n *\t};\n *\n *\tstatic struct info *foo_to_info(struct foo *foo)\n *\t{\n *\t\tsize_t off = container_off(struct info, my_foo);\n *\t\treturn (void *)((char *)foo - off);\n *\t}\n */\n#define container_off(containing_type, member)\t\\\n\toffsetof(containing_type, member)\n\n/**\n * container_of_var - get pointer to enclosing structure using a variable\n * @member_ptr: pointer to the structure member\n * @container_var: a pointer of same type as this member's container\n * @member: the name of this member within the structure.\n *\n * Given a pointer to a member of a structure, this macro does pointer\n * subtraction to return the pointer to the enclosing type.\n *\n * Example:\n *\tstatic struct info *foo_to_i(struct foo *foo)\n *\t{\n *\t\tstruct info *i = container_of_var(foo, i, my_foo);\n *\t\treturn i;\n *\t}\n */\n#if HAVE_TYPEOF\n#define container_of_var(member_ptr, container_var, member) \\\n\tcontainer_of(member_ptr, typeof(*container_var), member)\n#else\n#define container_of_var(member_ptr, container_var, member)\t\\\n\t((void *)((char *)(member_ptr)\t-\t\t\t\\\n\t\t  container_off_var(container_var, member)))\n#endif\n\n/**\n * container_off_var - get offset of a field in enclosing structure\n * @container_var: a pointer to a container structure\n * @member: the name of a member within the structure.\n *\n * Given (any) pointer to a structure and a its member name, this\n * macro does pointer subtraction to return offset of member in a\n * structure memory layout.\n *\n */\n#if HAVE_TYPEOF\n#define container_off_var(var, member)\t\t\\\n\tcontainer_off(typeof(*var), member)\n#else\n#define container_off_var(var, member)\t\t\t\\\n\t((char *)&(var)->member - (char *)(var))\n#endif\n\n#endif /* CCAN_CONTAINER_OF_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/test/compile_fail-bad-type.c",
    "content": "#include <ccan/container_of/container_of.h>\n#include <stdlib.h>\n\nstruct foo {\n\tint a;\n\tchar b;\n};\n\nint main(int argc, char *argv[])\n{\n\tstruct foo foo = { .a = 1, .b = 2 };\n\tint *intp = &foo.a;\n\tchar *p;\n\n#ifdef FAIL\n\t/* p is a char *, but this gives a struct foo * */\n\tp = container_of(intp, struct foo, a);\n#else\n\tp = (char *)intp;\n#endif\n\treturn p == NULL;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/test/compile_fail-types.c",
    "content": "#include <ccan/container_of/container_of.h>\n#include <stdlib.h>\n\nstruct foo {\n\tint a;\n\tchar b;\n};\n\nint main(int argc, char *argv[])\n{\n\tstruct foo foo = { .a = 1, .b = 2 }, *foop;\n\tint *intp = &foo.a;\n\n#ifdef FAIL\n\t/* b is a char, but intp is an int * */\n\tfoop = container_of(intp, struct foo, b);\n#else\n\tfoop = NULL;\n#endif\n\t(void) foop; /* Suppress unused-but-set-variable warning. */\n\treturn intp == NULL;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/test/compile_fail-var-types.c",
    "content": "#include <ccan/container_of/container_of.h>\n#include <stdlib.h>\n\nstruct foo {\n\tint a;\n\tchar b;\n};\n\nint main(int argc, char *argv[])\n{\n\tstruct foo foo = { .a = 1, .b = 2 }, *foop;\n\tint *intp = &foo.a;\n\n#ifdef FAIL\n\t/* b is a char, but intp is an int * */\n\tfoop = container_of_var(intp, foop, b);\n#if !HAVE_TYPEOF\n#error \"Unfortunately we don't fail if we don't have typeof.\"\n#endif\n#else\n\tfoop = NULL;\n#endif\n\t(void) foop; /* Suppress unused-but-set-variable warning. */\n\treturn intp == NULL;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/container_of/test/run.c",
    "content": "#include <ccan/container_of/container_of.h>\n#include <ccan/tap/tap.h>\n\nstruct foo {\n\tint a;\n\tchar b;\n};\n\nint main(int argc, char *argv[])\n{\n\tstruct foo foo = { .a = 1, .b = 2 };\n\tint *intp = &foo.a;\n\tchar *charp = &foo.b;\n\n\tplan_tests(8);\n\tok1(container_of(intp, struct foo, a) == &foo);\n\tok1(container_of(charp, struct foo, b) == &foo);\n\tok1(container_of_var(intp, &foo, a) == &foo);\n\tok1(container_of_var(charp, &foo, b) == &foo);\n\n\tok1(container_off(struct foo, a) == 0);\n\tok1(container_off(struct foo, b) == offsetof(struct foo, b));\n\tok1(container_off_var(&foo, a) == 0);\n\tok1(container_off_var(&foo, b) == offsetof(struct foo, b));\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/hash/_info",
    "content": "#include <string.h>\n#include <stdio.h>\n\n/**\n * hash - routines for hashing bytes\n *\n * When creating a hash table it's important to have a hash function\n * which mixes well and is fast.  This package supplies such functions.\n *\n * The hash functions come in two flavors: the normal ones and the\n * stable ones.  The normal ones can vary from machine-to-machine and\n * may change if we find better or faster hash algorithms in future.\n * The stable ones will always give the same results on any computer,\n * and on any version of this package.\n *\n * License: CC0 (Public domain)\n * Maintainer: Rusty Russell <rusty@rustcorp.com.au>\n * Author: Bob Jenkins <bob_jenkins@burtleburtle.net>\n */\nint main(int argc, char *argv[])\n{\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0) {\n\t\tprintf(\"ccan/build_assert\\n\");\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/hash/hash.c",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n/*\n-------------------------------------------------------------------------------\nlookup3.c, by Bob Jenkins, May 2006, Public Domain.\n\nThese are functions for producing 32-bit hashes for hash table lookup.\nhash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() \nare externally useful functions.  Routines to test the hash are included \nif SELF_TEST is defined.  You can use this free for any purpose.  It's in\nthe public domain.  It has no warranty.\n\nYou probably want to use hashlittle().  hashlittle() and hashbig()\nhash byte arrays.  hashlittle() is is faster than hashbig() on\nlittle-endian machines.  Intel and AMD are little-endian machines.\nOn second thought, you probably want hashlittle2(), which is identical to\nhashlittle() except it returns two 32-bit hashes for the price of one.  \nYou could implement hashbig2() if you wanted but I haven't bothered here.\n\nIf you want to find a hash of, say, exactly 7 integers, do\n  a = i1;  b = i2;  c = i3;\n  mix(a,b,c);\n  a += i4; b += i5; c += i6;\n  mix(a,b,c);\n  a += i7;\n  final(a,b,c);\nthen use c as the hash value.  If you have a variable length array of\n4-byte integers to hash, use hash_word().  If you have a byte array (like\na character string), use hashlittle().  If you have several byte arrays, or\na mix of things, see the comments above hashlittle().  \n\nWhy is this so big?  I read 12 bytes at a time into 3 4-byte integers, \nthen mix those integers.  This is fast (you can do a lot more thorough\nmixing with 12*3 instructions on 3 integers than you can with 3 instructions\non 1 byte), but shoehorning those bytes into integers efficiently is messy.\n-------------------------------------------------------------------------------\n*/\n//#define SELF_TEST 1\n\n#if 0\n#include <stdio.h>      /* defines printf for tests */\n#include <time.h>       /* defines time_t for timings in the test */\n#include <stdint.h>     /* defines uint32_t etc */\n#include <sys/param.h>  /* attempt to define endianness */\n\n#ifdef linux\n# include <endian.h>    /* attempt to define endianness */\n#endif\n\n/*\n * My best guess at if you are big-endian or little-endian.  This may\n * need adjustment.\n */\n#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \\\n     __BYTE_ORDER == __LITTLE_ENDIAN) || \\\n    (defined(i386) || defined(__i386__) || defined(__i486__) || \\\n     defined(__i586__) || defined(__i686__) || defined(__x86_64) || \\\n     defined(vax) || defined(MIPSEL))\n# define HASH_LITTLE_ENDIAN 1\n# define HASH_BIG_ENDIAN 0\n#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \\\n       __BYTE_ORDER == __BIG_ENDIAN) || \\\n      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))\n# define HASH_LITTLE_ENDIAN 0\n# define HASH_BIG_ENDIAN 1\n#else\n#define HASH_LITTLE_ENDIAN 1\n#define HASH_BIG_ENDIAN 0\n#endif\n#endif /* old hash.c headers. */\n\n#include \"hash.h\"\n\n#if HAVE_LITTLE_ENDIAN\n#define HASH_LITTLE_ENDIAN 1\n#define HASH_BIG_ENDIAN 0\n#elif HAVE_BIG_ENDIAN\n#define HASH_LITTLE_ENDIAN 0\n#define HASH_BIG_ENDIAN 1\n#else\n#define HASH_LITTLE_ENDIAN 1\n#define HASH_BIG_ENDIAN 0\n#endif\n\n#define hashsize(n) ((uint32_t)1<<(n))\n#define hashmask(n) (hashsize(n)-1)\n#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))\n\n/*\n-------------------------------------------------------------------------------\nmix -- mix 3 32-bit values reversibly.\n\nThis is reversible, so any information in (a,b,c) before mix() is\nstill in (a,b,c) after mix().\n\nIf four pairs of (a,b,c) inputs are run through mix(), or through\nmix() in reverse, there are at least 32 bits of the output that\nare sometimes the same for one pair and different for another pair.\nThis was tested for:\n* pairs that differed by one bit, by two bits, in any combination\n  of top bits of (a,b,c), or in any combination of bottom bits of\n  (a,b,c).\n* \"differ\" is defined as +, -, ^, or ~^.  For + and -, I transformed\n  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as\n  is commonly produced by subtraction) look like a single 1-bit\n  difference.\n* the base values were pseudorandom, all zero but one bit set, or \n  all zero plus a counter that starts at zero.\n\nSome k values for my \"a-=c; a^=rot(c,k); c+=b;\" arrangement that\nsatisfy this are\n    4  6  8 16 19  4\n    9 15  3 18 27 15\n   14  9  3  7 17  3\nWell, \"9 15 3 18 27 15\" didn't quite get 32 bits diffing\nfor \"differ\" defined as + with a one-bit base and a two-bit delta.  I\nused http://burtleburtle.net/bob/hash/avalanche.html to choose \nthe operations, constants, and arrangements of the variables.\n\nThis does not achieve avalanche.  There are input bits of (a,b,c)\nthat fail to affect some output bits of (a,b,c), especially of a.  The\nmost thoroughly mixed value is c, but it doesn't really even achieve\navalanche in c.\n\nThis allows some parallelism.  Read-after-writes are good at doubling\nthe number of bits affected, so the goal of mixing pulls in the opposite\ndirection as the goal of parallelism.  I did what I could.  Rotates\nseem to cost as much as shifts on every machine I could lay my hands\non, and rotates are much kinder to the top and bottom bits, so I used\nrotates.\n-------------------------------------------------------------------------------\n*/\n#define mix(a,b,c) \\\n{ \\\n  a -= c;  a ^= rot(c, 4);  c += b; \\\n  b -= a;  b ^= rot(a, 6);  a += c; \\\n  c -= b;  c ^= rot(b, 8);  b += a; \\\n  a -= c;  a ^= rot(c,16);  c += b; \\\n  b -= a;  b ^= rot(a,19);  a += c; \\\n  c -= b;  c ^= rot(b, 4);  b += a; \\\n}\n\n/*\n-------------------------------------------------------------------------------\nfinal -- final mixing of 3 32-bit values (a,b,c) into c\n\nPairs of (a,b,c) values differing in only a few bits will usually\nproduce values of c that look totally different.  This was tested for\n* pairs that differed by one bit, by two bits, in any combination\n  of top bits of (a,b,c), or in any combination of bottom bits of\n  (a,b,c).\n* \"differ\" is defined as +, -, ^, or ~^.  For + and -, I transformed\n  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as\n  is commonly produced by subtraction) look like a single 1-bit\n  difference.\n* the base values were pseudorandom, all zero but one bit set, or \n  all zero plus a counter that starts at zero.\n\nThese constants passed:\n 14 11 25 16 4 14 24\n 12 14 25 16 4 14 24\nand these came close:\n  4  8 15 26 3 22 24\n 10  8 15 26 3 22 24\n 11  8 15 26 3 22 24\n-------------------------------------------------------------------------------\n*/\n#define final(a,b,c) \\\n{ \\\n  c ^= b; c -= rot(b,14); \\\n  a ^= c; a -= rot(c,11); \\\n  b ^= a; b -= rot(a,25); \\\n  c ^= b; c -= rot(b,16); \\\n  a ^= c; a -= rot(c,4);  \\\n  b ^= a; b -= rot(a,14); \\\n  c ^= b; c -= rot(b,24); \\\n}\n\n/*\n--------------------------------------------------------------------\n This works on all machines.  To be useful, it requires\n -- that the key be an array of uint32_t's, and\n -- that the length be the number of uint32_t's in the key\n\n The function hash_word() is identical to hashlittle() on little-endian\n machines, and identical to hashbig() on big-endian machines,\n except that the length has to be measured in uint32_ts rather than in\n bytes.  hashlittle() is more complicated than hash_word() only because\n hashlittle() has to dance around fitting the key bytes into registers.\n--------------------------------------------------------------------\n*/\nuint32_t hash_u32(\nconst uint32_t *k,                   /* the key, an array of uint32_t values */\nsize_t          length,               /* the length of the key, in uint32_ts */\nuint32_t        initval)         /* the previous hash, or an arbitrary value */\n{\n  uint32_t a,b,c;\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;\n\n  /*------------------------------------------------- handle most of the key */\n  while (length > 3)\n  {\n    a += k[0];\n    b += k[1];\n    c += k[2];\n    mix(a,b,c);\n    length -= 3;\n    k += 3;\n  }\n\n  /*------------------------------------------- handle the last 3 uint32_t's */\n  switch(length)                     /* all the case statements fall through */\n  { \n  case 3 : c+=k[2];\n  case 2 : b+=k[1];\n  case 1 : a+=k[0];\n    final(a,b,c);\n  case 0:     /* case 0: nothing left to add */\n    break;\n  }\n  /*------------------------------------------------------ report the result */\n  return c;\n}\n\n/*\n-------------------------------------------------------------------------------\nhashlittle() -- hash a variable-length key into a 32-bit value\n  k       : the key (the unaligned variable-length array of bytes)\n  length  : the length of the key, counting by bytes\n  val2    : IN: can be any 4-byte value OUT: second 32 bit hash.\nReturns a 32-bit value.  Every bit of the key affects every bit of\nthe return value.  Two keys differing by one or two bits will have\ntotally different hash values.  Note that the return value is better\nmixed than val2, so use that first.\n\nThe best hash table sizes are powers of 2.  There is no need to do\nmod a prime (mod is sooo slow!).  If you need less than 32 bits,\nuse a bitmask.  For example, if you need only 10 bits, do\n  h = (h & hashmask(10));\nIn which case, the hash table should have hashsize(10) elements.\n\nIf you are hashing n strings (uint8_t **)k, do it like this:\n  for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);\n\nBy Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this\ncode any way you wish, private, educational, or commercial.  It's free.\n\nUse for hash table lookup, or anything where one collision in 2^^32 is\nacceptable.  Do NOT use for cryptographic purposes.\n-------------------------------------------------------------------------------\n*/\n\nstatic uint32_t hashlittle( const void *key, size_t length, uint32_t *val2 )\n{\n  uint32_t a,b,c;                                          /* internal state */\n  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2;\n\n  u.ptr = key;\n  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {\n    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */\n    const uint8_t  *k8;\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a,b,c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /* \n     * \"k[2]&0xffffff\" actually reads beyond the end of the string, but\n     * then masks off the part it's not allowed to read.  Because the\n     * string is aligned, the masked-off tail is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticably faster for short strings (like English words).\n     *\n     * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR.\n     */\n#if 0\n    switch(length)\n    {\n    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;\n    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;\n    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;\n    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;\n    case 8 : b+=k[1]; a+=k[0]; break;\n    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;\n    case 6 : b+=k[1]&0xffff; a+=k[0]; break;\n    case 5 : b+=k[1]&0xff; a+=k[0]; break;\n    case 4 : a+=k[0]; break;\n    case 3 : a+=k[0]&0xffffff; break;\n    case 2 : a+=k[0]&0xffff; break;\n    case 1 : a+=k[0]&0xff; break;\n    case 0 : return c;              /* zero length strings require no mixing */\n    }\n\n#else /* make valgrind happy */\n\n    k8 = (const uint8_t *)k;\n    switch(length)\n    {\n    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;\n    case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */\n    case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */\n    case 9 : c+=k8[8];                   /* fall through */\n    case 8 : b+=k[1]; a+=k[0]; break;\n    case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */\n    case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */\n    case 5 : b+=k8[4];                   /* fall through */\n    case 4 : a+=k[0]; break;\n    case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */\n    case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */\n    case 1 : a+=k8[0]; break;\n    case 0 : return c;\n    }\n\n#endif /* !valgrind */\n\n  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {\n    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */\n    const uint8_t  *k8;\n\n    /*--------------- all but last block: aligned reads and different mixing */\n    while (length > 12)\n    {\n      a += k[0] + (((uint32_t)k[1])<<16);\n      b += k[2] + (((uint32_t)k[3])<<16);\n      c += k[4] + (((uint32_t)k[5])<<16);\n      mix(a,b,c);\n      length -= 12;\n      k += 6;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    k8 = (const uint8_t *)k;\n    switch(length)\n    {\n    case 12: c+=k[4]+(((uint32_t)k[5])<<16);\n             b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */\n    case 10: c+=k[4];\n             b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 9 : c+=k8[8];                      /* fall through */\n    case 8 : b+=k[2]+(((uint32_t)k[3])<<16);\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */\n    case 6 : b+=k[2];\n             a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 5 : b+=k8[4];                      /* fall through */\n    case 4 : a+=k[0]+(((uint32_t)k[1])<<16);\n             break;\n    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */\n    case 2 : a+=k[0];\n             break;\n    case 1 : a+=k8[0];\n             break;\n    case 0 : return c;                     /* zero length requires no mixing */\n    }\n\n  } else {                        /* need to read the key one byte at a time */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += k[0];\n      a += ((uint32_t)k[1])<<8;\n      a += ((uint32_t)k[2])<<16;\n      a += ((uint32_t)k[3])<<24;\n      b += k[4];\n      b += ((uint32_t)k[5])<<8;\n      b += ((uint32_t)k[6])<<16;\n      b += ((uint32_t)k[7])<<24;\n      c += k[8];\n      c += ((uint32_t)k[9])<<8;\n      c += ((uint32_t)k[10])<<16;\n      c += ((uint32_t)k[11])<<24;\n      mix(a,b,c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch(length)                   /* all the case statements fall through */\n    {\n    case 12: c+=((uint32_t)k[11])<<24;\n    case 11: c+=((uint32_t)k[10])<<16;\n    case 10: c+=((uint32_t)k[9])<<8;\n    case 9 : c+=k[8];\n    case 8 : b+=((uint32_t)k[7])<<24;\n    case 7 : b+=((uint32_t)k[6])<<16;\n    case 6 : b+=((uint32_t)k[5])<<8;\n    case 5 : b+=k[4];\n    case 4 : a+=((uint32_t)k[3])<<24;\n    case 3 : a+=((uint32_t)k[2])<<16;\n    case 2 : a+=((uint32_t)k[1])<<8;\n    case 1 : a+=k[0];\n             break;\n    case 0 : return c;\n    }\n  }\n\n  final(a,b,c);\n  *val2 = b;\n  return c;\n}\n\n/*\n * hashbig():\n * This is the same as hash_word() on big-endian machines.  It is different\n * from hashlittle() on all machines.  hashbig() takes advantage of\n * big-endian byte ordering. \n */\nstatic uint32_t hashbig( const void *key, size_t length, uint32_t *val2)\n{\n  uint32_t a,b,c;\n  union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2;\n\n  u.ptr = key;\n  if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {\n    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */\n    const uint8_t  *k8;\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a,b,c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /* \n     * \"k[2]<<8\" actually reads beyond the end of the string, but\n     * then shifts out the part it's not allowed to read.  Because the\n     * string is aligned, the illegal read is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticably faster for short strings (like English words).\n     *\n     * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR.\n     */\n#if 0\n    switch(length)\n    {\n    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;\n    case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;\n    case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;\n    case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;\n    case 8 : b+=k[1]; a+=k[0]; break;\n    case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;\n    case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;\n    case 5 : b+=k[1]&0xff000000; a+=k[0]; break;\n    case 4 : a+=k[0]; break;\n    case 3 : a+=k[0]&0xffffff00; break;\n    case 2 : a+=k[0]&0xffff0000; break;\n    case 1 : a+=k[0]&0xff000000; break;\n    case 0 : return c;              /* zero length strings require no mixing */\n    }\n\n#else  /* make valgrind happy */\n\n    k8 = (const uint8_t *)k;\n    switch(length)                   /* all the case statements fall through */\n    {\n    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;\n    case 11: c+=((uint32_t)k8[10])<<8;  /* fall through */\n    case 10: c+=((uint32_t)k8[9])<<16;  /* fall through */\n    case 9 : c+=((uint32_t)k8[8])<<24;  /* fall through */\n    case 8 : b+=k[1]; a+=k[0]; break;\n    case 7 : b+=((uint32_t)k8[6])<<8;   /* fall through */\n    case 6 : b+=((uint32_t)k8[5])<<16;  /* fall through */\n    case 5 : b+=((uint32_t)k8[4])<<24;  /* fall through */\n    case 4 : a+=k[0]; break;\n    case 3 : a+=((uint32_t)k8[2])<<8;   /* fall through */\n    case 2 : a+=((uint32_t)k8[1])<<16;  /* fall through */\n    case 1 : a+=((uint32_t)k8[0])<<24; break;\n    case 0 : return c;\n    }\n\n#endif /* !VALGRIND */\n\n  } else {                        /* need to read the key one byte at a time */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12)\n    {\n      a += ((uint32_t)k[0])<<24;\n      a += ((uint32_t)k[1])<<16;\n      a += ((uint32_t)k[2])<<8;\n      a += ((uint32_t)k[3]);\n      b += ((uint32_t)k[4])<<24;\n      b += ((uint32_t)k[5])<<16;\n      b += ((uint32_t)k[6])<<8;\n      b += ((uint32_t)k[7]);\n      c += ((uint32_t)k[8])<<24;\n      c += ((uint32_t)k[9])<<16;\n      c += ((uint32_t)k[10])<<8;\n      c += ((uint32_t)k[11]);\n      mix(a,b,c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch(length)                   /* all the case statements fall through */\n    {\n    case 12: c+=k[11];\n    case 11: c+=((uint32_t)k[10])<<8;\n    case 10: c+=((uint32_t)k[9])<<16;\n    case 9 : c+=((uint32_t)k[8])<<24;\n    case 8 : b+=k[7];\n    case 7 : b+=((uint32_t)k[6])<<8;\n    case 6 : b+=((uint32_t)k[5])<<16;\n    case 5 : b+=((uint32_t)k[4])<<24;\n    case 4 : a+=k[3];\n    case 3 : a+=((uint32_t)k[2])<<8;\n    case 2 : a+=((uint32_t)k[1])<<16;\n    case 1 : a+=((uint32_t)k[0])<<24;\n             break;\n    case 0 : return c;\n    }\n  }\n\n  final(a,b,c);\n  *val2 = b;\n  return c;\n}\n\n/* I basically use hashlittle here, but use native endian within each\n * element.  This delivers least-surprise: hash such as \"int arr[] = {\n * 1, 2 }; hash_stable(arr, 2, 0);\" will be the same on big and little\n * endian machines, even though a bytewise hash wouldn't be. */\nuint64_t hash64_stable_64(const void *key, size_t n, uint64_t base)\n{\n\tconst uint64_t *k = key;\n\tuint32_t a,b,c;\n\n\t/* Set up the internal state */\n\ta = b = c = 0xdeadbeef + ((uint32_t)n*8) + (base >> 32) + base;\n\n\twhile (n > 3) {\n\t\ta += (uint32_t)k[0];\n\t\tb += (uint32_t)(k[0] >> 32);\n\t\tc += (uint32_t)k[1];\n\t\tmix(a,b,c);\n\t\ta += (uint32_t)(k[1] >> 32);\n\t\tb += (uint32_t)k[2];\n\t\tc += (uint32_t)(k[2] >> 32);\n\t\tmix(a,b,c);\n\t\tn -= 3;\n\t\tk += 3;\n\t}\n\tswitch (n) {\n\tcase 2:\n\t\ta += (uint32_t)k[0];\n\t\tb += (uint32_t)(k[0] >> 32);\n\t\tc += (uint32_t)k[1];\n\t\tmix(a,b,c);\n\t\ta += (uint32_t)(k[1] >> 32);\n\t\tbreak;\n\tcase 1:\n\t\ta += (uint32_t)k[0];\n\t\tb += (uint32_t)(k[0] >> 32);\n\t\tbreak;\n\tcase 0:\n\t\treturn c;\n\t}\n\tfinal(a,b,c);\n\treturn ((uint64_t)b << 32) | c;\n}\n\nuint64_t hash64_stable_32(const void *key, size_t n, uint64_t base)\n{\n\tconst uint32_t *k = key;\n\tuint32_t a,b,c;\n\n\t/* Set up the internal state */\n\ta = b = c = 0xdeadbeef + ((uint32_t)n*4) + (base >> 32) + base;\n\n\twhile (n > 3) {\n\t\ta += k[0];\n\t\tb += k[1];\n\t\tc += k[2];\n\t\tmix(a,b,c);\n\n\t\tn -= 3;\n\t\tk += 3;\n\t}\n\tswitch (n) {\n\tcase 2:\n\t\tb += (uint32_t)k[1];\n\tcase 1:\n\t\ta += (uint32_t)k[0];\n \t\tbreak;\n\tcase 0:\n\t\treturn c;\n\t}\n\tfinal(a,b,c);\n\treturn ((uint64_t)b << 32) | c;\n}\n\nuint64_t hash64_stable_16(const void *key, size_t n, uint64_t base)\n{\n\tconst uint16_t *k = key;\n\tuint32_t a,b,c;\n\n\t/* Set up the internal state */\n\ta = b = c = 0xdeadbeef + ((uint32_t)n*2) + (base >> 32) + base;\n\n\twhile (n > 6) {\n\t\ta += (uint32_t)k[0] + ((uint32_t)k[1] << 16);\n\t\tb += (uint32_t)k[2] + ((uint32_t)k[3] << 16);\n\t\tc += (uint32_t)k[4] + ((uint32_t)k[5] << 16);\n\t\tmix(a,b,c);\n\n\t\tn -= 6;\n\t\tk += 6;\n\t}\n\n\tswitch (n) {\n\tcase 5:\n\t\tc += (uint32_t)k[4];\n\tcase 4:\n\t\tb += ((uint32_t)k[3] << 16);\n\tcase 3:\n\t\tb += (uint32_t)k[2];\n\tcase 2:\n\t\ta += ((uint32_t)k[1] << 16);\n\tcase 1:\n\t\ta += (uint32_t)k[0];\n\t\tbreak;\n\tcase 0:\n\t\treturn c;\n\t}\n\tfinal(a,b,c);\n\treturn ((uint64_t)b << 32) | c;\n}\n\nuint64_t hash64_stable_8(const void *key, size_t n, uint64_t base)\n{\n\tuint32_t b32 = base + (base >> 32);\n\tuint32_t lower = hashlittle(key, n, &b32);\n\n\treturn ((uint64_t)b32 << 32) | lower;\t\n}\n\nuint32_t hash_any(const void *key, size_t length, uint32_t base)\n{\n\tif (HASH_BIG_ENDIAN)\n\t\treturn hashbig(key, length, &base);\n\telse\n\t\treturn hashlittle(key, length, &base);\n}\n\nuint32_t hash_stable_64(const void *key, size_t n, uint32_t base)\n{\n\treturn hash64_stable_64(key, n, base);\n}\n\nuint32_t hash_stable_32(const void *key, size_t n, uint32_t base)\n{\n\treturn hash64_stable_32(key, n, base);\n}\n\nuint32_t hash_stable_16(const void *key, size_t n, uint32_t base)\n{\n\treturn hash64_stable_16(key, n, base);\n}\n\nuint32_t hash_stable_8(const void *key, size_t n, uint32_t base)\n{\n\treturn hashlittle(key, n, &base);\n}\n\n/* Jenkins' lookup8 is a 64 bit hash, but he says it's obsolete.  Use\n * the plain one and recombine into 64 bits. */\nuint64_t hash64_any(const void *key, size_t length, uint64_t base)\n{\n\tuint32_t b32 = base + (base >> 32);\n\tuint32_t lower;\n\n\tif (HASH_BIG_ENDIAN)\n\t\tlower = hashbig(key, length, &b32);\n\telse\n\t\tlower = hashlittle(key, length, &b32);\n\n\treturn ((uint64_t)b32 << 32) | lower;\n}\n\n#ifdef SELF_TEST\n\n/* used for timings */\nvoid driver1()\n{\n  uint8_t buf[256];\n  uint32_t i;\n  uint32_t h=0;\n  time_t a,z;\n\n  time(&a);\n  for (i=0; i<256; ++i) buf[i] = 'x';\n  for (i=0; i<1; ++i) \n  {\n    h = hashlittle(&buf[0],1,h);\n  }\n  time(&z);\n  if (z-a > 0) printf(\"time %d %.8x\\n\", z-a, h);\n}\n\n/* check that every input bit changes every output bit half the time */\n#define HASHSTATE 1\n#define HASHLEN   1\n#define MAXPAIR 60\n#define MAXLEN  70\nvoid driver2()\n{\n  uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];\n  uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;\n  uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];\n  uint32_t x[HASHSTATE],y[HASHSTATE];\n  uint32_t hlen;\n\n  printf(\"No more than %d trials should ever be needed \\n\",MAXPAIR/2);\n  for (hlen=0; hlen < MAXLEN; ++hlen)\n  {\n    z=0;\n    for (i=0; i<hlen; ++i)  /*----------------------- for each input byte, */\n    {\n      for (j=0; j<8; ++j)   /*------------------------ for each input bit, */\n      {\n\tfor (m=1; m<8; ++m) /*------------ for several possible initvals, */\n\t{\n\t  for (l=0; l<HASHSTATE; ++l)\n\t    e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);\n\n      \t  /*---- check that every output bit is affected by that input bit */\n\t  for (k=0; k<MAXPAIR; k+=2)\n\t  { \n\t    uint32_t finished=1;\n\t    /* keys have one bit different */\n\t    for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}\n\t    /* have a and b be two keys differing in only one bit */\n\t    a[i] ^= (k<<j);\n\t    a[i] ^= (k>>(8-j));\n\t     c[0] = hashlittle(a, hlen, m);\n\t    b[i] ^= ((k+1)<<j);\n\t    b[i] ^= ((k+1)>>(8-j));\n\t     d[0] = hashlittle(b, hlen, m);\n\t    /* check every bit is 1, 0, set, and not set at least once */\n\t    for (l=0; l<HASHSTATE; ++l)\n\t    {\n\t      e[l] &= (c[l]^d[l]);\n\t      f[l] &= ~(c[l]^d[l]);\n\t      g[l] &= c[l];\n\t      h[l] &= ~c[l];\n\t      x[l] &= d[l];\n\t      y[l] &= ~d[l];\n\t      if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;\n\t    }\n\t    if (finished) break;\n\t  }\n\t  if (k>z) z=k;\n\t  if (k==MAXPAIR) \n\t  {\n\t     printf(\"Some bit didn't change: \");\n\t     printf(\"%.8x %.8x %.8x %.8x %.8x %.8x  \",\n\t            e[0],f[0],g[0],h[0],x[0],y[0]);\n\t     printf(\"i %d j %d m %d len %d\\n\", i, j, m, hlen);\n\t  }\n\t  if (z==MAXPAIR) goto done;\n\t}\n      }\n    }\n   done:\n    if (z < MAXPAIR)\n    {\n      printf(\"Mix success  %2d bytes  %2d initvals  \",i,m);\n      printf(\"required  %d  trials\\n\", z/2);\n    }\n  }\n  printf(\"\\n\");\n}\n\n/* Check for reading beyond the end of the buffer and alignment problems */\nvoid driver3()\n{\n  uint8_t buf[MAXLEN+20], *b;\n  uint32_t len;\n  uint8_t q[] = \"This is the time for all good men to come to the aid of their country...\";\n  uint32_t h;\n  uint8_t qq[] = \"xThis is the time for all good men to come to the aid of their country...\";\n  uint32_t i;\n  uint8_t qqq[] = \"xxThis is the time for all good men to come to the aid of their country...\";\n  uint32_t j;\n  uint8_t qqqq[] = \"xxxThis is the time for all good men to come to the aid of their country...\";\n  uint32_t ref,x,y;\n  uint8_t *p;\n\n  printf(\"Endianness.  These lines should all be the same (for values filled in):\\n\");\n  printf(\"%.8x                            %.8x                            %.8x\\n\",\n         hash_word((const uint32_t *)q, (sizeof(q)-1)/4, 13),\n         hash_word((const uint32_t *)q, (sizeof(q)-5)/4, 13),\n         hash_word((const uint32_t *)q, (sizeof(q)-9)/4, 13));\n  p = q;\n  printf(\"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),\n         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),\n         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),\n         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),\n         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),\n         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));\n  p = &qq[1];\n  printf(\"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),\n         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),\n         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),\n         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),\n         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),\n         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));\n  p = &qqq[2];\n  printf(\"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),\n         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),\n         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),\n         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),\n         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),\n         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));\n  p = &qqqq[3];\n  printf(\"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),\n         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),\n         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),\n         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),\n         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),\n         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));\n  printf(\"\\n\");\n\n  /* check that hashlittle2 and hashlittle produce the same results */\n  i=47; j=0;\n  hashlittle2(q, sizeof(q), &i, &j);\n  if (hashlittle(q, sizeof(q), 47) != i)\n    printf(\"hashlittle2 and hashlittle mismatch\\n\");\n\n  /* check that hash_word2 and hash_word produce the same results */\n  len = 0xdeadbeef;\n  i=47, j=0;\n  hash_word2(&len, 1, &i, &j);\n  if (hash_word(&len, 1, 47) != i)\n    printf(\"hash_word2 and hash_word mismatch %x %x\\n\", \n\t   i, hash_word(&len, 1, 47));\n\n  /* check hashlittle doesn't read before or after the ends of the string */\n  for (h=0, b=buf+1; h<8; ++h, ++b)\n  {\n    for (i=0; i<MAXLEN; ++i)\n    {\n      len = i;\n      for (j=0; j<i; ++j) *(b+j)=0;\n\n      /* these should all be equal */\n      ref = hashlittle(b, len, (uint32_t)1);\n      *(b+i)=(uint8_t)~0;\n      *(b-1)=(uint8_t)~0;\n      x = hashlittle(b, len, (uint32_t)1);\n      y = hashlittle(b, len, (uint32_t)1);\n      if ((ref != x) || (ref != y)) \n      {\n\tprintf(\"alignment error: %.8x %.8x %.8x %d %d\\n\",ref,x,y,\n               h, i);\n      }\n    }\n  }\n}\n\n/* check for problems with nulls */\n void driver4()\n{\n  uint8_t buf[1];\n  uint32_t h,i,state[HASHSTATE];\n\n\n  buf[0] = ~0;\n  for (i=0; i<HASHSTATE; ++i) state[i] = 1;\n  printf(\"These should all be different\\n\");\n  for (i=0, h=0; i<8; ++i)\n  {\n    h = hashlittle(buf, 0, h);\n    printf(\"%2ld  0-byte strings, hash is  %.8x\\n\", i, h);\n  }\n}\n\n\nint main()\n{\n  driver1();   /* test that the key is hashed: used for timings */\n  driver2();   /* test that whole key is hashed thoroughly */\n  driver3();   /* test that nothing but the key is hashed */\n  driver4();   /* test hashing multiple buffers (all buffers are null) */\n  return 1;\n}\n\n#endif  /* SELF_TEST */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/hash/hash.h",
    "content": "/* CC0 (Public domain) - see LICENSE file for details */\n#ifndef CCAN_HASH_H\n#define CCAN_HASH_H\n#include <stdint.h>\n#include <stdlib.h>\n#include <ccan/build_assert/build_assert.h>\n\n/* Stolen mostly from: lookup3.c, by Bob Jenkins, May 2006, Public Domain.\n * \n * http://burtleburtle.net/bob/c/lookup3.c\n */\n\n/**\n * hash - fast hash of an array for internal use\n * @p: the array or pointer to first element\n * @num: the number of elements to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * The memory region pointed to by p is combined with the base to form\n * a 32-bit hash.\n *\n * This hash will have different results on different machines, so is\n * only useful for internal hashes (ie. not hashes sent across the\n * network or saved to disk).\n *\n * It may also change with future versions: it could even detect at runtime\n * what the fastest hash to use is.\n *\n * See also: hash64, hash_stable.\n *\n * Example:\n *\t#include <ccan/hash/hash.h>\n *\t#include <err.h>\n *\t#include <stdio.h>\n *\t#include <string.h>\n *\n *\t// Simple demonstration: idential strings will have the same hash, but\n *\t// two different strings will probably not.\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tuint32_t hash1, hash2;\n *\n *\t\tif (argc != 3)\n *\t\t\terr(1, \"Usage: %s <string1> <string2>\", argv[0]);\n *\n *\t\thash1 = hash(argv[1], strlen(argv[1]), 0);\n *\t\thash2 = hash(argv[2], strlen(argv[2]), 0);\n *\t\tprintf(\"Hash is %s\\n\", hash1 == hash2 ? \"same\" : \"different\");\n *\t\treturn 0;\n *\t}\n */\n#define hash(p, num, base) hash_any((p), (num)*sizeof(*(p)), (base))\n\n/**\n * hash_stable - hash of an array for external use\n * @p: the array or pointer to first element\n * @num: the number of elements to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * The array of simple integer types pointed to by p is combined with\n * the base to form a 32-bit hash.\n *\n * This hash will have the same results on different machines, so can\n * be used for external hashes (ie. hashes sent across the network or\n * saved to disk).  The results will not change in future versions of\n * this module.\n *\n * Note that it is only legal to hand an array of simple integer types\n * to this hash (ie. char, uint16_t, int64_t, etc).  In these cases,\n * the same values will have the same hash result, even though the\n * memory representations of integers depend on the machine\n * endianness.\n *\n * See also:\n *\thash64_stable\n *\n * Example:\n *\t#include <ccan/hash/hash.h>\n *\t#include <err.h>\n *\t#include <stdio.h>\n *\t#include <string.h>\n *\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tif (argc != 2)\n *\t\t\terr(1, \"Usage: %s <string-to-hash>\", argv[0]);\n *\n *\t\tprintf(\"Hash stable result is %u\\n\",\n *\t\t       hash_stable(argv[1], strlen(argv[1]), 0));\n *\t\treturn 0;\n *\t}\n */\n#define hash_stable(p, num, base)\t\t\t\t\t\\\n\t(BUILD_ASSERT_OR_ZERO(sizeof(*(p)) == 8 || sizeof(*(p)) == 4\t\\\n\t\t\t      || sizeof(*(p)) == 2 || sizeof(*(p)) == 1) + \\\n\t sizeof(*(p)) == 8 ? hash_stable_64((p), (num), (base))\t\t\\\n\t : sizeof(*(p)) == 4 ? hash_stable_32((p), (num), (base))\t\\\n\t : sizeof(*(p)) == 2 ? hash_stable_16((p), (num), (base))\t\\\n\t : hash_stable_8((p), (num), (base)))\n\n/**\n * hash_u32 - fast hash an array of 32-bit values for internal use\n * @key: the array of uint32_t\n * @num: the number of elements to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * The array of uint32_t pointed to by @key is combined with the base\n * to form a 32-bit hash.  This is 2-3 times faster than hash() on small\n * arrays, but the advantage vanishes over large hashes.\n *\n * This hash will have different results on different machines, so is\n * only useful for internal hashes (ie. not hashes sent across the\n * network or saved to disk).\n */\nuint32_t hash_u32(const uint32_t *key, size_t num, uint32_t base);\n\n/**\n * hash_string - very fast hash of an ascii string\n * @str: the nul-terminated string\n *\n * The string is hashed, using a hash function optimized for ASCII and\n * similar strings.  It's weaker than the other hash functions.\n *\n * This hash may have different results on different machines, so is\n * only useful for internal hashes (ie. not hashes sent across the\n * network or saved to disk).  The results will be different from the\n * other hash functions in this module, too.\n */\nstatic inline uint32_t hash_string(const char *string)\n{\n\t/* This is Karl Nelson <kenelson@ece.ucdavis.edu>'s X31 hash.\n\t * It's a little faster than the (much better) lookup3 hash(): 56ns vs\n\t * 84ns on my 2GHz Intel Core Duo 2 laptop for a 10 char string. */\n\tuint32_t ret;\n\n\tfor (ret = 0; *string; string++)\n\t\tret = (ret << 5) - ret + *string;\n\n\treturn ret;\n}\n\n/**\n * hash64 - fast 64-bit hash of an array for internal use\n * @p: the array or pointer to first element\n * @num: the number of elements to hash\n * @base: the 64-bit base number to roll into the hash (usually 0)\n *\n * The memory region pointed to by p is combined with the base to form\n * a 64-bit hash.\n *\n * This hash will have different results on different machines, so is\n * only useful for internal hashes (ie. not hashes sent across the\n * network or saved to disk).\n *\n * It may also change with future versions: it could even detect at runtime\n * what the fastest hash to use is.\n *\n * See also: hash.\n *\n * Example:\n *\t#include <ccan/hash/hash.h>\n *\t#include <err.h>\n *\t#include <stdio.h>\n *\t#include <string.h>\n *\n *\t// Simple demonstration: idential strings will have the same hash, but\n *\t// two different strings will probably not.\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tuint64_t hash1, hash2;\n *\n *\t\tif (argc != 3)\n *\t\t\terr(1, \"Usage: %s <string1> <string2>\", argv[0]);\n *\n *\t\thash1 = hash64(argv[1], strlen(argv[1]), 0);\n *\t\thash2 = hash64(argv[2], strlen(argv[2]), 0);\n *\t\tprintf(\"Hash is %s\\n\", hash1 == hash2 ? \"same\" : \"different\");\n *\t\treturn 0;\n *\t}\n */\n#define hash64(p, num, base) hash64_any((p), (num)*sizeof(*(p)), (base))\n\n/**\n * hash64_stable - 64 bit hash of an array for external use\n * @p: the array or pointer to first element\n * @num: the number of elements to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * The array of simple integer types pointed to by p is combined with\n * the base to form a 64-bit hash.\n *\n * This hash will have the same results on different machines, so can\n * be used for external hashes (ie. hashes sent across the network or\n * saved to disk).  The results will not change in future versions of\n * this module.\n *\n * Note that it is only legal to hand an array of simple integer types\n * to this hash (ie. char, uint16_t, int64_t, etc).  In these cases,\n * the same values will have the same hash result, even though the\n * memory representations of integers depend on the machine\n * endianness.\n *\n * See also:\n *\thash_stable\n *\n * Example:\n *\t#include <ccan/hash/hash.h>\n *\t#include <err.h>\n *\t#include <stdio.h>\n *\t#include <string.h>\n *\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tif (argc != 2)\n *\t\t\terr(1, \"Usage: %s <string-to-hash>\", argv[0]);\n *\n *\t\tprintf(\"Hash stable result is %llu\\n\",\n *\t\t       (long long)hash64_stable(argv[1], strlen(argv[1]), 0));\n *\t\treturn 0;\n *\t}\n */\n#define hash64_stable(p, num, base)\t\t\t\t\t\\\n\t(BUILD_ASSERT_OR_ZERO(sizeof(*(p)) == 8 || sizeof(*(p)) == 4\t\\\n\t\t\t      || sizeof(*(p)) == 2 || sizeof(*(p)) == 1) + \\\n\t sizeof(*(p)) == 8 ? hash64_stable_64((p), (num), (base))\t\\\n\t : sizeof(*(p)) == 4 ? hash64_stable_32((p), (num), (base))\t\\\n\t : sizeof(*(p)) == 2 ? hash64_stable_16((p), (num), (base))\t\\\n\t : hash64_stable_8((p), (num), (base)))\n\n\n/**\n * hashl - fast 32/64-bit hash of an array for internal use\n * @p: the array or pointer to first element\n * @num: the number of elements to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * This is either hash() or hash64(), on 32/64 bit long machines.\n */\n#define hashl(p, num, base)\t\t\t\t\t\t\\\n\t(BUILD_ASSERT_OR_ZERO(sizeof(long) == sizeof(uint32_t)\t\t\\\n\t\t\t      || sizeof(long) == sizeof(uint64_t)) +\t\\\n\t(sizeof(long) == sizeof(uint64_t)\t\t\t\t\\\n\t ? hash64((p), (num), (base)) : hash((p), (num), (base))))\n\n/* Our underlying operations. */\nuint32_t hash_any(const void *key, size_t length, uint32_t base);\nuint32_t hash_stable_64(const void *key, size_t n, uint32_t base);\nuint32_t hash_stable_32(const void *key, size_t n, uint32_t base);\nuint32_t hash_stable_16(const void *key, size_t n, uint32_t base);\nuint32_t hash_stable_8(const void *key, size_t n, uint32_t base);\nuint64_t hash64_any(const void *key, size_t length, uint64_t base);\nuint64_t hash64_stable_64(const void *key, size_t n, uint64_t base);\nuint64_t hash64_stable_32(const void *key, size_t n, uint64_t base);\nuint64_t hash64_stable_16(const void *key, size_t n, uint64_t base);\nuint64_t hash64_stable_8(const void *key, size_t n, uint64_t base);\n\n/**\n * hash_pointer - hash a pointer for internal use\n * @p: the pointer value to hash\n * @base: the base number to roll into the hash (usually 0)\n *\n * The pointer p (not what p points to!) is combined with the base to form\n * a 32-bit hash.\n *\n * This hash will have different results on different machines, so is\n * only useful for internal hashes (ie. not hashes sent across the\n * network or saved to disk).\n *\n * Example:\n *\t#include <ccan/hash/hash.h>\n *\n *\t// Code to keep track of memory regions.\n *\tstruct region {\n *\t\tstruct region *chain;\n *\t\tvoid *start;\n *\t\tunsigned int size;\n *\t};\n *\t// We keep a simple hash table.\n *\tstatic struct region *region_hash[128];\n *\n *\tstatic void add_region(struct region *r)\n *\t{\n *\t\tunsigned int h = hash_pointer(r->start, 0);\n *\n *\t\tr->chain = region_hash[h];\n *\t\tregion_hash[h] = r->chain;\n *\t}\n *\n *\tstatic struct region *find_region(const void *start)\n *\t{\n *\t\tstruct region *r;\n *\n *\t\tfor (r = region_hash[hash_pointer(start, 0)]; r; r = r->chain)\n *\t\t\tif (r->start == start)\n *\t\t\t\treturn r;\n *\t\treturn NULL;\n *\t}\n */\nstatic inline uint32_t hash_pointer(const void *p, uint32_t base)\n{\n\tif (sizeof(p) % sizeof(uint32_t) == 0) {\n\t\t/* This convoluted union is the right way of aliasing. */\n\t\tunion {\n\t\t\tuint32_t a[sizeof(p) / sizeof(uint32_t)];\n\t\t\tconst void *p;\n\t\t} u;\n\t\tu.p = p;\n\t\treturn hash_u32(u.a, sizeof(p) / sizeof(uint32_t), base);\n\t} else\n\t\treturn hash(&p, 1, base);\n}\n#endif /* HASH_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/hash/test/api-hash_stable.c",
    "content": "#include <ccan/hash/hash.h>\n#include <ccan/tap/tap.h>\n#include <stdbool.h>\n#include <string.h>\n\n#define ARRAY_WORDS 5\n\nint main(int argc, char *argv[])\n{\n\tunsigned int i;\n\tuint8_t u8array[ARRAY_WORDS];\n\tuint16_t u16array[ARRAY_WORDS];\n\tuint32_t u32array[ARRAY_WORDS];\n\tuint64_t u64array[ARRAY_WORDS];\n\n\t/* Initialize arrays. */\n\tfor (i = 0; i < ARRAY_WORDS; i++) {\n\t\tu8array[i] = i;\n\t\tu16array[i] = i;\n\t\tu32array[i] = i;\n\t\tu64array[i] = i;\n\t}\n\n\tplan_tests(264);\n\n\t/* hash_stable is API-guaranteed. */\n\tok1(hash_stable(u8array, ARRAY_WORDS, 0) == 0x1d4833cc);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 1) == 0x37125e2 );\n\tok1(hash_stable(u8array, ARRAY_WORDS, 2) == 0x330a007a);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 4) == 0x7b0df29b);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 8) == 0xe7e5d741);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 16) == 0xaae57471);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 32) == 0xc55399e5);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 64) == 0x67f21f7 );\n\tok1(hash_stable(u8array, ARRAY_WORDS, 128) == 0x1d795b71);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 256) == 0xeb961671);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 512) == 0xc2597247);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 1024) == 0x3f5c4d75);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 2048) == 0xe65cf4f9);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 4096) == 0xf2cd06cb);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 8192) == 0x443041e1);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 16384) == 0xdfc618f5);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 32768) == 0x5e3d5b97);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 65536) == 0xd5f64730);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 131072) == 0x372bbecc);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 262144) == 0x7c194c8d);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 524288) == 0x16cbb416);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 1048576) == 0x53e99222);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 2097152) == 0x6394554a);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 4194304) == 0xd83a506d);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 8388608) == 0x7619d9a4);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 16777216) == 0xfe98e5f6);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 33554432) == 0x6c262927);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 67108864) == 0x3f0106fd);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 134217728) == 0xc91e3a28);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 268435456) == 0x14229579);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 536870912) == 0x9dbefa76);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 1073741824) == 0xb05c0c78);\n\tok1(hash_stable(u8array, ARRAY_WORDS, 2147483648U) == 0x88f24d81);\n\n\tok1(hash_stable(u16array, ARRAY_WORDS, 0) == 0xecb5f507);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 1) == 0xadd666e6);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 2) == 0xea0f214c);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 4) == 0xae4051ba);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 8) == 0x6ed28026);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 16) == 0xa3917a19);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 32) == 0xf370f32b);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 64) == 0x807af460);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 128) == 0xb4c8cd83);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 256) == 0xa10cb5b0);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 512) == 0x8b7d7387);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 1024) == 0x9e49d1c );\n\tok1(hash_stable(u16array, ARRAY_WORDS, 2048) == 0x288830d1);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 4096) == 0xbe078a43);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 8192) == 0xa16d5d88);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 16384) == 0x46839fcd);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 32768) == 0x9db9bd4f);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 65536) == 0xedff58f8);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 131072) == 0x95ecef18);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 262144) == 0x23c31b7d);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 524288) == 0x1d85c7d0);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 1048576) == 0x25218842);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 2097152) == 0x711d985c);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 4194304) == 0x85470eca);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 8388608) == 0x99ed4ceb);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 16777216) == 0x67b3710c);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 33554432) == 0x77f1ab35);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 67108864) == 0x81f688aa);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 134217728) == 0x27b56ca5);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 268435456) == 0xf21ba203);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 536870912) == 0xd48d1d1 );\n\tok1(hash_stable(u16array, ARRAY_WORDS, 1073741824) == 0xa542b62d);\n\tok1(hash_stable(u16array, ARRAY_WORDS, 2147483648U) == 0xa04c7058);\n\n\tok1(hash_stable(u32array, ARRAY_WORDS, 0) == 0x13305f8c);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 1) == 0x171abf74);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 2) == 0x7646fcc7);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 4) == 0xa758ed5);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 8) == 0x2dedc2e4);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 16) == 0x28e2076b);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 32) == 0xb73091c5);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 64) == 0x87daf5db);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 128) == 0xa16dfe20);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 256) == 0x300c63c3);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 512) == 0x255c91fc);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 1024) == 0x6357b26);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 2048) == 0x4bc5f339);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 4096) == 0x1301617c);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 8192) == 0x506792c9);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 16384) == 0xcd596705);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 32768) == 0xa8713cac);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 65536) == 0x94d9794);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 131072) == 0xac753e8);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 262144) == 0xcd8bdd20);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 524288) == 0xd44faf80);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 1048576) == 0x2547ccbe);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 2097152) == 0xbab06dbc);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 4194304) == 0xaac0e882);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 8388608) == 0x443f48d0);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 16777216) == 0xdff49fcc);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 33554432) == 0x9ce0fd65);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 67108864) == 0x9ddb1def);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 134217728) == 0x86096f25);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 268435456) == 0xe713b7b5);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 536870912) == 0x5baeffc5);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 1073741824) == 0xde874f52);\n\tok1(hash_stable(u32array, ARRAY_WORDS, 2147483648U) == 0xeca13b4e);\n\n\tok1(hash_stable(u64array, ARRAY_WORDS, 0) == 0x12ef6302);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 1) == 0xe9aeb406);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 2) == 0xc4218ceb);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 4) == 0xb3d11412);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 8) == 0xdafbd654);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 16) == 0x9c336cba);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 32) == 0x65059721);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 64) == 0x95b5bbe6);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 128) == 0xe7596b84);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 256) == 0x503622a2);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 512) == 0xecdcc5ca);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 1024) == 0xc40d0513);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 2048) == 0xaab25e4d);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 4096) == 0xcc353fb9);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 8192) == 0x18e2319f);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 16384) == 0xfddaae8d);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 32768) == 0xef7976f2);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 65536) == 0x86359fc9);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 131072) == 0x8b5af385);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 262144) == 0x80d4ee31);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 524288) == 0x42f5f85b);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 1048576) == 0x9a6920e1);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 2097152) == 0x7b7c9850);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 4194304) == 0x69573e09);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 8388608) == 0xc942bc0e);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 16777216) == 0x7a89f0f1);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 33554432) == 0x2dd641ca);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 67108864) == 0x89bbd391);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 134217728) == 0xbcf88e31);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 268435456) == 0xfa7a3460);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 536870912) == 0x49a37be0);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 1073741824) == 0x1b346394);\n\tok1(hash_stable(u64array, ARRAY_WORDS, 2147483648U) == 0x6c3a1592);\n\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 0) == 16887282882572727244ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 1) == 12032777473133454818ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 2) == 18183407363221487738ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 4) == 17860764172704150171ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 8) == 18076051600675559233ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 16) == 9909361918431556721ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 32) == 12937969888744675813ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 64) == 5245669057381736951ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 128) == 4376874646406519665ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 256) == 14219974419871569521ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 512) == 2263415354134458951ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 1024) == 4953859694526221685ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 2048) == 3432228642067641593ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 4096) == 1219647244417697483ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 8192) == 7629939424585859553ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 16384) == 10041660531376789749ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 32768) == 13859885793922603927ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 65536) == 15069060338344675120ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 131072) == 818163430835601100ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 262144) == 14914314323019517069ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 524288) == 17518437749769352214ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 1048576) == 14920048004901212706ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 2097152) == 8758567366332536138ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 4194304) == 6226655736088907885ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 8388608) == 13716650013685832100ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 16777216) == 305325651636315638ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 33554432) == 16784147606583781671ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 67108864) == 16509467555140798205ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 134217728) == 8717281234694060584ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 268435456) == 8098476701725660537ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 536870912) == 16345871539461094006ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 1073741824) == 3755557000429964408ULL);\n\tok1(hash64_stable(u8array, ARRAY_WORDS, 2147483648U) == 15017348801959710081ULL);\n\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 0) == 1038028831307724039ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 1) == 10155473272642627302ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 2) == 5714751190106841420ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 4) == 3923885607767527866ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 8) == 3931017318293995558ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 16) == 1469696588339313177ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 32) == 11522218526952715051ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 64) == 6953517591561958496ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 128) == 7406689491740052867ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 256) == 10101844489704093104ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 512) == 12511348870707245959ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 1024) == 1614019938016861468ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 2048) == 5294796182374592721ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 4096) == 16089570706643716675ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 8192) == 1689302638424579464ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 16384) == 1446340172370386893ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 32768) == 16535503506744393039ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 65536) == 3496794142527150328ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 131072) == 6568245367474548504ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 262144) == 9487676460765485949ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 524288) == 4519762130966530000ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 1048576) == 15623412069215340610ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 2097152) == 544013388676438108ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 4194304) == 5594904760290840266ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 8388608) == 18098755780041592043ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 16777216) == 6389168672387330316ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 33554432) == 896986127732419381ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 67108864) == 13232626471143901354ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 134217728) == 53378562890493093ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 268435456) == 10072361400297824771ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 536870912) == 14511948118285144529ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 1073741824) == 6981033484844447277ULL);\n\tok1(hash64_stable(u16array, ARRAY_WORDS, 2147483648U) == 5619339091684126808ULL);\n\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 0) == 3037571077312110476ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 1) == 14732398743825071988ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 2) == 14949132158206672071ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 4) == 1291370080511561429ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 8) == 10792665964172133092ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 16) == 14250138032054339435ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 32) == 17136741522078732741ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 64) == 3260193403318236635ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 128) == 10526616652205653536ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 256) == 9019690373358576579ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 512) == 6997491436599677436ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 1024) == 18302783371416533798ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 2048) == 10149320644446516025ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 4096) == 7073759949410623868ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 8192) == 17442399482223760073ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 16384) == 2983906194216281861ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 32768) == 4975845419129060524ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 65536) == 594019910205413268ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 131072) == 11903010186073691112ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 262144) == 7339636527154847008ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 524288) == 15243305400579108736ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 1048576) == 16737926245392043198ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 2097152) == 15725083267699862972ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 4194304) == 12527834265678833794ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 8388608) == 13908436455987824848ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 16777216) == 9672773345173872588ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 33554432) == 2305314279896710501ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 67108864) == 1866733780381408751ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 134217728) == 11906263969465724709ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 268435456) == 5501594918093830069ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 536870912) == 15823785789276225477ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 1073741824) == 17353000723889475410ULL);\n\tok1(hash64_stable(u32array, ARRAY_WORDS, 2147483648U) == 7494736910655503182ULL);\n\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 0) == 9765419389786481410ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 1) == 11182806172127114246ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 2) == 2559155171395472619ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 4) == 3311692033324815378ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 8) == 1297175419505333844ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 16) == 617896928653569210ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 32) == 1517398559958603553ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 64) == 4504821917445110758ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 128) == 1971743331114904452ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 256) == 6177667912354374306ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 512) == 15570521289777792458ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 1024) == 9204559632415917331ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 2048) == 9008982669760028237ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 4096) == 14803537660281700281ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 8192) == 2873966517448487327ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 16384) == 5859277625928363661ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 32768) == 15520461285618185970ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 65536) == 16746489793331175369ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 131072) == 514952025484227461ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 262144) == 10867212269810675249ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 524288) == 9822204377278314587ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 1048576) == 3295088921987850465ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 2097152) == 7559197431498053712ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 4194304) == 1667267269116771849ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 8388608) == 2916804068951374862ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 16777216) == 14422558383125688561ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 33554432) == 10083112683694342602ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 67108864) == 7222777647078298513ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 134217728) == 18424513674048212529ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 268435456) == 14913668581101810784ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 536870912) == 14377721174297902048ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 1073741824) == 6031715005667500948ULL);\n\tok1(hash64_stable(u64array, ARRAY_WORDS, 2147483648U) == 4827100319722378642ULL);\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/hash/test/run.c",
    "content": "#include <ccan/hash/hash.h>\n#include <ccan/tap/tap.h>\n#include <ccan/hash/hash.c>\n#include <stdbool.h>\n#include <string.h>\n\n#define ARRAY_WORDS 5\n\nint main(int argc, char *argv[])\n{\n\tunsigned int i, j, k;\n\tuint32_t array[ARRAY_WORDS], val;\n\tchar array2[sizeof(array) + sizeof(uint32_t)];\n\tuint32_t results[256];\n\n\t/* Initialize array. */\n\tfor (i = 0; i < ARRAY_WORDS; i++)\n\t\tarray[i] = i;\n\n\tplan_tests(39);\n\t/* Hash should be the same, indep of memory alignment. */\n\tval = hash(array, ARRAY_WORDS, 0);\n\tfor (i = 0; i < sizeof(uint32_t); i++) {\n\t\tmemcpy(array2 + i, array, sizeof(array));\n\t\tok(hash(array2 + i, ARRAY_WORDS, 0) != val,\n\t\t   \"hash matched at offset %i\", i);\n\t}\n\n\t/* Hash of random values should have random distribution:\n\t * check one byte at a time. */\n\tfor (i = 0; i < sizeof(uint32_t); i++) {\n\t\tunsigned int lowest = -1U, highest = 0;\n\n\t\tmemset(results, 0, sizeof(results));\n\n\t\tfor (j = 0; j < 256000; j++) {\n\t\t\tfor (k = 0; k < ARRAY_WORDS; k++)\n\t\t\t\tarray[k] = random();\n\t\t\tresults[(hash(array, ARRAY_WORDS, 0) >> i*8)&0xFF]++;\n\t\t}\n\n\t\tfor (j = 0; j < 256; j++) {\n\t\t\tif (results[j] < lowest)\n\t\t\t\tlowest = results[j];\n\t\t\tif (results[j] > highest)\n\t\t\t\thighest = results[j];\n\t\t}\n\t\t/* Expect within 20% */\n\t\tok(lowest > 800, \"Byte %i lowest %i\", i, lowest);\n\t\tok(highest < 1200, \"Byte %i highest %i\", i, highest);\n\t\tdiag(\"Byte %i, range %u-%u\", i, lowest, highest);\n\t}\n\n\t/* Hash of random values should have random distribution:\n\t * check one byte at a time. */\n\tfor (i = 0; i < sizeof(uint64_t); i++) {\n\t\tunsigned int lowest = -1U, highest = 0;\n\n\t\tmemset(results, 0, sizeof(results));\n\n\t\tfor (j = 0; j < 256000; j++) {\n\t\t\tfor (k = 0; k < ARRAY_WORDS; k++)\n\t\t\t\tarray[k] = random();\n\t\t\tresults[(hash64(array, sizeof(array)/sizeof(uint64_t),\n\t\t\t\t\t0) >> i*8)&0xFF]++;\n\t\t}\n\n\t\tfor (j = 0; j < 256; j++) {\n\t\t\tif (results[j] < lowest)\n\t\t\t\tlowest = results[j];\n\t\t\tif (results[j] > highest)\n\t\t\t\thighest = results[j];\n\t\t}\n\t\t/* Expect within 20% */\n\t\tok(lowest > 800, \"Byte %i lowest %i\", i, lowest);\n\t\tok(highest < 1200, \"Byte %i highest %i\", i, highest);\n\t\tdiag(\"Byte %i, range %u-%u\", i, lowest, highest);\n\t}\n\n\t/* Hash of pointer values should also have random distribution. */\n\tfor (i = 0; i < sizeof(uint32_t); i++) {\n\t\tunsigned int lowest = -1U, highest = 0;\n\t\tchar *p = malloc(256000);\n\n\t\tmemset(results, 0, sizeof(results));\n\n\t\tfor (j = 0; j < 256000; j++)\n\t\t\tresults[(hash_pointer(p + j, 0) >> i*8)&0xFF]++;\n\t\tfree(p);\n\n\t\tfor (j = 0; j < 256; j++) {\n\t\t\tif (results[j] < lowest)\n\t\t\t\tlowest = results[j];\n\t\t\tif (results[j] > highest)\n\t\t\t\thighest = results[j];\n\t\t}\n\t\t/* Expect within 20% */\n\t\tok(lowest > 800, \"hash_pointer byte %i lowest %i\", i, lowest);\n\t\tok(highest < 1200, \"hash_pointer byte %i highest %i\",\n\t\t   i, highest);\n\t\tdiag(\"hash_pointer byte %i, range %u-%u\", i, lowest, highest);\n\t}\n\n\tif (sizeof(long) == sizeof(uint32_t))\n\t\tok1(hashl(array, ARRAY_WORDS, 0)\n\t\t    == hash(array, ARRAY_WORDS, 0));\n\telse\n\t\tok1(hashl(array, ARRAY_WORDS, 0)\n\t\t    == hash64(array, ARRAY_WORDS, 0));\n\n\t/* String hash: weak, so only test bottom byte */\n\tfor (i = 0; i < 1; i++) {\n\t\tunsigned int num = 0, cursor, lowest = -1U, highest = 0;\n\t\tchar p[5];\n\n\t\tmemset(results, 0, sizeof(results));\n\n\t\tmemset(p, 'A', sizeof(p));\n\t\tp[sizeof(p)-1] = '\\0';\n\n\t\tfor (;;) {\n\t\t\tfor (cursor = 0; cursor < sizeof(p)-1; cursor++) {\n\t\t\t\tp[cursor]++;\n\t\t\t\tif (p[cursor] <= 'z')\n\t\t\t\t\tbreak;\n\t\t\t\tp[cursor] = 'A';\n\t\t\t}\n\t\t\tif (cursor == sizeof(p)-1)\n\t\t\t\tbreak;\n\n\t\t\tresults[(hash_string(p) >> i*8)&0xFF]++;\n\t\t\tnum++;\n\t\t}\n\n\t\tfor (j = 0; j < 256; j++) {\n\t\t\tif (results[j] < lowest)\n\t\t\t\tlowest = results[j];\n\t\t\tif (results[j] > highest)\n\t\t\t\thighest = results[j];\n\t\t}\n\t\t/* Expect within 20% */\n\t\tok(lowest > 35000, \"hash_pointer byte %i lowest %i\", i, lowest);\n\t\tok(highest < 53000, \"hash_pointer byte %i highest %i\",\n\t\t   i, highest);\n\t\tdiag(\"hash_pointer byte %i, range %u-%u\", i, lowest, highest);\n\t}\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/licenses/BSD-MIT",
    "content": "Permission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/licenses/CC0",
    "content": "Statement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an \"owner\") of an original work of authorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works (\"Commons\") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the \"Affirmer\"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights (\"Copyright and Related Rights\"). Copyright and Related Rights include, but are not limited to, the following:\n\n    the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;\n    moral rights retained by the original author(s) and/or performer(s);\n    publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;\n    rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;\n    rights protecting the extraction, dissemination, use and reuse of data in a Work;\n    database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and\n    other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the \"License\"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n    No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.\n    Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.\n    Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.\n    Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/_info",
    "content": "#include <stdio.h>\n#include <string.h>\n#include \"config.h\"\n\n/**\n * list - double linked list routines\n *\n * The list header contains routines for manipulating double linked lists.\n * It defines two types: struct list_head used for anchoring lists, and\n * struct list_node which is usually embedded in the structure which is placed\n * in the list.\n *\n * Example:\n *\t#include <err.h>\n *\t#include <stdio.h>\n *\t#include <stdlib.h>\n *\t#include <ccan/list/list.h>\n *\n *\tstruct parent {\n *\t\tconst char *name;\n *\t\tstruct list_head children;\n *\t\tunsigned int num_children;\n *\t};\n *\n *\tstruct child {\n *\t\tconst char *name;\n *\t\tstruct list_node list;\n *\t};\n *\n *\tint main(int argc, char *argv[])\n *\t{\n *\t\tstruct parent p;\n *\t\tstruct child *c;\n *\t\tunsigned int i;\n *\n *\t\tif (argc < 2)\n *\t\t\terrx(1, \"Usage: %s parent children...\", argv[0]);\n *\n *\t\tp.name = argv[1];\n *\t\tlist_head_init(&p.children);\n *\t\tp.num_children = 0;\n *\t\tfor (i = 2; i < argc; i++) {\n *\t\t\tc = malloc(sizeof(*c));\n *\t\t\tc->name = argv[i];\n *\t\t\tlist_add(&p.children, &c->list);\n *\t\t\tp.num_children++;\n *\t\t}\n *\n *\t\tprintf(\"%s has %u children:\", p.name, p.num_children);\n *\t\tlist_for_each(&p.children, c, list)\n *\t\t\tprintf(\"%s \", c->name);\n *\t\tprintf(\"\\n\");\n *\t\treturn 0;\n *\t}\n *\n * License: BSD-MIT\n * Author: Rusty Russell <rusty@rustcorp.com.au>\n */\nint main(int argc, char *argv[])\n{\n\tif (argc != 2)\n\t\treturn 1;\n\n\tif (strcmp(argv[1], \"depends\") == 0) {\n\t\tprintf(\"ccan/container_of\\n\");\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/list.c",
    "content": "/* Licensed under BSD-MIT - see LICENSE file for details */\n#include <stdio.h>\n#include <stdlib.h>\n#include \"list.h\"\n\nstatic void *corrupt(const char *abortstr,\n\t\t     const struct list_node *head,\n\t\t     const struct list_node *node,\n\t\t     unsigned int count)\n{\n\tif (abortstr) {\n\t\tfprintf(stderr,\n\t\t\t\"%s: prev corrupt in node %p (%u) of %p\\n\",\n\t\t\tabortstr, node, count, head);\n\t\tabort();\n\t}\n\treturn NULL;\n}\n\nstruct list_node *list_check_node(const struct list_node *node,\n\t\t\t\t  const char *abortstr)\n{\n\tconst struct list_node *p, *n;\n\tint count = 0;\n\n\tfor (p = node, n = node->next; n != node; p = n, n = n->next) {\n\t\tcount++;\n\t\tif (n->prev != p)\n\t\t\treturn corrupt(abortstr, node, n, count);\n\t}\n\t/* Check prev on head node. */\n\tif (node->prev != p)\n\t\treturn corrupt(abortstr, node, node, 0);\n\n\treturn (struct list_node *)node;\n}\n\nstruct list_head *list_check(const struct list_head *h, const char *abortstr)\n{\n\tif (!list_check_node(&h->n, abortstr))\n\t\treturn NULL;\n\treturn (struct list_head *)h;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/list.h",
    "content": "/* Licensed under BSD-MIT - see LICENSE file for details */\n#ifndef CCAN_LIST_H\n#define CCAN_LIST_H\n#include <stdbool.h>\n#include <assert.h>\n#include <ccan/container_of/container_of.h>\n#include <ccan/check_type/check_type.h>\n\n/**\n * struct list_node - an entry in a doubly-linked list\n * @next: next entry (self if empty)\n * @prev: previous entry (self if empty)\n *\n * This is used as an entry in a linked list.\n * Example:\n *\tstruct child {\n *\t\tconst char *name;\n *\t\t// Linked list of all us children.\n *\t\tstruct list_node list;\n *\t};\n */\nstruct list_node\n{\n\tstruct list_node *next, *prev;\n};\n\n/**\n * struct list_head - the head of a doubly-linked list\n * @h: the list_head (containing next and prev pointers)\n *\n * This is used as the head of a linked list.\n * Example:\n *\tstruct parent {\n *\t\tconst char *name;\n *\t\tstruct list_head children;\n *\t\tunsigned int num_children;\n *\t};\n */\nstruct list_head\n{\n\tstruct list_node n;\n};\n\n/**\n * list_check - check head of a list for consistency\n * @h: the list_head\n * @abortstr: the location to print on aborting, or NULL.\n *\n * Because list_nodes have redundant information, consistency checking between\n * the back and forward links can be done.  This is useful as a debugging check.\n * If @abortstr is non-NULL, that will be printed in a diagnostic if the list\n * is inconsistent, and the function will abort.\n *\n * Returns the list head if the list is consistent, NULL if not (it\n * can never return NULL if @abortstr is set).\n *\n * See also: list_check_node()\n *\n * Example:\n *\tstatic void dump_parent(struct parent *p)\n *\t{\n *\t\tstruct child *c;\n *\n *\t\tprintf(\"%s (%u children):\\n\", p->name, p->num_children);\n *\t\tlist_check(&p->children, \"bad child list\");\n *\t\tlist_for_each(&p->children, c, list)\n *\t\t\tprintf(\" -> %s\\n\", c->name);\n *\t}\n */\nstruct list_head *list_check(const struct list_head *h, const char *abortstr);\n\n/**\n * list_check_node - check node of a list for consistency\n * @n: the list_node\n * @abortstr: the location to print on aborting, or NULL.\n *\n * Check consistency of the list node is in (it must be in one).\n *\n * See also: list_check()\n *\n * Example:\n *\tstatic void dump_child(const struct child *c)\n *\t{\n *\t\tlist_check_node(&c->list, \"bad child list\");\n *\t\tprintf(\"%s\\n\", c->name);\n *\t}\n */\nstruct list_node *list_check_node(const struct list_node *n,\n\t\t\t\t  const char *abortstr);\n\n#ifdef CCAN_LIST_DEBUG\n#define list_debug(h) list_check((h), __func__)\n#define list_debug_node(n) list_check_node((n), __func__)\n#else\n#define list_debug(h) (h)\n#define list_debug_node(n) (n)\n#endif\n\n/**\n * LIST_HEAD_INIT - initializer for an empty list_head\n * @name: the name of the list.\n *\n * Explicit initializer for an empty list.\n *\n * See also:\n *\tLIST_HEAD, list_head_init()\n *\n * Example:\n *\tstatic struct list_head my_list = LIST_HEAD_INIT(my_list);\n */\n#define LIST_HEAD_INIT(name) { { &name.n, &name.n } }\n\n/**\n * LIST_HEAD - define and initialize an empty list_head\n * @name: the name of the list.\n *\n * The LIST_HEAD macro defines a list_head and initializes it to an empty\n * list.  It can be prepended by \"static\" to define a static list_head.\n *\n * See also:\n *\tLIST_HEAD_INIT, list_head_init()\n *\n * Example:\n *\tstatic LIST_HEAD(my_global_list);\n */\n/*#define LIST_HEAD(name) \\\n\tstruct list_head name = LIST_HEAD_INIT(name) */\n\n/**\n * list_head_init - initialize a list_head\n * @h: the list_head to set to the empty list\n *\n * Example:\n *\t...\n *\tstruct parent *parent = malloc(sizeof(*parent));\n *\n *\tlist_head_init(&parent->children);\n *\tparent->num_children = 0;\n */\nstatic inline void list_head_init(struct list_head *h)\n{\n\th->n.next = h->n.prev = &h->n;\n}\n\n/**\n * list_add - add an entry at the start of a linked list.\n * @h: the list_head to add the node to\n * @n: the list_node to add to the list.\n *\n * The list_node does not need to be initialized; it will be overwritten.\n * Example:\n *\tstruct child *child = malloc(sizeof(*child));\n *\n *\tchild->name = \"marvin\";\n *\tlist_add(&parent->children, &child->list);\n *\tparent->num_children++;\n */\nstatic inline void list_add(struct list_head *h, struct list_node *n)\n{\n\tn->next = h->n.next;\n\tn->prev = &h->n;\n\th->n.next->prev = n;\n\th->n.next = n;\n\t(void)list_debug(h);\n}\n\n/**\n * list_add_tail - add an entry at the end of a linked list.\n * @h: the list_head to add the node to\n * @n: the list_node to add to the list.\n *\n * The list_node does not need to be initialized; it will be overwritten.\n * Example:\n *\tlist_add_tail(&parent->children, &child->list);\n *\tparent->num_children++;\n */\nstatic inline void list_add_tail(struct list_head *h, struct list_node *n)\n{\n\tn->next = &h->n;\n\tn->prev = h->n.prev;\n\th->n.prev->next = n;\n\th->n.prev = n;\n\t(void)list_debug(h);\n}\n\n/**\n * list_empty - is a list empty?\n * @h: the list_head\n *\n * If the list is empty, returns true.\n *\n * Example:\n *\tassert(list_empty(&parent->children) == (parent->num_children == 0));\n */\nstatic inline bool list_empty(const struct list_head *h)\n{\n\t(void)list_debug(h);\n\treturn h->n.next == &h->n;\n}\n\n/**\n * list_del - delete an entry from an (unknown) linked list.\n * @n: the list_node to delete from the list.\n *\n * Note that this leaves @n in an undefined state; it can be added to\n * another list, but not deleted again.\n *\n * See also:\n *\tlist_del_from()\n *\n * Example:\n *\tlist_del(&child->list);\n *\tparent->num_children--;\n */\nstatic inline void list_del(struct list_node *n)\n{\n\t(void)list_debug_node(n);\n\tn->next->prev = n->prev;\n\tn->prev->next = n->next;\n#ifdef CCAN_LIST_DEBUG\n\t/* Catch use-after-del. */\n\tn->next = n->prev = NULL;\n#endif\n}\n\n/**\n * list_del_from - delete an entry from a known linked list.\n * @h: the list_head the node is in.\n * @n: the list_node to delete from the list.\n *\n * This explicitly indicates which list a node is expected to be in,\n * which is better documentation and can catch more bugs.\n *\n * See also: list_del()\n *\n * Example:\n *\tlist_del_from(&parent->children, &child->list);\n *\tparent->num_children--;\n */\nstatic inline void list_del_from(struct list_head *h, struct list_node *n)\n{\n#ifdef CCAN_LIST_DEBUG\n\t{\n\t\t/* Thorough check: make sure it was in list! */\n\t\tstruct list_node *i;\n\t\tfor (i = h->n.next; i != n; i = i->next)\n\t\t\tassert(i != &h->n);\n\t}\n#endif /* CCAN_LIST_DEBUG */\n\n\t/* Quick test that catches a surprising number of bugs. */\n\tassert(!list_empty(h));\n\tlist_del(n);\n}\n\n/**\n * list_entry - convert a list_node back into the structure containing it.\n * @n: the list_node\n * @type: the type of the entry\n * @member: the list_node member of the type\n *\n * Example:\n *\t// First list entry is children.next; convert back to child.\n *\tchild = list_entry(parent->children.n.next, struct child, list);\n *\n * See Also:\n *\tlist_top(), list_for_each()\n */\n#define list_entry(n, type, member) container_of(n, type, member)\n\n/**\n * list_top - get the first entry in a list\n * @h: the list_head\n * @type: the type of the entry\n * @member: the list_node member of the type\n *\n * If the list is empty, returns NULL.\n *\n * Example:\n *\tstruct child *first;\n *\tfirst = list_top(&parent->children, struct child, list);\n *\tif (!first)\n *\t\tprintf(\"Empty list!\\n\");\n */\n#define list_top(h, type, member)\t\t\t\t\t\\\n\t((type *)list_top_((h), list_off_(type, member)))\n\nstatic inline const void *list_top_(const struct list_head *h, size_t off)\n{\n\tif (list_empty(h))\n\t\treturn NULL;\n\treturn (const char *)h->n.next - off;\n}\n\n/**\n * list_pop - remove the first entry in a list\n * @h: the list_head\n * @type: the type of the entry\n * @member: the list_node member of the type\n *\n * If the list is empty, returns NULL.\n *\n * Example:\n *\tstruct child *one;\n *\tone = list_pop(&parent->children, struct child, list);\n *\tif (!one)\n *\t\tprintf(\"Empty list!\\n\");\n */\n#define list_pop(h, type, member)\t\t\t\t\t\\\n\t((type *)list_pop_((h), list_off_(type, member)))\n\nstatic inline void *list_pop_(const struct list_head *h, size_t off)\n{\n\tstruct list_node *n;\n\n\tif (list_empty(h))\n\t\treturn NULL;\n\tn = h->n.next;\n\tlist_del(n);\n\treturn (char *)n - off;\n}\n\n/**\n * list_tail - get the last entry in a list\n * @h: the list_head\n * @type: the type of the entry\n * @member: the list_node member of the type\n *\n * If the list is empty, returns NULL.\n *\n * Example:\n *\tstruct child *last;\n *\tlast = list_tail(&parent->children, struct child, list);\n *\tif (!last)\n *\t\tprintf(\"Empty list!\\n\");\n */\n#define list_tail(h, type, member) \\\n\t((type *)list_tail_((h), list_off_(type, member)))\n\nstatic inline const void *list_tail_(const struct list_head *h, size_t off)\n{\n\tif (list_empty(h))\n\t\treturn NULL;\n\treturn (const char *)h->n.prev - off;\n}\n\n/**\n * list_for_each - iterate through a list.\n * @h: the list_head (warning: evaluated multiple times!)\n * @i: the structure containing the list_node\n * @member: the list_node member of the structure\n *\n * This is a convenient wrapper to iterate @i over the entire list.  It's\n * a for loop, so you can break and continue as normal.\n *\n * Example:\n *\tlist_for_each(&parent->children, child, list)\n *\t\tprintf(\"Name: %s\\n\", child->name);\n */\n#define list_for_each(h, i, member)\t\t\t\t\t\\\n\tlist_for_each_off(h, i, list_off_var_(i, member))\n\n/**\n * list_for_each_rev - iterate through a list backwards.\n * @h: the list_head\n * @i: the structure containing the list_node\n * @member: the list_node member of the structure\n *\n * This is a convenient wrapper to iterate @i over the entire list.  It's\n * a for loop, so you can break and continue as normal.\n *\n * Example:\n *\tlist_for_each_rev(&parent->children, child, list)\n *\t\tprintf(\"Name: %s\\n\", child->name);\n */\n#define list_for_each_rev(h, i, member)\t\t\t\t\t\\\n\tfor (i = container_of_var(list_debug(h)->n.prev, i, member);\t\\\n\t     &i->member != &(h)->n;\t\t\t\t\t\\\n\t     i = container_of_var(i->member.prev, i, member))\n\n/**\n * list_for_each_safe - iterate through a list, maybe during deletion\n * @h: the list_head\n * @i: the structure containing the list_node\n * @nxt: the structure containing the list_node\n * @member: the list_node member of the structure\n *\n * This is a convenient wrapper to iterate @i over the entire list.  It's\n * a for loop, so you can break and continue as normal.  The extra variable\n * @nxt is used to hold the next element, so you can delete @i from the list.\n *\n * Example:\n *\tstruct child *next;\n *\tlist_for_each_safe(&parent->children, child, next, list) {\n *\t\tlist_del(&child->list);\n *\t\tparent->num_children--;\n *\t}\n */\n#define list_for_each_safe(h, i, nxt, member)\t\t\t\t\\\n\tlist_for_each_safe_off(h, i, nxt, list_off_var_(i, member))\n\n/**\n * list_next - get the next entry in a list\n * @h: the list_head\n * @i: a pointer to an entry in the list.\n * @member: the list_node member of the structure\n *\n * If @i was the last entry in the list, returns NULL.\n *\n * Example:\n *\tstruct child *second;\n *\tsecond = list_next(&parent->children, first, list);\n *\tif (!second)\n *\t\tprintf(\"No second child!\\n\");\n */\n#define list_next(h, i, member)\t\t\t\t\t\t\\\n\t((list_typeof(i))list_entry_or_null(list_debug(h),\t\t\\\n\t\t\t\t\t    (i)->member.next,\t\t\\\n\t\t\t\t\t    list_off_var_((i), member)))\n\n/**\n * list_prev - get the previous entry in a list\n * @h: the list_head\n * @i: a pointer to an entry in the list.\n * @member: the list_node member of the structure\n *\n * If @i was the first entry in the list, returns NULL.\n *\n * Example:\n *\tfirst = list_prev(&parent->children, second, list);\n *\tif (!first)\n *\t\tprintf(\"Can't go back to first child?!\\n\");\n */\n#define list_prev(h, i, member)\t\t\t\t\t\t\\\n\t((list_typeof(i))list_entry_or_null(list_debug(h),\t\t\\\n\t\t\t\t\t    (i)->member.prev,\t\t\\\n\t\t\t\t\t    list_off_var_((i), member)))\n\n/**\n * list_append_list - empty one list onto the end of another.\n * @to: the list to append into\n * @from: the list to empty.\n *\n * This takes the entire contents of @from and moves it to the end of\n * @to.  After this @from will be empty.\n *\n * Example:\n *\tstruct list_head adopter;\n *\n *\tlist_append_list(&adopter, &parent->children);\n *\tassert(list_empty(&parent->children));\n *\tparent->num_children = 0;\n */\nstatic inline void list_append_list(struct list_head *to,\n\t\t\t\t    struct list_head *from)\n{\n\tstruct list_node *from_tail = list_debug(from)->n.prev;\n\tstruct list_node *to_tail = list_debug(to)->n.prev;\n\n\t/* Sew in head and entire list. */\n\tto->n.prev = from_tail;\n\tfrom_tail->next = &to->n;\n\tto_tail->next = &from->n;\n\tfrom->n.prev = to_tail;\n\n\t/* Now remove head. */\n\tlist_del(&from->n);\n\tlist_head_init(from);\n}\n\n/**\n * list_prepend_list - empty one list into the start of another.\n * @to: the list to prepend into\n * @from: the list to empty.\n *\n * This takes the entire contents of @from and moves it to the start\n * of @to.  After this @from will be empty.\n *\n * Example:\n *\tlist_prepend_list(&adopter, &parent->children);\n *\tassert(list_empty(&parent->children));\n *\tparent->num_children = 0;\n */\nstatic inline void list_prepend_list(struct list_head *to,\n\t\t\t\t     struct list_head *from)\n{\n\tstruct list_node *from_tail = list_debug(from)->n.prev;\n\tstruct list_node *to_head = list_debug(to)->n.next;\n\n\t/* Sew in head and entire list. */\n\tto->n.next = &from->n;\n\tfrom->n.prev = &to->n;\n\tto_head->prev = from_tail;\n\tfrom_tail->next = to_head;\n\n\t/* Now remove head. */\n\tlist_del(&from->n);\n\tlist_head_init(from);\n}\n\n/**\n * list_for_each_off - iterate through a list of memory regions.\n * @h: the list_head\n * @i: the pointer to a memory region wich contains list node data.\n * @off: offset(relative to @i) at which list node data resides.\n *\n * This is a low-level wrapper to iterate @i over the entire list, used to\n * implement all oher, more high-level, for-each constructs. It's a for loop,\n * so you can break and continue as normal.\n *\n * WARNING! Being the low-level macro that it is, this wrapper doesn't know\n * nor care about the type of @i. The only assumtion made is that @i points\n * to a chunk of memory that at some @offset, relative to @i, contains a\n * properly filled `struct node_list' which in turn contains pointers to\n * memory chunks and it's turtles all the way down. Whith all that in mind\n * remember that given the wrong pointer/offset couple this macro will\n * happilly churn all you memory untill SEGFAULT stops it, in other words\n * caveat emptor.\n *\n * It is worth mentioning that one of legitimate use-cases for that wrapper\n * is operation on opaque types with known offset for `struct list_node'\n * member(preferably 0), because it allows you not to disclose the type of\n * @i.\n *\n * Example:\n *\tlist_for_each_off(&parent->children, child,\n *\t\t\t\toffsetof(struct child, list))\n *\t\tprintf(\"Name: %s\\n\", child->name);\n */\n#define list_for_each_off(h, i, off)                                    \\\n  for (i = (typeof(i))list_node_to_off_(list_debug(h)->n.next, (off));             \\\n       list_node_from_off_((void *)i, (off)) != &(h)->n;                \\\n       i = (typeof(i))list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \\\n                             (off)))\n\n/**\n * list_for_each_safe_off - iterate through a list of memory regions, maybe\n * during deletion\n * @h: the list_head\n * @i: the pointer to a memory region wich contains list node data.\n * @nxt: the structure containing the list_node\n * @off: offset(relative to @i) at which list node data resides.\n *\n * For details see `list_for_each_off' and `list_for_each_safe'\n * descriptions.\n *\n * Example:\n *\tlist_for_each_safe_off(&parent->children, child,\n *\t\tnext, offsetof(struct child, list))\n *\t\tprintf(\"Name: %s\\n\", child->name);\n */\n#define list_for_each_safe_off(h, i, nxt, off)                          \\\n  for (i = (typeof(i))list_node_to_off_(list_debug(h)->n.next, (off)),             \\\n         nxt = (typeof(nxt))list_node_to_off_(list_node_from_off_(i, (off))->next,   \\\n                                 (off));                                \\\n       list_node_from_off_(i, (off)) != &(h)->n;                        \\\n       i = nxt,                                                         \\\n         nxt = (typeof(nxt))list_node_to_off_(list_node_from_off_(i, (off))->next,   \\\n                                 (off)))\n\n\n/* Other -off variants. */\n#define list_entry_off(n, type, off)\t\t\\\n\t((type *)list_node_from_off_((n), (off)))\n\n#define list_head_off(h, type, off)\t\t\\\n\t((type *)list_head_off((h), (off)))\n\n#define list_tail_off(h, type, off)\t\t\\\n\t((type *)list_tail_((h), (off)))\n\n#define list_add_off(h, n, off)                 \\\n\tlist_add((h), list_node_from_off_((n), (off)))\n\n#define list_del_off(n, off)                    \\\n\tlist_del(list_node_from_off_((n), (off)))\n\n#define list_del_from_off(h, n, off)\t\t\t\\\n\tlist_del_from(h, list_node_from_off_((n), (off)))\n\n/* Offset helper functions so we only single-evaluate. */\nstatic inline void *list_node_to_off_(struct list_node *node, size_t off)\n{\n\treturn (void *)((char *)node - off);\n}\nstatic inline struct list_node *list_node_from_off_(void *ptr, size_t off)\n{\n\treturn (struct list_node *)((char *)ptr + off);\n}\n\n/* Get the offset of the member, but make sure it's a list_node. */\n#define list_off_(type, member)\t\t\t\t\t\\\n\t(container_off(type, member) +\t\t\t\t\\\n\t check_type(((type *)0)->member, struct list_node))\n\n#define list_off_var_(var, member)\t\t\t\\\n\t(container_off_var(var, member) +\t\t\\\n\t check_type(var->member, struct list_node))\n\n#if HAVE_TYPEOF\n#define list_typeof(var) typeof(var)\n#else\n#define list_typeof(var) void *\n#endif\n\n/* Returns member, or NULL if at end of list. */\nstatic inline void *list_entry_or_null(const struct list_head *h,\n\t\t\t\t       struct list_node *n,\n\t\t\t\t       size_t off)\n{\n\tif (n == &h->n)\n\t\treturn NULL;\n\treturn (char *)n - off;\n}\n#endif /* CCAN_LIST_H */\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/compile_ok-constant.c",
    "content": "#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n#include <stdbool.h>\n#include <stdio.h>\n\nstruct child {\n\tconst char *name;\n\tstruct list_node list;\n};\n\nstatic bool children(const struct list_head *list)\n{\n\treturn !list_empty(list);\n}\n\nstatic const struct child *first_child(const struct list_head *list)\n{\n\treturn list_top(list, struct child, list);\n}\n\nstatic const struct child *last_child(const struct list_head *list)\n{\n\treturn list_tail(list, struct child, list);\n}\n\nstatic void check_children(const struct list_head *list)\n{\n\tlist_check(list, \"bad child list\");\n}\n\nstatic void print_children(const struct list_head *list)\n{\n\tconst struct child *c;\n\tlist_for_each(list, c, list)\n\t\tprintf(\"%s\\n\", c->name);\n}\n\nint main(void)\n{\n\tLIST_HEAD(h);\n\n\tchildren(&h);\n\tfirst_child(&h);\n\tlast_child(&h);\n\tcheck_children(&h);\n\tprint_children(&h);\n\treturn 0;\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/helper.c",
    "content": "#include <stdlib.h>\n#include <stdbool.h>\n#include <time.h>\n\n#include <ccan/list/list.h>\n#include \"helper.h\"\n\n#define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \\\n  (42)\n\nstruct opaque {\n  struct list_node list;\n  size_t secret_offset;\n  char   secret_drawer[42];\n};\n\nstatic bool not_randomized = true;\n\nstruct opaque *create_opaque_blob(void)\n{\n  struct opaque *blob = calloc(1, sizeof(struct opaque));\n\n  if (not_randomized) {\n    srandom((int)time(NULL));\n    not_randomized = false;\n  }\n\n  blob->secret_offset = random() % (sizeof(blob->secret_drawer));\n  blob->secret_drawer[blob->secret_offset] =\n    ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING;\n\n  return blob;\n}\n\nbool if_blobs_know_the_secret(struct opaque *blob)\n{\n  bool answer = true;\n  int i;\n  for (i = 0; i < sizeof(blob->secret_drawer) /\n               sizeof(blob->secret_drawer[0]); i++)\n          if (i != blob->secret_offset)\n                  answer = answer && (blob->secret_drawer[i] == 0);\n          else\n                  answer = answer &&\n                          (blob->secret_drawer[blob->secret_offset] ==\n           ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING);\n\n  return answer;\n}\n\nvoid destroy_opaque_blob(struct opaque *blob)\n{\n  free(blob);\n}\n\n\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/helper.h",
    "content": "/* These are in a separate C file so we can test undefined structures. */\nstruct opaque;\ntypedef struct opaque opaque_t;\n\nopaque_t *create_opaque_blob(void);\nbool if_blobs_know_the_secret(opaque_t *blob);\nvoid destroy_opaque_blob(opaque_t *blob);\n\n\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-check-corrupt.c",
    "content": "#include <setjmp.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <err.h>\n\n/* We don't actually want it to exit... */\nstatic jmp_buf aborted;\n#define abort() longjmp(aborted, 1)\n\n#define fprintf my_fprintf\nstatic char printf_buffer[1000];\n\nstatic int my_fprintf(FILE *stream, const char *format, ...)\n{\n\tva_list ap;\n\tint ret;\n\tva_start(ap, format);\n\tret = vsprintf(printf_buffer, format, ap);\n\tva_end(ap);\n\treturn ret;\n}\n\n#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n\nint main(int argc, char *argv[])\n{\n\tstruct list_head list;\n\tstruct list_node n1;\n\tchar expect[100];\n\n\tplan_tests(9);\n\t/* Empty list. */\n\tlist.n.next = &list.n;\n\tlist.n.prev = &list.n;\n\tok1(list_check(&list, NULL) == &list);\n\n\t/* Bad back ptr */\n\tlist.n.prev = &n1;\n\t/* Non-aborting version. */\n\tok1(list_check(&list, NULL) == NULL);\n\n\t/* Aborting version. */\n\tsprintf(expect, \"test message: prev corrupt in node %p (0) of %p\\n\",\n\t\t&list, &list);\n\tif (setjmp(aborted) == 0) {\n\t\tlist_check(&list, \"test message\");\n\t\tfail(\"list_check on empty with bad back ptr didn't fail!\");\n\t} else {\n\t\tok1(strcmp(printf_buffer, expect) == 0);\n\t}\n\n\t/* n1 in list. */\n\tlist.n.next = &n1;\n\tlist.n.prev = &n1;\n\tn1.prev = &list.n;\n\tn1.next = &list.n;\n\tok1(list_check(&list, NULL) == &list);\n\tok1(list_check_node(&n1, NULL) == &n1);\n\n\t/* Bad back ptr */\n\tn1.prev = &n1;\n\tok1(list_check(&list, NULL) == NULL);\n\tok1(list_check_node(&n1, NULL) == NULL);\n\n\t/* Aborting version. */\n\tsprintf(expect, \"test message: prev corrupt in node %p (1) of %p\\n\",\n\t\t&n1, &list);\n\tif (setjmp(aborted) == 0) {\n\t\tlist_check(&list, \"test message\");\n\t\tfail(\"list_check on n1 bad back ptr didn't fail!\");\n\t} else {\n\t\tok1(strcmp(printf_buffer, expect) == 0);\n\t}\n\n\tsprintf(expect, \"test message: prev corrupt in node %p (0) of %p\\n\",\n\t\t&n1, &n1);\n\tif (setjmp(aborted) == 0) {\n\t\tlist_check_node(&n1, \"test message\");\n\t\tfail(\"list_check_node on n1 bad back ptr didn't fail!\");\n\t} else {\n\t\tok1(strcmp(printf_buffer, expect) == 0);\n\t}\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-list_del_from-assert.c",
    "content": "#define CCAN_LIST_DEBUG 1\n#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <signal.h>\n\nint main(int argc, char *argv[])\n{\n\tstruct list_head list1, list2;\n\tstruct list_node n1, n2, n3;\n\tpid_t child;\n\tint status;\n\n\tplan_tests(1);\n\tlist_head_init(&list1);\n\tlist_head_init(&list2);\n\tlist_add(&list1, &n1);\n\tlist_add(&list2, &n2);\n\tlist_add_tail(&list2, &n3);\n\n\tchild = fork();\n\tif (child) {\n\t\twait(&status);\n\t} else {\n\t\t/* This should abort. */\n\t\tlist_del_from(&list1, &n3);\n\t\texit(0);\n\t}\n\n\tok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT);\n\tlist_del_from(&list2, &n3);\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-list_prev-list_next.c",
    "content": "#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n#include \"helper.h\"\n\nstruct parent {\n\tconst char *name;\n\tunsigned int num_children;\n\tstruct list_head children;\n};\n\nstruct child {\n\tconst char *name;\n\tstruct list_node list;\n};\n\nint main(int argc, char *argv[])\n{\n\tstruct parent parent;\n\tstruct child c1, c2, c3;\n\tconst struct parent *p;\n\tconst struct child *c;\n\n\tplan_tests(20);\n\tparent.num_children = 0;\n\tlist_head_init(&parent.children);\n\n\tc1.name = \"c1\";\n\tlist_add(&parent.children, &c1.list);\n\n\tok1(list_next(&parent.children, &c1, list) == NULL);\n\tok1(list_prev(&parent.children, &c1, list) == NULL);\n\n\tc2.name = \"c2\";\n\tlist_add_tail(&parent.children, &c2.list);\n\n\tok1(list_next(&parent.children, &c1, list) == &c2);\n\tok1(list_prev(&parent.children, &c1, list) == NULL);\n\tok1(list_next(&parent.children, &c2, list) == NULL);\n\tok1(list_prev(&parent.children, &c2, list) == &c1);\n\n\tc3.name = \"c3\";\n\tlist_add_tail(&parent.children, &c3.list);\n\n\tok1(list_next(&parent.children, &c1, list) == &c2);\n\tok1(list_prev(&parent.children, &c1, list) == NULL);\n\tok1(list_next(&parent.children, &c2, list) == &c3);\n\tok1(list_prev(&parent.children, &c2, list) == &c1);\n\tok1(list_next(&parent.children, &c3, list) == NULL);\n\tok1(list_prev(&parent.children, &c3, list) == &c2);\n\n\t/* Const variants */\n\tp = &parent;\n\tc = &c2;\n\tok1(list_next(&p->children, &c1, list) == &c2);\n\tok1(list_prev(&p->children, &c1, list) == NULL);\n\tok1(list_next(&p->children, c, list) == &c3);\n\tok1(list_prev(&p->children, c, list) == &c1);\n\tok1(list_next(&parent.children, c, list) == &c3);\n\tok1(list_prev(&parent.children, c, list) == &c1);\n\tok1(list_next(&p->children, &c3, list) == NULL);\n\tok1(list_prev(&p->children, &c3, list) == &c2);\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-prepend_list.c",
    "content": "#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n#include <stdarg.h>\n\nstatic bool list_expect(struct list_head *h, ...)\n{\n\tva_list ap;\n\tstruct list_node *n = &h->n, *expected;\n\n\tva_start(ap, h);\n\twhile ((expected = va_arg(ap, struct list_node *)) != NULL) {\n\t\tn = n->next;\n\t\tif (n != expected)\n\t\t\treturn false;\n\t}\n\treturn (n->next == &h->n);\n}\n\nint main(int argc, char *argv[])\n{\n\tstruct list_head h1, h2;\n\tstruct list_node n[4];\n\n\tplan_tests(40);\n\n\tlist_head_init(&h1);\n\tlist_head_init(&h2);\n\n\t/* Append an empty list to an empty list. */\n\tlist_append_list(&h1, &h2);\n\tok1(list_empty(&h1));\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\n\t/* Prepend an empty list to an empty list. */\n\tlist_prepend_list(&h1, &h2);\n\tok1(list_empty(&h1));\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\n\t/* Append an empty list to a non-empty list */\n\tlist_add(&h1, &n[0]);\n\tlist_append_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[0], NULL));\n\n\t/* Prepend an empty list to a non-empty list */\n\tlist_prepend_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[0], NULL));\n\n\t/* Append a non-empty list to an empty list. */\n\tlist_append_list(&h2, &h1);\n\tok1(list_empty(&h1));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h2, &n[0], NULL));\n\n\t/* Prepend a non-empty list to an empty list. */\n\tlist_prepend_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[0], NULL));\n\n\t/* Prepend a non-empty list to non-empty list. */\n\tlist_add(&h2, &n[1]);\n\tlist_prepend_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[1], &n[0], NULL));\n\n\t/* Append a non-empty list to non-empty list. */\n\tlist_add(&h2, &n[2]);\n\tlist_append_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[1], &n[0], &n[2], NULL));\n\n\t/* Prepend a 2-entry list to a 2-entry list. */\n\tlist_del_from(&h1, &n[2]);\n\tlist_add(&h2, &n[2]);\n\tlist_add_tail(&h2, &n[3]);\n\tlist_prepend_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[2], &n[3], &n[1], &n[0], NULL));\n\n\t/* Append a 2-entry list to a 2-entry list. */\n\tlist_del_from(&h1, &n[2]);\n\tlist_del_from(&h1, &n[3]);\n\tlist_add(&h2, &n[2]);\n\tlist_add_tail(&h2, &n[3]);\n\tlist_append_list(&h1, &h2);\n\tok1(list_empty(&h2));\n\tok1(list_check(&h1, NULL));\n\tok1(list_check(&h2, NULL));\n\tok1(list_expect(&h1, &n[1], &n[0], &n[2], &n[3], NULL));\n\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-single-eval.c",
    "content": "/* Make sure macros only evaluate their args once. */\n#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n\nstruct parent {\n\tconst char *name;\n\tstruct list_head children;\n\tunsigned int num_children;\n\tint eval_count;\n};\n\nstruct child {\n\tconst char *name;\n\tstruct list_node list;\n};\n\nstatic LIST_HEAD(static_list);\n\n#define ref(obj, counter) ((counter)++, (obj))\n\nint main(int argc, char *argv[])\n{\n\tstruct parent parent;\n\tstruct child c1, c2, c3, *c, *n;\n\tunsigned int i;\n\tunsigned int static_count = 0, parent_count = 0, list_count = 0,\n\t\tnode_count = 0;\n\tstruct list_head list = LIST_HEAD_INIT(list);\n\n\tplan_tests(74);\n\t/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */\n\tok1(list_empty(ref(&static_list, static_count)));\n\tok1(static_count == 1);\n\tok1(list_check(ref(&static_list, static_count), NULL));\n\tok1(static_count == 2);\n\tok1(list_empty(ref(&list, list_count)));\n\tok1(list_count == 1);\n\tok1(list_check(ref(&list, list_count), NULL));\n\tok1(list_count == 2);\n\n\tparent.num_children = 0;\n\tlist_head_init(ref(&parent.children, parent_count));\n\tok1(parent_count == 1);\n\t/* Test list_head_init */\n\tok1(list_empty(ref(&parent.children, parent_count)));\n\tok1(parent_count == 2);\n\tok1(list_check(ref(&parent.children, parent_count), NULL));\n\tok1(parent_count == 3);\n\n\tc2.name = \"c2\";\n\tlist_add(ref(&parent.children, parent_count), &c2.list);\n\tok1(parent_count == 4);\n\t/* Test list_add and !list_empty. */\n\tok1(!list_empty(ref(&parent.children, parent_count)));\n\tok1(parent_count == 5);\n\tok1(c2.list.next == &parent.children.n);\n\tok1(c2.list.prev == &parent.children.n);\n\tok1(parent.children.n.next == &c2.list);\n\tok1(parent.children.n.prev == &c2.list);\n\t/* Test list_check */\n\tok1(list_check(ref(&parent.children, parent_count), NULL));\n\tok1(parent_count == 6);\n\n\tc1.name = \"c1\";\n\tlist_add(ref(&parent.children, parent_count), &c1.list);\n\tok1(parent_count == 7);\n\t/* Test list_add and !list_empty. */\n\tok1(!list_empty(ref(&parent.children, parent_count)));\n\tok1(parent_count == 8);\n\tok1(c2.list.next == &parent.children.n);\n\tok1(c2.list.prev == &c1.list);\n\tok1(parent.children.n.next == &c1.list);\n\tok1(parent.children.n.prev == &c2.list);\n\tok1(c1.list.next == &c2.list);\n\tok1(c1.list.prev == &parent.children.n);\n\t/* Test list_check */\n\tok1(list_check(ref(&parent.children, parent_count), NULL));\n\tok1(parent_count == 9);\n\n\tc3.name = \"c3\";\n\tlist_add_tail(ref(&parent.children, parent_count), &c3.list);\n\tok1(parent_count == 10);\n\t/* Test list_add_tail and !list_empty. */\n\tok1(!list_empty(ref(&parent.children, parent_count)));\n\tok1(parent_count == 11);\n\tok1(parent.children.n.next == &c1.list);\n\tok1(parent.children.n.prev == &c3.list);\n\tok1(c1.list.next == &c2.list);\n\tok1(c1.list.prev == &parent.children.n);\n\tok1(c2.list.next == &c3.list);\n\tok1(c2.list.prev == &c1.list);\n\tok1(c3.list.next == &parent.children.n);\n\tok1(c3.list.prev == &c2.list);\n\t/* Test list_check */\n\tok1(list_check(ref(&parent.children, parent_count), NULL));\n\tok1(parent_count == 12);\n\n\t/* Test list_check_node */\n\tok1(list_check_node(&c1.list, NULL));\n\tok1(list_check_node(&c2.list, NULL));\n\tok1(list_check_node(&c3.list, NULL));\n\n\t/* Test list_top */\n\tok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1);\n\tok1(parent_count == 13);\n\n\t/* Test list_tail */\n\tok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3);\n\tok1(parent_count == 14);\n\n\t/* Test list_for_each. */\n\ti = 0;\n\tlist_for_each(&parent.children, c, list) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(c == &c1);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(c == &c2);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c3);\n\t\t\tbreak;\n\t\t}\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\n\t/* Test list_for_each_safe, list_del and list_del_from. */\n\ti = 0;\n\tlist_for_each_safe(&parent.children, c, n, list) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(c == &c1);\n\t\t\tlist_del(ref(&c->list, node_count));\n\t\t\tok1(node_count == 1);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(c == &c2);\n\t\t\tlist_del_from(ref(&parent.children, parent_count),\n\t\t\t\t      ref(&c->list, node_count));\n\t\t\tok1(node_count == 2);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c3);\n\t\t\tlist_del_from(ref(&parent.children, parent_count),\n\t\t\t\t      ref(&c->list, node_count));\n\t\t\tok1(node_count == 3);\n\t\t\tbreak;\n\t\t}\n\t\tok1(list_check(ref(&parent.children, parent_count), NULL));\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\tok1(parent_count == 19);\n\tok1(list_empty(ref(&parent.children, parent_count)));\n\tok1(parent_count == 20);\n\n\t/* Test list_top/list_tail on empty list. */\n\tok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL);\n\tok1(parent_count == 21);\n\tok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL);\n\tok1(parent_count == 22);\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run-with-debug.c",
    "content": "/* Just like run.c, but with all debug checks enabled. */\n#define CCAN_LIST_DEBUG 1\n#include <ccan/list/test/run.c>\n"
  },
  {
    "path": "build-tools/cpp_misc/ccan/list/test/run.c",
    "content": "#include <ccan/list/list.h>\n#include <ccan/tap/tap.h>\n#include <ccan/list/list.c>\n#include \"helper.h\"\n\nstruct parent {\n\tconst char *name;\n\tstruct list_head children;\n\tunsigned int num_children;\n};\n\nstruct child {\n\tconst char *name;\n\tstruct list_node list;\n};\n\nstatic LIST_HEAD(static_list);\n\nint main(int argc, char *argv[])\n{\n\tstruct parent parent;\n\tstruct child c1, c2, c3, *c, *n;\n\tunsigned int i;\n\tstruct list_head list = LIST_HEAD_INIT(list);\n\topaque_t *q, *nq;\n\tstruct list_head opaque_list = LIST_HEAD_INIT(opaque_list);\n\n\tplan_tests(68);\n\t/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */\n\tok1(list_empty(&static_list));\n\tok1(list_check(&static_list, NULL));\n\tok1(list_empty(&list));\n\tok1(list_check(&list, NULL));\n\n\tparent.num_children = 0;\n\tlist_head_init(&parent.children);\n\t/* Test list_head_init */\n\tok1(list_empty(&parent.children));\n\tok1(list_check(&parent.children, NULL));\n\n\tc2.name = \"c2\";\n\tlist_add(&parent.children, &c2.list);\n\t/* Test list_add and !list_empty. */\n\tok1(!list_empty(&parent.children));\n\tok1(c2.list.next == &parent.children.n);\n\tok1(c2.list.prev == &parent.children.n);\n\tok1(parent.children.n.next == &c2.list);\n\tok1(parent.children.n.prev == &c2.list);\n\t/* Test list_check */\n\tok1(list_check(&parent.children, NULL));\n\n\tc1.name = \"c1\";\n\tlist_add(&parent.children, &c1.list);\n\t/* Test list_add and !list_empty. */\n\tok1(!list_empty(&parent.children));\n\tok1(c2.list.next == &parent.children.n);\n\tok1(c2.list.prev == &c1.list);\n\tok1(parent.children.n.next == &c1.list);\n\tok1(parent.children.n.prev == &c2.list);\n\tok1(c1.list.next == &c2.list);\n\tok1(c1.list.prev == &parent.children.n);\n\t/* Test list_check */\n\tok1(list_check(&parent.children, NULL));\n\n\tc3.name = \"c3\";\n\tlist_add_tail(&parent.children, &c3.list);\n\t/* Test list_add_tail and !list_empty. */\n\tok1(!list_empty(&parent.children));\n\tok1(parent.children.n.next == &c1.list);\n\tok1(parent.children.n.prev == &c3.list);\n\tok1(c1.list.next == &c2.list);\n\tok1(c1.list.prev == &parent.children.n);\n\tok1(c2.list.next == &c3.list);\n\tok1(c2.list.prev == &c1.list);\n\tok1(c3.list.next == &parent.children.n);\n\tok1(c3.list.prev == &c2.list);\n\t/* Test list_check */\n\tok1(list_check(&parent.children, NULL));\n\n\t/* Test list_check_node */\n\tok1(list_check_node(&c1.list, NULL));\n\tok1(list_check_node(&c2.list, NULL));\n\tok1(list_check_node(&c3.list, NULL));\n\n\t/* Test list_top */\n\tok1(list_top(&parent.children, struct child, list) == &c1);\n\n\t/* Test list_pop */\n\tok1(list_pop(&parent.children, struct child, list) == &c1);\n\tok1(list_top(&parent.children, struct child, list) == &c2);\n\tlist_add(&parent.children, &c1.list);\n\n\t/* Test list_tail */\n\tok1(list_tail(&parent.children, struct child, list) == &c3);\n\n\t/* Test list_for_each. */\n\ti = 0;\n\tlist_for_each(&parent.children, c, list) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(c == &c1);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(c == &c2);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c3);\n\t\t\tbreak;\n\t\t}\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\n\t/* Test list_for_each_rev. */\n\ti = 0;\n\tlist_for_each_rev(&parent.children, c, list) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(c == &c3);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(c == &c2);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c1);\n\t\t\tbreak;\n\t\t}\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\n\t/* Test list_for_each_safe, list_del and list_del_from. */\n\ti = 0;\n\tlist_for_each_safe(&parent.children, c, n, list) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(c == &c1);\t\n\t\t\tlist_del(&c->list);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(c == &c2);\n\t\t\tlist_del_from(&parent.children, &c->list);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c3);\n\t\t\tlist_del_from(&parent.children, &c->list);\n\t\t\tbreak;\n\t\t}\n\t\tok1(list_check(&parent.children, NULL));\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\tok1(list_empty(&parent.children));\n\n\t/* Test list_for_each_off. */\n\tlist_add_tail(&opaque_list,\n\t\t      (struct list_node *)create_opaque_blob());\n\tlist_add_tail(&opaque_list,\n\t\t      (struct list_node *)create_opaque_blob());\n\tlist_add_tail(&opaque_list,\n\t\t      (struct list_node *)create_opaque_blob());\n\n\ti = 0;\n\n\tlist_for_each_off(&opaque_list, q, 0) {\n\t  i++;\n\t  ok1(if_blobs_know_the_secret(q));\n\t}\n\tok1(i == 3);\n\n\t/* Test list_for_each_safe_off, list_del_off and list_del_from_off. */\n\ti = 0;\n\tlist_for_each_safe_off(&opaque_list, q, nq, 0) {\n\t\tswitch (i++) {\n\t\tcase 0:\n\t\t\tok1(if_blobs_know_the_secret(q));\n\t\t\tlist_del_off(q, 0);\n\t\t\tdestroy_opaque_blob(q);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tok1(if_blobs_know_the_secret(q));\n\t\t\tlist_del_from_off(&opaque_list, q, 0);\n\t\t\tdestroy_opaque_blob(q);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tok1(c == &c3);\n\t\t\tlist_del_from_off(&opaque_list, q, 0);\n\t\t\tdestroy_opaque_blob(q);\n\t\t\tbreak;\n\t\t}\n\t\tok1(list_check(&opaque_list, NULL));\n\t\tif (i > 2)\n\t\t\tbreak;\n\t}\n\tok1(i == 3);\n\tok1(list_empty(&opaque_list));\n\n\t/* Test list_top/list_tail/list_pop on empty list. */\n\tok1(list_top(&parent.children, struct child, list) == NULL);\n\tok1(list_tail(&parent.children, struct child, list) == NULL);\n\tok1(list_pop(&parent.children, struct child, list) == NULL);\n\treturn exit_status();\n}\n"
  },
  {
    "path": "build-tools/final/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nARG base_IMAGE_TAG\nARG libuv_IMAGE_TAG\nARG aws_sdk_IMAGE_TAG\nARG cpp_misc_IMAGE_TAG\nARG libmaxminddb_IMAGE_TAG\nARG libbpf_IMAGE_TAG\n#gen:dep-arg\n\nFROM $base_IMAGE_TAG as build-main\nFROM $libuv_IMAGE_TAG as build-libuv\nFROM $aws_sdk_IMAGE_TAG as build-aws-sdk\nFROM $cpp_misc_IMAGE_TAG as build-cpp-misc\nFROM $libmaxminddb_IMAGE_TAG as build-libmaxminddb\nFROM $libbpf_IMAGE_TAG as build-libbpf\n#gen:dep-from\n\n# Bring everything together\nFROM build-main AS build-result\n\nARG RUST_TOOLCHAIN=stable\n\n# Package definitions\n# Include fuse-overlayfs so rootless podman can run on overlayfs (e.g., in containers)\nARG PKG_DOCKER=\"podman uidmap fuse-overlayfs\"\nARG PKG_KERNEL_TOOLS=\"kmod selinux-utils\"\nARG PKG_CORE_TOOLS=\"pass\"\nARG PKG_DEV_TOOLS=\"vim-nox lsof silversearcher-ag ssh\"\nARG PKG_AWS_TOOLS=\"awscli\"\nARG BENV_JAVA_VERSION=21\nARG PKG_EXTRA_PACKAGES=\"openjdk-${BENV_JAVA_VERSION}-jdk-headless google-cloud-sdk google-cloud-sdk-skaffold\"\nARG PKG_PYTHON_LIBS=\"python3-ijson python3-docker\"\n\nRUN sudo sh -c 'echo \"deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main\" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list' && \\\n    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo tee /usr/share/keyrings/cloud.google.gpg && \\\n    sudo apt-get -y update && \\\n    sudo apt-get install --no-install-recommends -y \\\n        $PKG_DOCKER \\\n        $PKG_KERNEL_TOOLS \\\n        $PKG_CORE_TOOLS \\\n        $PKG_DEV_TOOLS \\\n        $PKG_AWS_TOOLS \\\n        $PKG_EXTRA_PACKAGES \\\n        $PKG_PYTHON_LIBS \\\n        libcap2-bin && \\\n    # START fix podman permissions -- see comment below \\\n    sudo chmod 0755 /usr/bin/newuidmap /usr/bin/newgidmap && \\\n    sudo setcap cap_setuid=ep /usr/bin/newuidmap && \\\n    sudo setcap cap_setgid=ep /usr/bin/newgidmap && \\\n    sudo apt-get autoremove --purge -y libcap2-bin && \\\n    # END fix podman permissions \\\n    sudo apt-get clean && \\\n    sudo rm -rf /var/lib/apt/lists/*\n\n# Ensure podman uses fuse-overlayfs in rootless environments where kernel overlay is unavailable\nRUN mkdir -p $HOME/.config/containers && \\\n    printf \"[storage]\\n\" > $HOME/.config/containers/storage.conf && \\\n    printf \"driver = \\\"overlay\\\"\\n\" >> $HOME/.config/containers/storage.conf && \\\n    printf \"[storage.options]\\n\" >> $HOME/.config/containers/storage.conf && \\\n    printf \"mount_program = \\\"/usr/bin/fuse-overlayfs\\\"\\n\" >> $HOME/.config/containers/storage.conf\n\n# For info on the fix to podman in container, see https://samuel.forestier.app/blog/security/podman-rootless-in-podman-rootless-the-debian-way\n# Replace setuid bits by proper file capabilities for uidmap binaries.\n# See <https://github.com/containers/podman/discussions/19931>.\n\n## java version required by render framework parser\nRUN case $(uname -m) in \\\n      x86_64) sudo update-alternatives --set java /usr/lib/jvm/java-${BENV_JAVA_VERSION}-openjdk-amd64/bin/java && \\\n              sudo update-alternatives --set javac /usr/lib/jvm/java-${BENV_JAVA_VERSION}-openjdk-amd64/bin/javac \\\n              ;; \\\n      aarch64) sudo update-alternatives --set java /usr/lib/jvm/java-${BENV_JAVA_VERSION}-openjdk-arm64/bin/java  && \\\n              sudo update-alternatives --set javac /usr/lib/jvm/java-${BENV_JAVA_VERSION}-openjdk-arm64/bin/javac \\\n              ;; \\\n    esac\n\n# gradle\nRUN sudo wget https://services.gradle.org/distributions/gradle-8.14.3-bin.zip -O /usr/local/lib/gradle.zip\n\n# Rust toolchain for building Rust components\nRUN curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain ${RUST_TOOLCHAIN} && \\\n    . \"$HOME/.cargo/env\" && \\\n    rustup component add rustfmt clippy && \\\n    cargo install cxxbridge-cmd\nENV PATH=\"${HOME}/.cargo/bin:${PATH}\"\n\n# Preprocessor for BPF used by cmake\nRUN pip3 install --break-system-packages pcpp\n\n# install GitHub's gh CLI\nRUN (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \\\n\t&& sudo mkdir -p -m 755 /etc/apt/keyrings \\\n\t&& out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \\\n\t&& cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \\\n\t&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \\\n\t&& sudo mkdir -p -m 755 /etc/apt/sources.list.d \\\n\t&& echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \\\n\t&& sudo apt update \\\n\t&& sudo apt install gh -y \\\n    && sudo apt-get clean \\\n    && sudo rm -rf /var/lib/apt/lists/*\n\n# add a script to setup build inside of container\n# to be run after we build the image.\nRUN ln -s $HOME/src/dev/benv-build.sh build.sh\n\n# Licensing information\n#\nCOPY LICENSE.txt $HOME/\nCOPY NOTICE.txt $HOME/\n\n# Create a link in $HOME/install to /install so any values in compiled\n# projects that mention $HOME/install are still valid\nRUN ln -s /install $HOME/install\n\n# copy artifacts from individual builds\nCOPY --from=build-libuv $HOME/install /install\nCOPY --from=build-aws-sdk $HOME/install /install\nCOPY --from=build-cpp-misc $HOME/install /install\nCOPY --from=build-libmaxminddb $HOME/install /install\nCOPY --from=build-libbpf $HOME/install /install\n#gen:dep-copy\n\nARG BENV_UNMINIMIZE=false\nRUN (which unminimize && $BENV_UNMINIMIZE && (yes | sudo unminimize)) || true\n\nRUN echo 'if [ -e \"$HOME/src/dev/build-env/profile\" ]; then source \"$HOME/src/dev/build-env/profile\"; fi' >> $HOME/.profile\n"
  },
  {
    "path": "build-tools/final/LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "build-tools/final/NOTICE.txt",
    "content": "OpenTelemetry-eBPF Build Tools\n==============================\n\nhttps://github.com/open-telemetry/opentelemetry-ebpf-build-tools\n\nCopyright The OpenTelemetry Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\nThird party software\n====================\n\n\n-------------------------------------------------------------------------------\nargs\n\nhttps://github.com/Taywee/args\n\nCopyright (c) 2016-2017 Taylor C. Richberger <taywee@gmx.com> and Pavel Belikov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nspdlog\n\nhttps://github.com/gabime/spdlog\n\nCopyright (c) 2016 Gabi Melman.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nares\n\nhttps://github.com/c-ares/c-ares\n\n/* Copyright 1998 by the Massachusetts Institute of Technology.\n * Copyright (C) 2007-2013 by Daniel Stenberg\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n\n-------------------------------------------------------------------------------\nbcc\n\nhttps://github.com/iovisor/bcc\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\nabseil\n\nhttps://github.com/abseil/abseil-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\njson\n\nhttps://github.com/nlohmann/json\n\nCopyright (c) 2013-2018 Niels Lohmann\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\n-------------------------------------------------------------------------------\nlookup3\n\nhttp://www.burtleburtle.net/bob/c/lookup3.c\n\nlookup3.c, by Bob Jenkins, May 2006, Public Domain.\n\n\n-------------------------------------------------------------------------------\nlibuv\n\nhttps://github.com/libuv/libuv\n\nlibuv is licensed for use as follows:\n\n====\nCopyright (c) 2015-present libuv project contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n====\n\nThis license applies to parts of libuv originating from the\nhttps://github.com/joyent/libuv repository:\n\n====\n\nCopyright Joyent, Inc. and other Node contributors. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n\n====\n\nThis license applies to all parts of libuv that are not externally\nmaintained libraries.\n\nThe externally maintained libraries used by libuv are:\n\n  - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license.\n\n  - inet_pton and inet_ntop implementations, contained in src/inet.c, are\n    copyright the Internet Systems Consortium, Inc., and licensed under the ISC\n    license.\n\n  - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three\n    clause BSD license.\n\n  - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB.\n    Three clause BSD license.\n\n  - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design\n    Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement\n    n° 289016). Three clause BSD license.\n\n\n-------------------------------------------------------------------------------\nopenssl\n\nhttps://github.com/openssl/openssl\n\n/* ====================================================================\n * Copyright (c) 1998-2019 The OpenSSL Project.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * 3. All advertising materials mentioning features or use of this\n *    software must display the following acknowledgment:\n *    \"This product includes software developed by the OpenSSL Project\n *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n *\n * 4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to\n *    endorse or promote products derived from this software without\n *    prior written permission. For written permission, please contact\n *    openssl-core@openssl.org.\n *\n * 5. Products derived from this software may not be called \"OpenSSL\"\n *    nor may \"OpenSSL\" appear in their names without prior written\n *    permission of the OpenSSL Project.\n *\n * 6. Redistributions of any form whatsoever must retain the following\n *    acknowledgment:\n *    \"This product includes software developed by the OpenSSL Project\n *    for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n *\n * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY\n * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR\n * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n * OF THE POSSIBILITY OF SUCH DAMAGE.\n * ====================================================================\n *\n * This product includes cryptographic software written by Eric Young\n * (eay@cryptsoft.com).  This product includes software written by Tim\n * Hudson (tjh@cryptsoft.com).\n *\n */\n\n Original SSLeay License\n -----------------------\n\n/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\n * All rights reserved.\n *\n * This package is an SSL implementation written\n * by Eric Young (eay@cryptsoft.com).\n * The implementation was written so as to conform with Netscapes SSL.\n *\n * This library is free for commercial and non-commercial use as long as\n * the following conditions are aheared to.  The following conditions\n * apply to all code found in this distribution, be it the RC4, RSA,\n * lhash, DES, etc., code; not just the SSL code.  The SSL documentation\n * included with this distribution is covered by the same copyright terms\n * except that the holder is Tim Hudson (tjh@cryptsoft.com).\n *\n * Copyright remains Eric Young's, and as such any Copyright notices in\n * the code are not to be removed.\n * If this package is used in a product, Eric Young should be given attribution\n * as the author of the parts of the library used.\n * This can be in the form of a textual message at program startup or\n * in documentation (online or textual) provided with the package.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. All advertising materials mentioning features or use of this software\n *    must display the following acknowledgement:\n *    \"This product includes cryptographic software written by\n *     Eric Young (eay@cryptsoft.com)\"\n *    The word 'cryptographic' can be left out if the rouines from the library\n *    being used are not cryptographic related :-).\n * 4. If you include any Windows specific code (or a derivative thereof) from\n *    the apps directory (application code) you must include an acknowledgement:\n *    \"This product includes software written by Tim Hudson (tjh@cryptsoft.com)\"\n *\n * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * The licence and distribution terms for any publically available version or\n * derivative of this code cannot be changed.  i.e. this code cannot simply be\n * copied and put under another distribution licence\n * [including the GNU Public Licence.]\n */\n\n\n-------------------------------------------------------------------------------\ngrpc\n\nhttps://github.com/grpc/grpc\n\nCopyright 2014 gRPC authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n-------------------------------------------------------------------------------\ncurl\n\nhttps://github.com/curl/curl\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright (c) 1996 - 2017, Daniel Stenberg, <daniel@haxx.se>, and many\ncontributors, see the THANKS file.\n\nAll rights reserved.\n\nPermission to use, copy, modify, and distribute this software for any purpose\nwith or without fee is hereby granted, provided that the above copyright\nnotice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\nOR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall not\nbe used in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization of the copyright holder.\n\n\n-------------------------------------------------------------------------------\ncurlpp\n\nhttps://github.com/jpbarrette/curlpp\n\nCopyright (c) <2002-2004> <Jean-Philippe Barrette-LaPierre>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files\n(cURLpp), to deal in the Software without restriction,\nincluding without limitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of the Software,\nand to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\naws-sdk-cpp\n\nhttps://github.com/aws/aws-sdk-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\ngoogle-cloud-cpp\n\nhttps://github.com/googleapis/google-cloud-cpp\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\nccan\n\nhttps://github.com/rustyrussell/ccan\n\nThe list module is licensed under BSD-MIT:\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy\n  of this software and associated documentation files (the \"Software\"), to deal\n  in the Software without restriction, including without limitation the rights\n  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  copies of the Software, and to permit persons to whom the Software is\n  furnished to do so, subject to the following conditions:\n\n  The above copyright notice and this permission notice shall be included in\n  all copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n  THE SOFTWARE.\n\n\nThe build_assert, check_type, compiler, container_of and hash modules are\nlicensed under CC0 (Public domain):\n\n  Statement of Purpose\n\n  The laws of most jurisdictions throughout the world automatically confer\n  exclusive Copyright and Related Rights (defined below) upon the creator and\n  subsequent owner(s) (each and all, an \"owner\") of an original work of\n  authorship and/or a database (each, a \"Work\").\n\n  Certain owners wish to permanently relinquish those rights to a Work for the\n  purpose of contributing to a commons of creative, cultural and scientific\n  works (\"Commons\") that the public can reliably and without fear of later\n  claims of infringement build upon, modify, incorporate in other works, reuse\n  and redistribute as freely as possible in any form whatsoever and for any\n  purposes, including without limitation commercial purposes. These owners may\n  contribute to the Commons to promote the ideal of a free culture and the\n  further production of creative, cultural and scientific works, or to gain\n  reputation or greater distribution for their Work in part through the use and\n  efforts of others.\n\n  For these and/or other purposes and motivations, and without any expectation\n  of additional consideration or compensation, the person associating CC0 with a\n  Work (the \"Affirmer\"), to the extent that he or she is an owner of Copyright\n  and Related Rights in the Work, voluntarily elects to apply CC0 to the Work\n  and publicly distribute the Work under its terms, with knowledge of his or her\n  Copyright and Related Rights in the Work and the meaning and intended legal\n  effect of CC0 on those rights.\n\n  1. Copyright and Related Rights. A Work made available under CC0 may be\n  protected by copyright and related or neighboring rights (\"Copyright and\n  Related Rights\"). Copyright and Related Rights include, but are not limited\n  to, the following:\n\n    the right to reproduce, adapt, distribute, perform, display, communicate,\n    and translate a Work;\n\n    moral rights retained by the original author(s) and/or performer(s);\n\n    publicity and privacy rights pertaining to a person's image or likeness\n    depicted in a Work;\n\n    rights protecting against unfair competition in regards to a Work, subject\n    to the limitations in paragraph 4(a), below;\n\n    rights protecting the extraction, dissemination, use and reuse of data in a\n    Work;\n\n    database rights (such as those arising under Directive 96/9/EC of the\n    European Parliament and of the Council of 11 March 1996 on the legal\n    protection of databases, and under any national implementation thereof,\n    including any amended or successor version of such directive);\n\n    and other similar, equivalent or corresponding rights throughout the world\n    based on applicable law or treaty, and any national implementations thereof.\n\n  2. Waiver. To the greatest extent permitted by, but not in contravention of,\n  applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and\n  unconditionally waives, abandons, and surrenders all of Affirmer's Copyright\n  and Related Rights and associated claims and causes of action, whether now\n  known or unknown (including existing as well as future claims and causes of\n  action), in the Work (i) in all territories worldwide, (ii) for the maximum\n  duration provided by applicable law or treaty (including future time\n  extensions), (iii) in any current or future medium and for any number of\n  copies, and (iv) for any purpose whatsoever, including without limitation\n  commercial, advertising or promotional purposes (the \"Waiver\"). Affirmer makes\n  the Waiver for the benefit of each member of the public at large and to the\n  detriment of Affirmer's heirs and successors, fully intending that such Waiver\n  shall not be subject to revocation, rescission, cancellation, termination, or\n  any other legal or equitable action to disrupt the quiet enjoyment of the Work\n  by the public as contemplated by Affirmer's express Statement of Purpose.\n\n  3. Public License Fallback. Should any part of the Waiver for any reason be\n  judged legally invalid or ineffective under applicable law, then the Waiver\n  shall be preserved to the maximum extent permitted taking into account\n  Affirmer's express Statement of Purpose. In addition, to the extent the Waiver\n  is so judged Affirmer hereby grants to each affected person a royalty-free,\n  non transferable, non sublicensable, non exclusive, irrevocable and\n  unconditional license to exercise Affirmer's Copyright and Related Rights in\n  the Work (i) in all territories worldwide, (ii) for the maximum duration\n  provided by applicable law or treaty (including future time extensions), (iii)\n  in any current or future medium and for any number of copies, and (iv) for any\n  purpose whatsoever, including without limitation commercial, advertising or\n  promotional purposes (the \"License\"). The License shall be deemed effective as\n  of the date CC0 was applied by Affirmer to the Work. Should any part of the\n  License for any reason be judged legally invalid or ineffective under\n  applicable law, such partial invalidity or ineffectiveness shall not\n  invalidate the remainder of the License, and in such case Affirmer hereby\n  affirms that he or she will not (i) exercise any of his or her remaining\n  Copyright and Related Rights in the Work or (ii) assert any associated claims\n  and causes of action with respect to the Work, in either case contrary to\n  Affirmer's express Statement of Purpose.\n\n  4. Limitations and Disclaimers.\n\n    No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n\n    Affirmer offers the Work as-is and makes no representations or warranties of\n    any kind concerning the Work, express, implied, statutory or otherwise,\n    including without limitation warranties of title, merchantability, fitness\n    for a particular purpose, non infringement, or the absence of latent or\n    other defects, accuracy, or the present or absence of errors, whether or not\n    discoverable, all to the greatest extent permissible under applicable law.\n\n    Affirmer disclaims responsibility for clearing rights of other persons that\n    may apply to the Work or any use thereof, including without limitation any\n    person's Copyright and Related Rights in the Work. Further, Affirmer\n    disclaims responsibility for obtaining any necessary consents, permissions\n    or other rights required for any use of the Work.\n\n    Affirmer understands and acknowledges that Creative Commons is not a party\n    to this document and has no duty or obligation with respect to this CC0 or\n    use of the Work.\n\n\n-------------------------------------------------------------------------------\nlz4\n\nhttps://github.com/lz4/lz4\n\nThis repository uses 2 different licenses :\n- all files in the `lib` directory use a BSD 2-Clause license\n- all other files use a GPLv2 license, unless explicitly stated otherwise\n\nRelevant license is reminded at the top of each source file,\nand with presence of COPYING or LICENSE file in associated directories.\n\nThis model is selected to emphasize that\nfiles in the `lib` directory are designed to be included into 3rd party applications,\nwhile all other files, in `programs`, `tests` or `examples`,\nreceive more limited attention and support for such scenario.\n\n\n-------------------------------------------------------------------------------\nyaml-cpp\n\nhttps://github.com/jbeder/yaml-cpp\n\nCopyright (c) 2008-2015 Jesse Beder.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n-------------------------------------------------------------------------------\nlibmaxminddb\n\nhttps://github.com/maxmind/libmaxminddb\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n-------------------------------------------------------------------------------\ngradle\n\nhttps://github.com/gradle/gradle\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "build-tools/get_tag.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# This script gets a tag for the given directory (DIR=$1) from the latest git modification.\n# The image+tag will be ${BENV_PREFIX}-${DIR}:${VERSION_HASH}\n\nSCRIPTDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nDIR=\"$1\"\nBENV_PREFIX=\"${DOCKER_TAG_PREFIX}benv\"\n\nVERSION_HASH=$(git log -1 --format=%h $SCRIPTDIR/${DIR})\n\nIMAGE_TAG=\"${BENV_PREFIX}-${DIR}:${VERSION_HASH}\"\n\necho ${IMAGE_TAG}\n"
  },
  {
    "path": "build-tools/libbpf/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# compile our own libbpf\n\nARG base_IMAGE_TAG\nFROM $base_IMAGE_TAG AS build\n\nARG CMAKE_BUILD_TYPE\nARG RESTRICTED_NPROC\n\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} libbpf libbpf\nCOPY --chown=${UID}:${GID} bpftool bpftool\n\n# Build libbpf first\nWORKDIR $HOME/libbpf/src\nRUN make -j ${RESTRICTED_NPROC:-1} DESTDIR=$HOME/install install\n\n# Build bpftool (it will use the libbpf we just built)\nWORKDIR $HOME/bpftool/src\nRUN make -j ${RESTRICTED_NPROC:-1} DESTDIR=$HOME/install install\n\n# Runtime stage - copy only necessary artifacts\nFROM $base_IMAGE_TAG\nCOPY --from=build $HOME/install $HOME/install"
  },
  {
    "path": "build-tools/libmaxminddb/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# libmaxminddb\n\nARG base_IMAGE_TAG\nFROM $base_IMAGE_TAG AS build\n\nARG CONFIGURE_ENABLE_DEBUG\nARG NPROC\n\nWORKDIR $HOME\nCOPY --chown=${UID}:${GID} libmaxminddb libmaxminddb\nWORKDIR $HOME/libmaxminddb\nRUN ./bootstrap\nRUN ./configure ${CONFIGURE_ENABLE_DEBUG} --prefix=$HOME/install \\\n    --enable-static\nRUN nice make -j${NPROC:-3} && make install\n\n# Runtime stage - copy only necessary artifacts\nFROM $base_IMAGE_TAG\nCOPY --from=build $HOME/install $HOME/install\n"
  },
  {
    "path": "build-tools/libuv/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# libuv\n\nARG base_IMAGE_TAG\nFROM $base_IMAGE_TAG AS build\n\nARG NPROC\nARG BUILD_CFLAGS\n\nWORKDIR $HOME\nRUN sudo apt remove -y libuv1\nCOPY --chown=${UID}:${GID} libuv libuv\nWORKDIR $HOME/libuv\nRUN ./autogen.sh\nWORKDIR $HOME/build/libuv\nRUN $HOME/libuv/configure --prefix=$HOME/install\nRUN CFLAGS=`echo ${BUILD_CFLAGS} | sed 's/\\\\\\\\ / /g'`; nice make -j${NPROC:-3} && make install\n\n# Runtime stage - copy only necessary artifacts\nFROM $base_IMAGE_TAG\nCOPY --from=build $HOME/install $HOME/install\n"
  },
  {
    "path": "build-tools/nproc.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nif which nproc > /dev/null; then\n  echo \"$(((`nproc --all` + 1) / 2))\"\nelif which sysctl > /dev/null; then\n  sysctl -n hw.physicalcpu\nelse\n  # some default value - erring on the conservative side to avoid cache trashing\n  echo 2\nfi\n"
  },
  {
    "path": "channel/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_library(\n  file_channel\n  STATIC\n    file_channel.cc\n)\ntarget_link_libraries(\n  file_channel\n    file_ops\n    logging\n)\n\nadd_library(\n  double_write_channel\n  STATIC\n    double_write_channel.cc\n)\ntarget_link_libraries(\n  double_write_channel\n    logging\n)\n\nadd_library(\n  buffered_writer\n  STATIC\n    buffered_writer.cc\n)\ntarget_link_libraries(\n  buffered_writer\n    logging\n)\n\nadd_library(\n  tcp_channel\n  STATIC\n    tcp_channel.cc\n)\ntarget_link_libraries(\n  tcp_channel\n    error_handling\n    uv_helpers\n    libuv-interface\n    logging\n)\n\nadd_library(\n  lz4_channel\n  STATIC\n    lz4_channel.cc\n)\ntarget_link_libraries(\n  lz4_channel\n    logging\n    lz4\n)\n\nadd_library(\n  upstream_connection\n  STATIC\n    upstream_connection.cc\n)\ntarget_link_libraries(\n  upstream_connection\n    double_write_channel\n    lz4_channel\n    buffered_writer\n    logging\n)\n\nadd_library(\n  reconnecting_channel\n  STATIC\n    reconnecting_channel.cc\n)\n\ntarget_link_libraries(\n  reconnecting_channel\n    upstream_connection\n    spdlog\n    libuv-interface\n    render_ebpf_net_artifacts\n)\n\nadd_library(\n  connection_caretaker\n  STATIC\n    connection_caretaker.cc\n)\ntarget_link_libraries(\n  connection_caretaker\n    agent_id\n    aws_instance_metadata\n    gcp_instance_metadata\n    intake_config\n    element_queue_writer\n    fastpass_util\n    render_ebpf_net_artifacts\n    logging\n    tcp_channel\n    absl::strings\n    versions\n)\n\nadd_library(\n  test_channel\n  STATIC\n    test_channel.cc\n)\ntarget_link_libraries(\n  test_channel\n    render_ebpf_net_ingest\n    json\n    llvm\n    logging\n)\n\nadd_unit_test(buffered_writer LIBS buffered_writer element_queue_writer llvm)\n"
  },
  {
    "path": "channel/buffered_writer.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/buffered_writer.h>\n#include <channel/channel.h>\n#include <util/log.h>\n\n#include <stdexcept>\n\nnamespace channel {\n\nBufferedWriter::BufferedWriter(Channel &channel, u32 buf_size)\n    : buf_size_(buf_size), write_start_loc_(0), write_finish_loc_(0), channel_(channel)\n{\n  buf_ = (u8 *)malloc(buf_size * sizeof(u8));\n  if (buf_ == NULL)\n    throw std::runtime_error(\"BufferedWriter: failed to allocate memory\\n\");\n}\n\nBufferedWriter::~BufferedWriter()\n{\n  /* if we're in a consistent state, try flushing the buffer */\n  if (write_start_loc_ == write_finish_loc_) {\n    flush();\n  }\n\n  free(buf_);\n}\n\nExpected<u8 *, std::error_code> BufferedWriter::start_write(u32 length)\n{\n  /* if requesting more than buffer maximum size, bad request */\n  if (length > buf_size_) {\n    LOG::error(\n        \"BufferedWriter::start_write: requesting more than buffer maximum size\"\n        \" (requested={}, buf_size={})\",\n        length,\n        buf_size_);\n    return {unexpected, std::make_error_code(std::errc::no_buffer_space)};\n  }\n\n  /* is there enough space in the current buffer? */\n  if (buf_size_ - write_start_loc_ < length) {\n    if (auto error = flush()) {\n      LOG::error(\n          \"BufferedWriter::start_write: failed to flush the channel and there's\"\n          \" not enough space in the current buffer to return - check for channel\"\n          \" errors prior to this one (requested={}, buf_size={} offset={})\",\n          length,\n          buf_size_,\n          write_start_loc_);\n      return {unexpected, error};\n    }\n  }\n  assert(buf_size_ - write_start_loc_ >= length);\n\n  /* mark the end of the write */\n  write_finish_loc_ = write_start_loc_ + length;\n\n  /* return a pointer to the start of the write */\n  return &buf_[write_start_loc_];\n}\n\nvoid BufferedWriter::finish_write()\n{\n  write_start_loc_ = write_finish_loc_;\n}\n\nstd::error_code BufferedWriter::flush()\n{\n  /* we shouldn't be in the middle of a write */\n  assert(write_start_loc_ == write_finish_loc_);\n\n  if (write_start_loc_ == 0) {\n    return {};\n  }\n\n  // TODO: it should never throw\n  try {\n    if (is_writable()) {\n      if (auto error = channel_.send(buf_, write_start_loc_)) {\n        return error;\n      }\n    }\n  } catch (...) {\n    return std::make_error_code(std::errc::invalid_argument);\n  }\n\n  write_start_loc_ = write_finish_loc_ = 0;\n  return {};\n}\n\nvoid BufferedWriter::reset()\n{\n  write_start_loc_ = write_finish_loc_ = 0;\n}\n\nu32 BufferedWriter::buf_size() const\n{\n  return buf_size_;\n}\n\nbool BufferedWriter::is_writable() const\n{\n  return channel_.is_open();\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/buffered_writer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/ibuffered_writer.h>\n#include <platform/platform.h>\n\nnamespace channel {\n\nclass Channel;\n\n/**\n * A class that enables writing through a buffer so send() calls don't have to\n * happen for every message. When buffers are exhausted, they are sent into\n * the given Channel.\n */\nclass BufferedWriter : public ::IBufferedWriter {\npublic:\n  /**\n   * c'tor\n   * throws if buff_ can't be malloc-ed\n   * @param channel: the channel on which to send messages\n   * @param buffsize: how many bytes used to batch the sent messages\n   */\n  BufferedWriter(Channel &channel, u32 buf_size);\n\n  /**\n   * d'tor\n   */\n  virtual ~BufferedWriter();\n\n  /**\n   * batches as many entries into buffer as possible before calling\n   * send_buffer(). always flushes the buffer at the end.\n   * @see PerfPoller::poll\n   *\n   * @returns: on success, where caller should write the data. nullptr when\n   *   the requested length is larger than buf_size_, or if call to\n   *   flush() fails.\n   */\n  Expected<u8 *, std::error_code> start_write(u32 length) override;\n\n  /**\n   * Finishes the current write\n   */\n  void finish_write() override;\n\n  /**\n   * Flushes the buffer to the channel if it's non-empty.\n   *\n   * @return -EINVAL if a send() fails, 0 if successful\n   */\n  std::error_code flush() override;\n\n  /**\n   * Abandons the current buffered data\n   */\n  void reset();\n\n  /**\n   * Returns the buffer size\n   */\n  u32 buf_size() const override;\n\n  bool is_writable() const override;\n\nprivate:\n  u8 *buf_;\n  const u32 buf_size_;\n\n  /* where next or active write starts */\n  u32 write_start_loc_;\n  /* where active write will finish */\n  u32 write_finish_loc_;\n\n  Channel &channel_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/buffered_writer_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/buffered_writer.h>\n\n#include <memory>\n\n#include <channel/mock_channel.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\nnamespace {\nusing ::testing::_;\nusing ::testing::IsFalse;\nusing ::testing::IsTrue;\nusing ::testing::NotNull;\nusing ::testing::Return;\nusing ::testing::Test;\n\nstatic constexpr u32 default_buffer_size = 32;\n\nclass BufferedWriterTest : public Test {\nprotected:\n  void SetUp() override { writer_.reset(new channel::BufferedWriter(mock_channel_, default_buffer_size)); }\n\n  ::channel::MockChannel mock_channel_;\n  std::unique_ptr<channel::BufferedWriter> writer_;\n};\n\nTEST_F(BufferedWriterTest, empty_writer)\n{\n  EXPECT_CALL(mock_channel_, send(_, _)).Times(0);\n\n  EXPECT_EQ(writer_->buf_size(), default_buffer_size);\n}\n\nTEST_F(BufferedWriterTest, one_flush)\n{\n  ON_CALL(mock_channel_, is_open()).WillByDefault(Return(true));\n  EXPECT_CALL(mock_channel_, send(_, 24)).Times(1);\n\n  EXPECT_THAT(writer_->start_write(24), IsTrue());\n  EXPECT_THAT(*writer_->start_write(24), NotNull());\n  writer_->finish_write();\n\n  EXPECT_THAT(writer_->flush(), IsFalse());\n}\n\n} // namespace\n"
  },
  {
    "path": "channel/callbacks.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\nnamespace channel {\n\nclass Callbacks {\npublic:\n  /**\n   * virtual d'tor\n   */\n  virtual ~Callbacks() {}\n\n  /**\n   * Callback with ready data.\n   *\n   * The default implementation ignores received data.\n   *\n   * @returns how many bytes were consumed\n   */\n  virtual u32 received_data(u8 const *data, int length) { return length; }\n\n  u32 received_data(std::basic_string_view<u8> data) { return received_data(data.data(), data.size()); }\n\n  u32 received_data(std::string_view data) { return received_data(reinterpret_cast<u8 const *>(data.data()), data.size()); }\n\n  /**\n   * An error occurred on the channel, or -ENOLINK on EOF\n   */\n  virtual void on_error(int error) {}\n\n  /**\n   * The link finished closing\n   */\n  virtual void on_closed() {}\n\n  /**\n   * Connected\n   */\n  virtual void on_connect() {}\n};\n\n} /* namespace channel */\n"
  },
  {
    "path": "channel/channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <string_view>\n#include <system_error>\n\nnamespace channel {\n\n/**\n * An interface that allows reading and writing data to a pipe/socket/etc\n */\nclass Channel {\npublic:\n  /**\n   * Virtual d'tor\n   */\n  virtual ~Channel() {}\n\n  /**\n   * Sends data onto the channel.\n   */\n  virtual std::error_code send(const u8 *data, int data_len) = 0;\n\n  inline std::error_code send(std::basic_string_view<u8> data) { return send(data.data(), data.size()); }\n\n  inline std::error_code send(std::string_view data) { return send(reinterpret_cast<u8 const *>(data.data()), data.size()); }\n\n  /**\n   * Flushes any internal buffers.\n   */\n  virtual std::error_code flush() { return {}; }\n\n  /**\n   * Closes the channel.\n   */\n  virtual void close() {}\n\n  virtual bool is_open() const = 0;\n};\n\n} /* namespace channel */\n"
  },
  {
    "path": "channel/component.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE channel\n#define ENUM_NAME Component\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(tls, 1, \"\")                                                                                                                \\\n  X(reconnecting_channel, 2, \"\")                                                                                               \\\n  X(tcp, 3, \"\")                                                                                                                \\\n  X(upstream, 4, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "channel/connection_caretaker.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/connection_caretaker.h>\n\n#include <common/cloud_platform.h>\n#include <common/constants.h>\n#include <util/boot_time.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <absl/strings/match.h>\n\nnamespace channel {\n\nnamespace {\n\nvoid heartbeat_timer_cb(uv_timer_t *timer)\n{\n  auto *caretaker = (ConnectionCaretaker *)(timer->data);\n  caretaker->send_heartbeat();\n}\n\n} // namespace\n\nConnectionCaretaker::ConnectionCaretaker(\n    std::string_view hostname,\n    ClientType client_type,\n    config_labels_t const &config_labels,\n    uv_loop_t *loop,\n    ebpf_net::ingest::Writer &writer,\n    std::chrono::milliseconds metadata_timeout,\n    std::chrono::milliseconds heartbeat_interval,\n    std::function<void()> flush_cb,\n    std::function<void(bool)> set_compression_cb,\n    std::function<void()> on_connected_cb)\n    : hostname_(hostname),\n      client_type_(client_type),\n      config_labels_(config_labels),\n      loop_(loop),\n      heartbeat_interval_(heartbeat_interval),\n      flush_cb_(std::move(flush_cb)),\n      set_compression_cb_(std::move(set_compression_cb)),\n      on_connected_cb_(std::move(on_connected_cb)),\n      writer_(writer)\n{\n  assert(loop != nullptr);\n  assert(heartbeat_interval_.count() > 0);\n\n  LOG::trace_in(CloudPlatform::aws, \"--- resolving AWS metadata ---\");\n  if (auto aws_metadata = AwsMetadata::fetch(metadata_timeout)) {\n    aws_metadata_.emplace(std::move(aws_metadata.value()));\n    aws_metadata_->print_instance_metadata();\n    aws_metadata_->print_interfaces();\n  } else {\n    LOG::warn(\"Unable to fetch AWS metadata: {}\", aws_metadata.error().what());\n  }\n\n  LOG::trace_in(CloudPlatform::gcp, \"--- resolving GCP metadata ---\");\n  if (auto gcp_metadata = GcpInstanceMetadata::fetch(metadata_timeout)) {\n    gcp_metadata_.emplace(std::move(gcp_metadata.value()));\n    gcp_metadata_->print();\n  } else {\n    LOG::warn(\"Unable to fetch GCP metadata: {}\", gcp_metadata.error().what());\n  }\n\n  int res = uv_timer_init(loop_, &heartbeat_timer_);\n  if (res != 0) {\n    throw std::runtime_error(\"Cannot init heartbeat_timer\");\n  }\n  heartbeat_timer_.data = this;\n}\n\nConnectionCaretaker::~ConnectionCaretaker()\n{\n  stop_heartbeat();\n  uv_close((uv_handle_t *)&heartbeat_timer_, NULL);\n}\n\nvoid ConnectionCaretaker::send_metadata_header()\n{\n  set_compression_cb_(false);\n  LOG::info(\"initiating connection of {} collector version {}\", client_type_, versions::release);\n  writer_.version_info(versions::release.major(), versions::release.minor(), versions::release.patch());\n  flush();\n  set_compression_cb_(true);\n\n  writer_.connect(static_cast<u8>(client_type_), jb_blob(hostname_));\n\n  writer_.report_cpu_cores(std::thread::hardware_concurrency());\n\n  flush();\n\n#define make_buf_from_field(struct_name, field, buf_name)                                                                      \\\n  struct struct_name __##struct_name##__##buf_name;                                                                            \\\n  char buf_name[sizeof(__##struct_name##__##buf_name.field)] = {};\n\n  for (auto const &label : config_labels_) {\n    writer_.set_config_label(jb_blob{label.first}, jb_blob{label.second});\n  }\n  flush();\n\n  if (aws_metadata_) {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::aws));\n    if (auto const &account_id = aws_metadata_->account_id()) {\n      LOG::trace_in(CloudPlatform::aws, \"reporting aws account id: {}\", account_id.value());\n      writer_.cloud_platform_account_info(jb_blob{account_id.value()});\n    } else {\n      LOG::trace_in(CloudPlatform::aws, \"no aws account id to report\");\n    }\n\n    auto id = aws_metadata_->id().value();\n    if (id.starts_with(std::string_view(\"i-\"))) {\n      id.remove_prefix(2);\n    }\n\n    writer_.set_node_info(\n        jb_blob{aws_metadata_->az().value()},\n        jb_blob{aws_metadata_->iam_role().value()},\n        jb_blob{id},\n        jb_blob{aws_metadata_->type().value()});\n    flush();\n\n    for (auto const &interface : aws_metadata_->network_interfaces()) {\n      for (auto const &ipv4 : interface.private_ipv4s()) {\n        struct sockaddr_in private_sa;\n        int res = inet_pton(AF_INET, ipv4.c_str(), &(private_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__private_ipv4_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.private_ipv4_addr(private_sa.sin_addr.s_addr, (u8 *)vpc_id_buf);\n      }\n\n      for (auto const &ipv6 : interface.ipv6s()) {\n        struct sockaddr_in6 sa;\n        int res = inet_pton(AF_INET6, ipv6.c_str(), &(sa.sin6_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__ipv6_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.ipv6_addr(sa.sin6_addr.s6_addr, (u8 *)vpc_id_buf);\n      }\n\n      for (auto const &mapped_ipv4 : interface.mapped_ipv4s()) {\n        struct sockaddr_in public_sa;\n        int res = inet_pton(AF_INET, mapped_ipv4.first.c_str(), &(public_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        struct sockaddr_in private_sa;\n        res = inet_pton(AF_INET, mapped_ipv4.second.c_str(), &(private_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__public_to_private_ipv4, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.public_to_private_ipv4(public_sa.sin_addr.s_addr, private_sa.sin_addr.s_addr, (u8 *)vpc_id_buf);\n      }\n    }\n  } else if (gcp_metadata_) {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::gcp));\n    // TODO: obtain account_id for GCP and uncomment below\n    // writer_.cloud_platform_account_info(jb_blob{account_id});\n\n    writer_.set_node_info(\n        jb_blob{gcp_metadata_->az()},\n        jb_blob{gcp_metadata_->role()},\n        jb_blob{gcp_metadata_->hostname()},\n        jb_blob{gcp_metadata_->type()});\n    flush();\n\n    for (auto const &interface : gcp_metadata_->network_interfaces()) {\n      if (auto const ipv4 = interface.ipv4()) {\n        make_buf_from_field(jb_ingest__private_ipv4_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.private_ipv4_addr(ipv4->as_int(), (u8 *)vpc_id_buf);\n\n        for (auto const &public_ip : interface.public_ips()) {\n          make_buf_from_field(jb_ingest__public_to_private_ipv4, vpc_id, vpc_id_buf);\n          strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n          writer_.public_to_private_ipv4(public_ip.as_int(), ipv4->as_int(), (u8 *)vpc_id_buf);\n        }\n      } else if (auto const ipv6 = interface.ipv6()) {\n        uint8_t ipv6_buffer[16];\n        ipv6->write_to(ipv6_buffer);\n        make_buf_from_field(jb_ingest__ipv6_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.ipv6_addr(ipv6_buffer, (u8 *)vpc_id_buf);\n      }\n    }\n  } else {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::unknown));\n\n    writer_.set_node_info(jb_blob{/* az */}, jb_blob{/* role */}, jb_blob{hostname_}, jb_blob{/* instance_type */});\n\n    // no network interface data (public/private ip) to send\n  }\n\n  writer_.metadata_complete(0);\n\n  flush();\n#undef make_buf_from_field\n\n  on_connected_cb_();\n}\n\nvoid ConnectionCaretaker::flush()\n{\n  flush_cb_();\n}\n\nvoid ConnectionCaretaker::start_heartbeat()\n{\n  int res = uv_timer_start(&heartbeat_timer_, heartbeat_timer_cb, heartbeat_interval_.count(), heartbeat_interval_.count());\n\n  if (res != 0) {\n    LOG::error(\"Cannot start heartbeat_timer: {}\", uv_err_name(res));\n  }\n}\n\nvoid ConnectionCaretaker::stop_heartbeat()\n{\n  uv_timer_stop(&heartbeat_timer_);\n}\n\nvoid ConnectionCaretaker::send_heartbeat()\n{\n  LOG::debug(\"sending heartbeat for {} collector\", client_type_);\n  if (writer_.is_writable()) {\n    writer_.heartbeat();\n    flush();\n  }\n}\n\nvoid ConnectionCaretaker::set_connected()\n{\n  LOG::info(\"collector {} connected to host\", hostname_);\n  send_metadata_header();\n  start_heartbeat();\n}\n\nvoid ConnectionCaretaker::set_disconnected()\n{\n  LOG::info(\"collector {} disconnected from host\", hostname_);\n  stop_heartbeat();\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/connection_caretaker.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/callbacks.h>\n#include <channel/channel.h>\n#include <common/client_type.h>\n#include <generated/ebpf_net/ingest/writer.h>\n#include <util/aws_instance_metadata.h>\n#include <util/curl_engine.h>\n#include <util/gcp_instance_metadata.h>\n\n#include <uv.h>\n\n#include <chrono>\n#include <functional>\n#include <map>\n\nnamespace channel {\n\n// ConnectionCaretaker handles common tasks of agent->server connection.\n//\n// Current implementation does followings:\n//   1. Sends back initial metadata, including agent version, and configuration labels.\n//   2. Sends back heartbeat signal to server periodically.\n//\n// This class is NOT thread-safe.\nclass ConnectionCaretaker {\npublic:\n  using config_labels_t = std::map<std::string, std::string>;\n\n  // Constructor\n  //\n  // |config_data|: Configuration labels, read from a yaml file.\n  // |loop|: Libuv event loop.\n  // |channel|: Underline channel connecting agent and server.\n  // |heartbeat_interval|: How often a heartbeat signal is sent back to server.\n  // |flush_cb|: Callback to flush any downstream buffer.\n  ConnectionCaretaker(\n      std::string_view hostname,\n      ClientType client_type,\n      config_labels_t const &config_labels,\n      uv_loop_t *loop,\n      ebpf_net::ingest::Writer &writer,\n      std::chrono::milliseconds metadata_timeout,\n      std::chrono::milliseconds heartbeat_interval,\n      std::function<void()> flush_cb,\n      std::function<void(bool)> set_compression_cb,\n      std::function<void()> on_connected_cb);\n\n  ~ConnectionCaretaker();\n\n  // Note, this function triggers metadata to be sent back, and starts\n  // heartbeat signal.\n  void set_connected();\n\n  // Note, this function stops heartbeat timer implicitly\n  void set_disconnected();\n\n  // Sends one heartbeat. It is public so that timer callback can use it.\n  void send_heartbeat();\n\nprivate:\n  // Sends following information:\n  //   agent version and any config labels.\n  // TODO: Send agent type as well.\n  void send_metadata_header();\n  void start_heartbeat();\n  void stop_heartbeat();\n\n  void flush();\n\n  std::string_view const hostname_;\n  ClientType const client_type_;\n\n  const config_labels_t config_labels_;\n\n  uv_loop_t *loop_ = nullptr; // not owned\n\n  std::optional<AwsMetadata> aws_metadata_;\n  std::optional<GcpInstanceMetadata> gcp_metadata_;\n\n  const std::chrono::milliseconds heartbeat_interval_;\n\n  std::function<void()> flush_cb_;\n  std::function<void(bool)> set_compression_cb_;\n  std::function<void()> on_connected_cb_;\n\n  uv_timer_t heartbeat_timer_;\n\n  ebpf_net::ingest::Writer &writer_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/double_write_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/double_write_channel.h>\n\nnamespace channel {\n\nDoubleWriteChannel::DoubleWriteChannel(Channel &first, Channel &second) : first_(first), second_(second) {}\n\nstd::error_code DoubleWriteChannel::send(const u8 *data, int size)\n{\n  if (auto error = first_.send(data, size)) {\n    return error;\n  }\n\n  if (second_.is_open()) {\n    if (auto error = second_.send(data, size)) {\n      return error;\n    }\n  }\n\n  return {};\n}\n\nvoid DoubleWriteChannel::close()\n{\n  first_.close();\n  second_.close();\n}\n\nstd::error_code DoubleWriteChannel::flush()\n{\n  if (auto error = first_.flush()) {\n    return error;\n  }\n\n  if (second_.is_open()) {\n    if (auto error = second_.flush()) {\n      return error;\n    }\n  }\n\n  return {};\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/double_write_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/channel.h>\n\nnamespace channel {\n\nclass DoubleWriteChannel : public Channel {\npublic:\n  DoubleWriteChannel(Channel &first, Channel &second);\n\n  std::error_code send(const u8 *data, int size) override;\n\n  void close() override;\n  std::error_code flush() override;\n\n  bool is_open() const override { return first_.is_open() && second_.is_open(); }\n\nprivate:\n  Channel &first_;\n  Channel &second_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/file_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/file_channel.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <string_view>\n\n#include <cassert>\n\nnamespace channel {\n\nFileChannel::FileChannel(FileDescriptor fd) : fd_(std::move(fd)) {}\n\nstd::error_code FileChannel::send(const u8 *data, int size)\n{\n  std::string_view const buffer{reinterpret_cast<char const *>(data), static_cast<std::string_view::size_type>(size)};\n\n  if (auto const error = fd_.write_all(buffer)) {\n    const std::string error_message = error.message();\n    LOG::error(\"error while writing {} bytes into file channel: {} ({})\", size, error_message, error.value());\n    return error;\n  }\n\n  return {};\n}\n\nvoid FileChannel::close()\n{\n  fd_.close();\n}\n\nstd::error_code FileChannel::flush()\n{\n  auto const error = fd_.flush_data();\n  if (error) {\n    const std::string error_message = error.message();\n    LOG::error(\"error while flushing data for file channel: {} ({})\", error_message, error.value());\n  }\n  return error;\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/file_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/channel.h>\n#include <util/file_ops.h>\n\nnamespace channel {\n\nclass FileChannel : public Channel {\npublic:\n  FileChannel(FileDescriptor fd);\n\n  std::error_code send(const u8 *data, int size) override;\n\n  void close() override;\n  std::error_code flush() override;\n\n  bool valid() const { return fd_.valid(); }\n\n  explicit operator bool() const { return valid(); }\n  bool operator!() const { return !valid(); }\n\n  bool is_open() const override { return fd_.valid(); }\n\nprivate:\n  FileDescriptor fd_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/ibuffered_writer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/expected.h>\n\n#include <system_error>\n\n#include <cstring>\n\n// Interface for classes that write through a buffer.\n//\nclass IBufferedWriter {\npublic:\n  virtual ~IBufferedWriter() {}\n\n  // Starts a new write.\n  //\n  // Returns the memory where caller should write the data, or nullptr in case\n  // of an error.\n  //\n  virtual Expected<u8 *, std::error_code> start_write(u32 length) = 0;\n\n  // Finishes the current write.\n  //\n  virtual void finish_write() = 0;\n\n  // Writes the given payload in smaller batches to fit the internal buffer\n  Expected<bool, std::error_code> write_as_chunks(std::string_view payload)\n  {\n    for (auto const max = buf_size(); !payload.empty();) {\n      auto const size = (max <= payload.size()) ? max : static_cast<u32>(payload.size());\n\n      auto const allocated = start_write(size);\n      if (!allocated) {\n        return {unexpected, allocated.error()};\n      }\n\n      memcpy(*allocated, payload.data(), size);\n      finish_write();\n      payload.remove_prefix(size);\n    }\n\n    return true;\n  }\n\n  // Flushes the buffer.\n  //\n  virtual std::error_code flush() = 0;\n\n  // Returns the buffer size.\n  //\n  virtual u32 buf_size() const = 0;\n\n  virtual bool is_writable() const = 0;\n};\n"
  },
  {
    "path": "channel/lz4_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"channel/lz4_channel.h\"\n#include <stdexcept>\n\nnamespace channel {\n\nLz4Channel::Lz4Channel(Channel &channel, u32 max_data_length)\n    : compression_enabled_(false), channel_(channel), buffer_(LZ4F_compressBound(max_data_length, NULL) + LZ4F_HEADER_SIZE_MAX)\n{\n  if (LZ4F_cctx *lz4_context = nullptr; LZ4F_isError(LZ4F_createCompressionContext(&lz4_context, LZ4F_VERSION))) {\n    throw std::runtime_error(\"Lz4Channel: Failed to create LZ4 context.\");\n  } else {\n    lz4_ctx_.reset(lz4_context);\n  }\n}\n\nvoid Lz4Channel::set_compression(bool enabled)\n{\n  compression_enabled_ = enabled;\n}\n\n#define _CHECK_LZ4_ERROR(code)                                                                                                 \\\n  if (LZ4F_isError(code)) {                                                                                                    \\\n    throw std::runtime_error(std::string(\"Lz4Channel: compression failed: \") + std::string(LZ4F_getErrorName(code)));          \\\n  }\n\nstd::error_code Lz4Channel::send(const u8 *data, int data_len)\n{\n  if (!compression_enabled_) {\n    return channel_.send(data, data_len);\n  }\n\n  // Reference: https://github.com/lz4/lz4/blob/dev/lib/lz4frame.h#L248\n  size_t tail = 0;\n  size_t res = LZ4F_compressBegin(lz4_ctx_.get(), (void *)buffer_.data(), buffer_.size(), NULL);\n  _CHECK_LZ4_ERROR(res);\n  tail += res;\n\n  res =\n      LZ4F_compressUpdate(lz4_ctx_.get(), (void *)(buffer_.data() + tail), buffer_.size() - tail, (void *)data, data_len, NULL);\n  _CHECK_LZ4_ERROR(res);\n  tail += res;\n\n  res = LZ4F_compressEnd(lz4_ctx_.get(), (void *)(buffer_.data() + tail), buffer_.size() - tail, NULL);\n  _CHECK_LZ4_ERROR(res);\n  tail += res;\n\n  return channel_.send(buffer_.data(), tail);\n}\n\nvoid Lz4Channel::close()\n{\n  channel_.close();\n}\n\nstd::error_code Lz4Channel::flush()\n{\n  return channel_.flush();\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/lz4_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/channel.h>\n#include <platform/types.h>\n#include <util/raii.h>\n\n#include <lz4frame.h>\n\n#include <vector>\n\nnamespace channel {\n\n// Lz4Channel serves as an adapter between upstream data source and downstream\n// channel.\n//\n// When the compression is disabled, the Lz4Channel will pass any incoming\n// data packets to downstream channel directly.\n//\n// When the compression is enabled, the Lz4Channel will compress the incoming\n// data packets before relay then.\nclass Lz4Channel : public Channel {\npublic:\n  // |channel|: the downstream channel which will actually send out the data.\n  // |max_data_length|: max number of bytes of any incoming data packet sent\n  //                    via send() function. Note that it's caller's\n  //                    responsibility to honor this constraint.\n  Lz4Channel(Channel &channel, u32 max_data_length);\n\n  std::error_code send(const u8 *data, int data_len) override;\n\n  void set_compression(bool enabled);\n\n  void close() override;\n  std::error_code flush() override;\n\n  bool is_open() const override { return channel_.is_open(); }\n\nprivate:\n  bool compression_enabled_;\n\n  Channel &channel_;\n  std::vector<u8> buffer_;\n\n  pod_unique_ptr<LZ4F_cctx, LZ4F_errorCode_t, LZ4F_freeCompressionContext> lz4_ctx_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/mock_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <channel/channel.h>\n#include <gmock/gmock.h>\n\nnamespace channel {\n\nclass MockChannel : public Channel {\npublic:\n  MockChannel() = default;\n  ~MockChannel() override = default;\n\n  MOCK_METHOD2(send, std::error_code(const u8 *, int));\n  MOCK_CONST_METHOD0(is_open, bool());\n}; // class MockChannel\n} // namespace channel\n"
  },
  {
    "path": "channel/network_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/callbacks.h>\n#include <channel/channel.h>\n#include <platform/platform.h>\n\nnamespace channel {\n\n/**\n * An interface that allows reading and writing data to a pipe/socket/etc\n */\nclass NetworkChannel : public Channel {\npublic:\n  /**\n   * Connects to an endpoint and starts negotiating\n   * @param callbacks: the callbacks to use during this connection\n   */\n  virtual void connect(Callbacks &callbacks) = 0;\n\n  /**\n   * Returns the address (in binary format) that this channel is connected to,\n   * if available. `nullptr` otherwise.\n   */\n  virtual in_addr_t const *connected_address() const = 0;\n};\n\n} /* namespace channel */\n"
  },
  {
    "path": "channel/reconnecting_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/component.h>\n#include <channel/reconnecting_channel.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <stdexcept>\n\nnamespace channel {\nnamespace {\n// Callbacks passed to libuv\nvoid connection_timer_cb(uv_timer_t *timer)\n{\n  ReconnectingChannel *channel = (ReconnectingChannel *)(timer->data);\n\n  LOG::error(\"ReconnectingChannel: Connection timeout.\");\n  channel->on_error(UV_ETIMEDOUT);\n}\n} // namespace\n\nvoid start_timer_cb(uv_timer_t *timer)\n{\n  ReconnectingChannel *channel = (ReconnectingChannel *)(timer->data);\n  channel->to_connecting_state();\n}\n\nReconnectingChannel::ReconnectingChannel(config::IntakeConfig intake_config, uv_loop_t &loop, std::size_t buffer_size)\n    : loop_(loop),\n      intake_config_(std::move(intake_config)),\n      network_channel_(intake_config_.make_channel(loop)),\n      upstream_connection_(buffer_size, intake_config_.allow_compression(), *network_channel_),\n      state_(State::INACTIVE)\n{\n  int res = uv_timer_init(&loop_, &start_timer_);\n  if (res != 0) {\n    LOG::error(\"ReconnectingChannel: Cannot init start_timer\");\n  }\n  start_timer_.data = this;\n\n  res = uv_timer_init(&loop_, &connection_timer_);\n  if (res != 0) {\n    LOG::error(\"ReconnectingChannel: Cannot init connection_timer\");\n  }\n  connection_timer_.data = this;\n}\n\nReconnectingChannel::~ReconnectingChannel()\n{\n  close();\n  uv_close((uv_handle_t *)&connection_timer_, NULL);\n  uv_close((uv_handle_t *)&start_timer_, NULL);\n}\n\nvoid ReconnectingChannel::register_pipeline_observer(Callbacks *observer)\n{\n  pipeline_observers_.insert(observer);\n}\n\nvoid ReconnectingChannel::unregister_pipeline_observer(Callbacks *observer)\n{\n  pipeline_observers_.erase(observer);\n}\n\n//// Callbacks interface ////\nu32 ReconnectingChannel::received_data(const u8 *data, int data_len)\n{\n  // Do nothing for now.\n  return data_len;\n}\n\nvoid ReconnectingChannel::on_error(int err)\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: on_error(). prev_state: {}\", state_string());\n  LOG::warn(\"ReconnectingChannel: Connection error: {}\", uv_err_name(err));\n  for (auto *observer : pipeline_observers_) {\n    observer->on_error(err);\n  }\n\n  to_closing_state();\n}\n\nvoid ReconnectingChannel::on_closed()\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: on_closed(). State: {}\", state_string());\n\n  for (auto *observer : pipeline_observers_) {\n    observer->on_closed();\n  }\n\n  to_backoff_state();\n}\n\nvoid ReconnectingChannel::on_connect()\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: on_connect(). State: {}\", state_string());\n  LOG::info(\"ReconnectingChannel: Remote connection established.\");\n\n  num_bytes_sent_ = 0;\n  set_compression(false);\n\n  stop_all_timers();\n  assert(state_ == State::CONNECTING);\n  state_ = State::CONNECTED;\n\n  for (auto *observer : pipeline_observers_) {\n    observer->on_connect();\n  }\n}\n\nvoid ReconnectingChannel::set_compression(bool enabled)\n{\n  upstream_connection_.set_compression(enabled);\n}\n\n//// Channel interface ////\nstd::error_code ReconnectingChannel::send(const u8 *data, int data_len)\n{\n  if (state_ != State::CONNECTED) {\n    LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: Attempt to send when the channel is NOT connected.\");\n    return std::make_error_code(std::errc::not_connected);\n  }\n\n  auto &buffered_writer = upstream_connection_.buffered_writer();\n\n  num_bytes_sent_ += data_len;\n  LOG::trace_in(\n      Component::reconnecting_channel,\n      \"Sending ReconnectingChannel: {} bytes. {} bytes sent in total\",\n      data_len,\n      num_bytes_sent_);\n\n  auto buffer = buffered_writer.start_write(data_len);\n  if (!buffer) {\n    LOG::error(\"ReconnectingChannel: buffered writer overflow: {}\", buffer.error());\n    return buffer.error();\n  }\n  memcpy(*buffer, data, data_len);\n  buffered_writer.finish_write();\n  return {};\n}\n\nBufferedWriter &ReconnectingChannel::buffered_writer()\n{\n  return upstream_connection_.buffered_writer();\n}\n\nvoid ReconnectingChannel::close()\n{\n  state_ = State::INACTIVE;\n  stop_all_timers();\n  upstream_connection_.close();\n}\n\nstd::error_code ReconnectingChannel::flush()\n{\n  return upstream_connection_.flush();\n}\n\nu64 ReconnectingChannel::get_start_wait_time() const\n{\n  // TODO: better back-off mechanism here.\n  return 1000;\n}\n\nvoid ReconnectingChannel::to_connecting_state()\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: to_connecting_state(). State: {}\", state_string());\n\n  state_ = State::CONNECTING;\n\n  try {\n    upstream_connection_.connect(*this);\n  } catch (std::exception &e) {\n    LOG::warn(\n        \"ReconnectingChannel: Connection attempt failed; will backoff \"\n        \"and retry. Error: {}\",\n        e.what());\n    to_backoff_state();\n    return;\n  }\n  stop_all_timers();\n  int res = uv_timer_start(&connection_timer_, connection_timer_cb, connection_timeout_ms_, 0);\n\n  if (res != 0) {\n    LOG::error(\"ReconnectingChannel: Cannot start connection_timer {}\", uv_err_name(res));\n  }\n}\n\nvoid ReconnectingChannel::to_backoff_state()\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: start_timer(). State: {}\", state_string());\n  state_ = State::BACKOFF;\n\n  stop_all_timers();\n  int res = uv_timer_start(&start_timer_, start_timer_cb, get_start_wait_time(), 0);\n\n  if (res != 0) {\n    LOG::error(\"ReconnectingChannel: Cannot start start_timer {}\", uv_err_name(res));\n  }\n}\n\nvoid ReconnectingChannel::start_connect()\n{\n  LOG::trace_in(Component::reconnecting_channel, \"ReconnectingChannel: start_connect(). State: {}\", state_string());\n\n  assert(state_ == State::INACTIVE);\n\n  to_connecting_state();\n}\n\nvoid ReconnectingChannel::to_closing_state()\n{\n  state_ = State::CLOSING;\n  stop_all_timers();\n  try {\n    upstream_connection_.close();\n  } catch (std::exception &e) {\n    LOG::warn(\"ReconnectingChannel: Cannot close connection: {}\", e.what());\n  }\n  //  to_backoff_state();\n}\n\nconst char *ReconnectingChannel::state_string() const\n{\n  switch (state_) {\n  case State::INACTIVE:\n    return \"INACTIVE\";\n  case State::CONNECTING:\n    return \"CONNECTING\";\n  case State::CONNECTED:\n    return \"CONNECTED\";\n  case State::CLOSING:\n    return \"CLOSING\";\n  case State::BACKOFF:\n    return \"BACKOFF\";\n  }\n\n  // Make compiler happy.\n  // TODO: remove this line if we switch to clang++\n  return \"UNKNOWN\";\n}\n\nReconnectingChannel::State ReconnectingChannel::state() const\n{\n  return state_;\n}\n\nvoid ReconnectingChannel::stop_all_timers()\n{\n  uv_timer_stop(&connection_timer_);\n  uv_timer_stop(&start_timer_);\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/reconnecting_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <uv.h>\n\n#include <channel/callbacks.h>\n#include <channel/channel.h>\n#include <channel/upstream_connection.h>\n#include <config/intake_config.h>\n\n#include <set>\n\nnamespace channel {\n\n// ReconnectingChannel manages the connection to a remote server.\n//\n// Retries connection when network error occurs.\n//\n// Note that this class is NOT thread safe.\nclass ReconnectingChannel : public Channel, public Callbacks {\npublic:\n  ReconnectingChannel(config::IntakeConfig intake_config, uv_loop_t &loop, std::size_t buffer_size);\n  ~ReconnectingChannel() final;\n\n  // Registers/unregisters a observer.\n  //\n  // They are expected to use during initialization or clean-up phase.\n  void register_pipeline_observer(Callbacks *cb);\n  void unregister_pipeline_observer(Callbacks *cb);\n\n  // Enables or disables compression.\n  void set_compression(bool enabled);\n\n  // Channel interface\n  std::error_code send(const u8 *data, int data_len) override;\n\n  // Callbacks interface.\n  u32 received_data(const u8 *data, int data_len) override;\n  void on_error(int err) override;\n  void on_closed() override;\n  void on_connect() override;\n\n  // Starts the connection to remote server.\n  //\n  // It can only be called once.\n  void start_connect();\n\n  BufferedWriter &buffered_writer();\n\n  void close() override;\n\n  // Flushes and sends out any remaining messages in the send buffer.\n  std::error_code flush() override;\n\n  enum class State : int { INACTIVE, CONNECTING, CONNECTED, CLOSING, BACKOFF };\n\n  const char *state_string() const;\n  State state() const;\n\n  config::IntakeConfig const &intake_config() const { return intake_config_; }\n\n  bool is_open() const override { return upstream_connection_.is_open(); }\n\nprivate:\n  friend void start_timer_cb(uv_timer_t *timer);\n\n  // How long we should wait for the connection to be established, before\n  // it times out and reconnects again.\n  static constexpr u64 connection_timeout_ms_ = 10000;\n\n  // Starts the connection_timer_ to track if the TCP connection is\n  // established within |connection_timeout_ms_|\n  void to_connecting_state();\n\n  // Starts the start_timer_, to let the service sleep for certain period\n  // of time before start a new connection.\n  void to_backoff_state();\n\n  // Closes the TCP connection, and timers.\n  void to_closing_state();\n\n  // Stops all active timers\n  void stop_all_timers();\n\n  // Returns how much time the system should wait, in microsecond,\n  // before it tries to start a new connection.\n  u64 get_start_wait_time() const;\n\n  // UV loop that this object runs on.\n  uv_loop_t &loop_;\n\n  // Intake endpoint config\n  config::IntakeConfig const intake_config_;\n\n  // Handles low-level TLS, TCP connection.\n  std::unique_ptr<NetworkChannel> network_channel_;\n  UpstreamConnection upstream_connection_;\n\n  // Current state of the pipeline.\n  State state_;\n\n  // The timer to clock the waiting period before TCP connection restarts.\n  // ([INACTIVE | BACKOFF] -> CONNECTING)\n  uv_timer_t start_timer_;\n\n  // The timer to track if connection is establish successfully.\n  // (CONNECTING -> CONNECTED)\n  uv_timer_t connection_timer_;\n\n  // Observers interested in the status of the pipeline.\n  std::set<Callbacks *> pipeline_observers_;\n\n  // Number of bytes this channel has sent, or is about to send.\n  u64 num_bytes_sent_ = 0;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/tcp_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/tcp_channel.h>\n\n#include <channel/component.h>\n#include <platform/platform.h>\n#include <util/defer.h>\n#include <util/error_handling.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/uv_helpers.h>\n\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <netinet/tcp.h> //for disabling Nagle's\n#include <poll.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n\n#include <stdexcept>\n\n#define INVALID_FD -1\n\nnamespace channel {\n\nstatic constexpr std::string_view CONNECTED_DISCONNECTED[2] = {\"disconnected\", \"connected\"};\n/**\n * Callback passed to uv_read_start that allocates memory for the read callback\n */\nvoid TCPChannel::conn_read_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)\n{\n  uv_tcp_t *tcp_conn = (uv_tcp_t *)handle;\n  TCPChannel *conn = (TCPChannel *)tcp_conn->data;\n\n  if (conn->allocated_) {\n    buf->base = nullptr;\n    return;\n  }\n\n  /* assign the free buffer, reserving bytes for overflow */\n  buf->base = (char *)conn->rx_buffer_ + conn->rx_len_;\n  buf->len = TCPChannel::rx_buffer_size - conn->rx_len_;\n  conn->allocated_ = true;\n}\n\n/**\n * Read callback\n */\nvoid TCPChannel::conn_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)\n{\n  uv_tcp_t *tcp_conn = (uv_tcp_t *)stream;\n  TCPChannel *conn = (TCPChannel *)tcp_conn->data;\n\n  if (nread < 0) {\n    /* oh-oh, read error */\n    /* free buffer if it is valid */\n    if (buf->base) {\n      conn->allocated_ = false;\n    }\n\n    /* need to notify */\n    conn->connected_ = false;\n    conn->callbacks_->on_error(nread);\n    return;\n  }\n\n  /* \"merge\" the read data into the buffer */\n  conn->rx_len_ += nread;\n  conn->allocated_ = false;\n\n  /* read all complete messages from buffer */\n  try {\n    u32 res = conn->callbacks_->received_data((u8 *)conn->rx_buffer_, conn->rx_len_);\n\n    if (res > 0) {\n      ASSUME(res <= conn->rx_len_);\n      conn->rx_len_ -= res;\n      memmove(conn->rx_buffer_, (u8 *)conn->rx_buffer_ + res, conn->rx_len_);\n    }\n  } catch (const std::exception &e) {\n    LOG::error(\"TCPChannel: error handling received data: '{}'\", e.what());\n    conn->connected_ = false;\n    conn->callbacks_->on_error(-EPROTO);\n    return;\n  }\n\n  /* check that we don't exceed the buffer size */\n  if (conn->rx_len_ == TCPChannel::rx_buffer_size) {\n    conn->connected_ = false;\n    conn->callbacks_->on_error(-EOVERFLOW);\n    return;\n  }\n}\n\nvoid TCPChannel::conn_close_cb(uv_handle_t *handle)\n{\n  TCPChannel *conn = (TCPChannel *)handle->data;\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: calling on_closed()\", __func__);\n  conn->callbacks_->on_closed();\n}\n\nvoid TCPChannel::conn_close_and_reinit_cb(uv_handle_t *handle)\n{\n  TCPChannel *conn = (TCPChannel *)handle->data;\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: calling on_closed()\", __func__);\n  conn->callbacks_->on_closed();\n\n  /* re-initialize so we can reuse the handle */\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: calling reinit()\", __func__);\n  conn->reinit(handle->loop);\n}\n\nvoid TCPChannel::conn_connect_cb(uv_connect_t *req, int status)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  auto tcp = (TCPChannel *)req->handle->data;\n\n  if (status < 0) {\n    LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}(): error {}\", __func__, uv_error_t{status});\n    /* error occurred */\n    tcp->connected_ = false;\n    tcp->callbacks_->on_error(status);\n    return;\n  }\n\n  tcp->connected_ = true;\n\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}(): calling callback::on_connect()\", __func__);\n  tcp->callbacks_->on_connect();\n\n  tcp->start_processing();\n}\n\nvoid TCPChannel::conn_write_cb(uv_write_t *req, int status)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  auto tcp = (TCPChannel *)req->handle->data;\n\n  if (status < 0) {\n    /* no need to notify if close() was called, otherwise -- notify */\n    if (!uv_is_closing((uv_handle_t *)req->handle)) {\n      LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: connection not closing, calling close on handle()\", __func__);\n      tcp->connected_ = false;\n      tcp->callbacks_->on_error(status);\n    }\n  }\n\n  /* assumes req is the first field in send_buffer_t */\n  free(req);\n}\n\nTCPChannel::TCPChannel(uv_loop_t &loop)\n{\n  reinit(&loop);\n}\n\nTCPChannel::TCPChannel(uv_loop_t &loop, std::string addr, std::string port) : addr_(std::move(addr)), port_(std::move(port))\n{\n  reinit(&loop);\n}\n\nTCPChannel::~TCPChannel()\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: connection not closing, calling close on handle()\", __func__);\n  DEBUG_ASSUME(uv_is_closing((uv_handle_t *)&conn_));\n}\n\nvoid TCPChannel::connect(Callbacks &callbacks)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n\n  callbacks_ = callback_wrapper_ ? callback_wrapper_.get() : &callbacks;\n\n  struct addrinfo hints;\n  memset(&hints, 0, sizeof hints);\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n\n  struct addrinfo *res = nullptr;\n  LOG::debug(\"TCPChannel::{}: Connecting to intake @ {}:{}\", __func__, addr_, port_);\n  int status = getaddrinfo(addr_.c_str(), port_.c_str(), &hints, &res);\n\n  if (status != 0) {\n    const char *error_text = gai_strerror(status);\n    LOG::critical(\"getaddrinfo failed: {} - calling abort\", error_text ? error_text : \"unknown error\");\n    // TODO: gracefully handle getaddrinfo errors\n    std::abort();\n  }\n\n  Defer free_addrinfo([&res] { freeaddrinfo(res); });\n\n  if (res->ai_addr->sa_family == AF_INET) {\n    struct sockaddr_in *sa = (struct sockaddr_in *)(res->ai_addr);\n    connected_address_available_ = true;\n    connected_address_ = sa->sin_addr.s_addr;\n  }\n\n  if (auto const error = ::uv_tcp_connect(&connect_req_, &conn_, res->ai_addr, &conn_connect_cb)) {\n    LOG::error(\"TCPChannel::{}: failed to establish connection to {}:{}: {}\", __func__, addr_, port_, uv_error_t{error});\n    callbacks_->on_error(error);\n  }\n}\n\nvoid TCPChannel::accept(Callbacks &callbacks, uv_tcp_t *listener)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  callbacks_ = &callbacks;\n\n  if (auto const error = ::uv_accept(reinterpret_cast<uv_stream_t *>(listener), reinterpret_cast<uv_stream_t *>(&conn_))) {\n    // this is guaranteed to succeed when called from the connection callback:\n    // http://docs.libuv.org/en/v1.x/stream.html#c.uv_accept\n    LOG::error(\"TCPChannel::{}: failed to accept incoming connections: {}\", __func__, uv_error_t{error});\n    CHECK_UV(error); // TODO: verify that users of `accept` properly handle `on_error`\n    callbacks_->on_error(error);\n  } else {\n    connected_ = true;\n    start_processing();\n  }\n}\n\nvoid TCPChannel::open_fd(Callbacks &callbacks, const uv_os_sock_t fd)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  callbacks_ = &callbacks;\n\n  if (auto const error = ::uv_tcp_open(&conn_, fd)) {\n    LOG::error(\"TCPChannel::{}: failed to open existing file descriptor as a TCP handle: {}\", __func__, uv_error_t{error});\n    CHECK_UV(error); // TODO: verify that users of `open_fd` properly handle `on_error`\n    callbacks_->on_error(error);\n  } else {\n    connected_ = true;\n    start_processing();\n  }\n}\n\nvoid TCPChannel::close()\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  close_internal(&conn_close_and_reinit_cb);\n}\n\nvoid TCPChannel::close_permanently()\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  close_internal(&conn_close_cb);\n}\n\nstd::error_code TCPChannel::send(const u8 *data, int data_len)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}(len:{})\", __func__, data_len);\n  auto send_buffer = allocate_send_buffer(data_len);\n  if (!send_buffer) {\n    // TODO: gracefully handle out-of-memory errors\n    std::abort();\n    return std::make_error_code(std::errc::not_enough_memory);\n  }\n\n  memcpy(send_buffer->data, data, data_len);\n  send_buffer->len = data_len;\n\n  return send(send_buffer);\n}\n\nstruct TCPChannel::send_buffer_t *TCPChannel::allocate_send_buffer(u32 size)\n{\n  u32 mem_size = ((sizeof(struct send_buffer_t) + 7) & ~7) + size;\n  struct send_buffer_t *ret = (struct send_buffer_t *)malloc(mem_size);\n  if (ret == nullptr) {\n    LOG::critical(\"Failed to allocate send buffer of size {} mem_size {}\", size, mem_size);\n    return nullptr;\n  }\n\n  // clear memory to ensure we don't exfiltrate uninitialized data\n  memset(ret, 0, mem_size);\n\n  return ret;\n}\n\nstd::error_code TCPChannel::send(struct send_buffer_t *send_buffer)\n{\n  uv_buf_t uv_buf = {.base = (char *)send_buffer->data, .len = send_buffer->len};\n\n  if (auto const error = ::uv_write(&send_buffer->req, reinterpret_cast<uv_stream_t *>(&conn_), &uv_buf, 1, conn_write_cb)) {\n    LOG::error(\n        \"TCPChannel::{}: failed to write {} bytes into {} channel: {}\",\n        __func__,\n        send_buffer->len,\n        CONNECTED_DISCONNECTED[connected_],\n        uv_error_t{error});\n\n    callbacks_->on_error(error);\n    return {error, libuv_category()};\n  }\n\n  return {};\n}\n\nvoid TCPChannel::reinit(uv_loop_t *loop)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  /* reinit RX buffers */\n  rx_len_ = 0;\n  allocated_ = false;\n\n  /* re-init handle */\n  CHECK_UV(uv_tcp_init(loop, &conn_));\n  conn_.data = this;\n}\n\nvoid TCPChannel::start_processing()\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n\n  if (auto const error = ::uv_tcp_nodelay(&conn_, true)) {\n    LOG::error(\"TCPChannel::{}: failed to disable Nagle's algorithm: {}\", __func__, uv_error_t{error});\n    // this error is not critical, we may continue\n  }\n\n  if (auto const error = ::uv_read_start(reinterpret_cast<uv_stream_t *>(&conn_), &conn_read_alloc_cb, conn_read_cb)) {\n    LOG::error(\"TCPChannel::{}: failed to start read loop on channel: {}\", __func__, uv_error_t{error});\n\n    callbacks_->on_error(error);\n  }\n}\n\nin_addr_t const *channel::TCPChannel::connected_address() const\n{\n  std::array<in_addr_t const *, 2> choice = {nullptr, &connected_address_};\n  return choice[connected_address_available_];\n}\n\nvoid channel::TCPChannel::close_internal(const uv_close_cb close_cb)\n{\n  LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}()\", __func__);\n  connected_address_available_ = false;\n  connected_ = false;\n  if (!uv_is_closing((uv_handle_t *)&conn_)) {\n    LOG::trace_in(channel::Component::tcp, \"TCPChannel::{}: connection not closing, calling close on handle()\", __func__);\n    uv_close((uv_handle_t *)&conn_, close_cb);\n  }\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/tcp_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/callbacks.h>\n#include <channel/network_channel.h>\n#include <platform/platform.h>\n#include <memory>\n\n#include <uv.h>\n\nnamespace channel {\n\nstruct buffer_t {\n  char *base;\n  u32 offset;\n  u32 len;\n};\n\n/**\n * A TCP channel\n *\n * Errors for on_error callback:\n *   -EPROTO: handler threw exception\n *   -EOVERFLOW: overflow occupies entire buffer SERVER_CONN_BUFFER_SIZE and not\n *     handled by handler\n *   libuv errors.\n */\nclass TCPChannel : public NetworkChannel {\npublic:\n  static constexpr u32 rx_buffer_size = (64 * 1024);\n\n  struct send_buffer_t {\n    uv_write_t req; /* must be first */\n    u32 len;\n    u64 data[0];\n  };\n\n  /**\n   * c'tor -- leaves socket ready for accept()\n   */\n  TCPChannel(uv_loop_t &loop);\n\n  /**\n   * c'tor -- leaves socket ready for connect()\n   */\n  TCPChannel(uv_loop_t &loop, std::string addr, std::string port);\n\n  /**\n   * d'tor\n   */\n  virtual ~TCPChannel();\n\n  /**\n   * Connects to an endpoint\n   * @param callbacks: the callbacks to use during this connection\n   * @param addr: ip address or hostname\n   * @param port: string holding port number\n   */\n  void connect(Callbacks &callbacks) override;\n\n  /**\n   * Accepts a connection\n   *\n   * @param callbacks: the callbacks to use during this connection\n   * @param listener: the listening socket to accept on\n   */\n  void accept(Callbacks &callbacks, uv_tcp_t *listener);\n\n  /**\n   * Opens a TCP connection from the file descriptor.\n   */\n  void open_fd(Callbacks &callbacks, uv_os_sock_t fd);\n\n  /**\n   * closes the channel. Callbacks::on_close will be called\n   */\n  void close() override;\n\n  /**\n   * Closes the channel, and does not try to reinitilize.\n   */\n  void close_permanently();\n\n  /**\n   * @see Channel::send\n   */\n  std::error_code send(const u8 *data, int data_len) override;\n\n  /**\n   * Allocates a send buffer capable of holding @size bytes.\n   *\n   * It is the responsibility of the caller to call send() with the buffer.\n   */\n  struct send_buffer_t *allocate_send_buffer(u32 size);\n\n  /**\n   * Sends the given TCPChannel::send_buffer_t allocated with\n   *   allocate_send_buffer().\n   */\n  std::error_code send(struct send_buffer_t *send_buffer);\n\n  /**\n   * Returns the address (in binary format) that this channel is connected to,\n   * if available. `nullptr` otherwise.\n   */\n  in_addr_t const *connected_address() const override;\n\n  bool is_open() const override { return connected_; }\n\nprivate:\n  static void conn_read_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf);\n  static void conn_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);\n  static void conn_close_cb(uv_handle_t *handle);\n  static void conn_close_and_reinit_cb(uv_handle_t *handle);\n  static void conn_connect_cb(uv_connect_t *req, int status);\n  static void conn_write_cb(uv_write_t *req, int status);\n\n  void close_internal(const uv_close_cb close_cb);\n\n  /**\n   * Inits the tcp handle (conn_) and buffers\n   */\n  void reinit(uv_loop_t *loop);\n\n  /**\n   * Completes socket configuration and starts reading\n   */\n  void start_processing();\n\n  Callbacks *callbacks_ = nullptr;\n  std::string addr_;\n  std::string port_;\n  std::unique_ptr<Callbacks> callback_wrapper_;\n\n  uv_tcp_t conn_;\n  uv_connect_t connect_req_;\n\n  u64 rx_buffer_[(rx_buffer_size + 7) / 8];\n\n  /* number of bytes currently in the rx_buffer */\n  u32 rx_len_;\n\n  bool allocated_ = false;\n\n  bool connected_address_available_ = false;\n  bool connected_ = false;\n  in_addr_t connected_address_ = 0;\n};\n\n} /* namespace channel */\n"
  },
  {
    "path": "channel/test_channel.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/test_channel.h>\n#include <generated/ebpf_net/ingest/meta.h>\n#include <jitbuf/jb.h>\n\n#include <util/json_converter.h>\n\nnamespace channel {\n\nTestChannel::TestChannel(std::optional<std::reference_wrapper<uv_loop_t>> loop, IntakeEncoder encoder)\n    : loop_(loop), encoder_(encoder)\n{}\n\nstd::error_code TestChannel::send(const u8 *data, int size)\n{\n  try {\n    ++num_sends_;\n\n    LOG::trace(\"TestChannel::send() num_sends_ {}\", num_sends_);\n\n    switch (encoder_) {\n    case IntakeEncoder::binary: {\n      auto rpc = reinterpret_cast<jb_rpc const *>(data + sizeof(u64)); // + sizeof(u64) to skip past timestamp\n      LOG::trace(\"TestChannel::send() rpc: rpc_id {} size {}\", rpc->rpc_id, rpc->size);\n\n      binary_messages_.emplace_back(data, data + size);\n\n      std::stringstream ss;\n      json_converter::WireToJsonConverter<ebpf_net::ingest_metadata> converter(ss);\n\n      if (auto const handled = converter.process(reinterpret_cast<char const *>(data), size); !handled) {\n        if (handled.error().value() == EAGAIN) {\n          LOG::error(\"TestChannel::send() converter.process() returned EAGAIN\");\n        } else {\n          LOG::error(\"TestChannel::send() error while handling message: {}\", handled.error());\n        }\n        ++num_failed_sends_;\n      } else {\n        LOG::trace(\"TestChannel::send() binary format msg converted to JSON {}\", log_waive(ss.str()));\n\n        std::string str = \"[\" + ss.str() + \"]\";\n        nlohmann::json const objects = nlohmann::json::parse(str);\n        for (auto const &object : objects) {\n          ++message_counts_[object[\"name\"]];\n          json_messages_.push_back(object);\n          if (sent_msg_cb_) {\n            sent_msg_cb_(object);\n          }\n        }\n      }\n    } break;\n    default:\n      ++num_failed_sends_;\n      LOG::error(\"unknown IntakeEncoder {}\", encoder_);\n      break;\n    }\n  } catch (std::exception &ex) {\n    ++num_failed_sends_;\n    LOG::error(\"exception caught in TestChannel::send() {}\", ex.what());\n  }\n\n  return {};\n}\n\nvoid TestChannel::close() {}\n\nstd::error_code TestChannel::flush()\n{\n  return {};\n}\n\nu64 TestChannel::get_num_sends()\n{\n  return num_sends_;\n};\n\nu64 TestChannel::get_num_failed_sends()\n{\n  return num_failed_sends_;\n}\n\nstd::stringstream &TestChannel::get_ss()\n{\n  return ss_;\n}\n\nTestChannel::MessageCountsType &TestChannel::get_message_counts()\n{\n  return message_counts_;\n}\n\nvoid TestChannel::binary_messages_for_each(std::function<void(BinaryMessageType const &)> const &cb)\n{\n  for (auto const &msg : binary_messages_) {\n    cb(msg);\n  }\n}\n\nTestChannel::JsonMessagesType &TestChannel::get_json_messages()\n{\n  return json_messages_;\n}\n\nvoid TestChannel::json_messages_for_each(std::function<void(JsonMessageType const &)> const &cb)\n{\n  for (auto const &msg : json_messages_) {\n    cb(msg);\n  }\n}\n\nvoid TestChannel::set_sent_msg_cb(std::function<void(nlohmann::json const &)> const &sent_msg_cb)\n{\n  sent_msg_cb_ = sent_msg_cb;\n}\n\nvoid TestChannel::connect(Callbacks &callbacks)\n{\n  auto fake_connected_cb = [&callbacks]() {\n    LOG::trace(\"TestChannel::connect() fake_connected_cb() - calling callbacks.on_connect()\");\n    callbacks.on_connect();\n  };\n\n  fake_connected_cb_timer_ = std::make_unique<scheduling::Timer>(*loop_, fake_connected_cb);\n  auto delay_sec = 1;\n  if (auto const result = fake_connected_cb_timer_->defer(std::chrono::seconds(delay_sec))) {\n    LOG::trace(\"successfully scheduled fake_connected_cb() {} second(s) from now\", delay_sec);\n  } else {\n    throw std::runtime_error(\"failed to schedule fake_connected_cb()\");\n  }\n}\n\nin_addr_t const *TestChannel::connected_address() const\n{\n  return 0;\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/test_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/channel.h>\n#include <channel/network_channel.h>\n#include <common/intake_encoder.h>\n#include <scheduling/timer.h>\n#include <util/json.h>\n#include <util/log.h>\n\n#include <map>\n#include <sstream>\n\n#include <uv.h>\n\nnamespace channel {\n\n/**\n * A channel intended for use by unit tests.  It implements all Channel methods.  If loop is provided to the constructor then\n * it also implements the NetworkChannel methods.\n */\nclass TestChannel : public NetworkChannel {\npublic:\n  TestChannel(\n      std::optional<std::reference_wrapper<uv_loop_t>> loop = std::nullopt, IntakeEncoder encoder = IntakeEncoder::binary);\n\n  std::error_code send(const u8 *data, int size) override;\n\n  void close() override;\n  std::error_code flush() override;\n\n  bool is_open() const override { return true; }\n\n  void connect(Callbacks &callbacks) override;\n  in_addr_t const *connected_address() const override;\n\n  u64 get_num_sends();\n  u64 get_num_failed_sends();\n  std::stringstream &get_ss();\n\n  using MessageCountsType = std::map<std::string, u64>; // map of message name to number sent\n  MessageCountsType &get_message_counts();\n\n  using BinaryMessageType = std::vector<u8>;\n  using BinaryMessagesType = std::vector<BinaryMessageType>; // vector of messages sent in binary format\n  BinaryMessagesType &get_binary_messages();\n  void binary_messages_for_each(std::function<void(BinaryMessageType const &)> const &cb);\n\n  using JsonMessageType = nlohmann::json;\n  using JsonMessagesType = std::vector<JsonMessageType>; // vector of messages sent in JSON format\n  JsonMessagesType &get_json_messages();\n  void json_messages_for_each(std::function<void(JsonMessageType const &)> const &cb);\n\n  // Used to specify a function for the TestChannel to call for every render message processed by send().\n  void set_sent_msg_cb(std::function<void(nlohmann::json const &)> const &sent_msg_cb_);\n\nprivate:\n  std::optional<std::reference_wrapper<uv_loop_t>> loop_;\n  std::unique_ptr<scheduling::Timer> fake_connected_cb_timer_;\n\n  u64 num_sends_ = 0;\n  u64 num_failed_sends_ = 0;\n\n  IntakeEncoder encoder_;\n  std::stringstream ss_;\n\n  MessageCountsType message_counts_;\n\n  BinaryMessagesType binary_messages_;\n  JsonMessagesType json_messages_;\n\n  std::function<void(nlohmann::json const &)> sent_msg_cb_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/tls_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/tls_handler.h>\n\n#include <uv.h>\n\nnamespace channel {\n\nTLSHandler::TLSHandler(\n    uv_loop_t &loop,\n    std::string addr,\n    std::string port,\n    std::string agent_key,\n    std::string agent_crt,\n    std::string server_hostname,\n    std::optional<config::HttpProxyConfig> proxy)\n    : creds_(std::move(agent_key), std::move(agent_crt)),\n      tls_channel_(loop, std::move(addr), std::move(port), creds_, std::move(server_hostname), std::move(proxy))\n{}\n\nvoid TLSHandler::connect(Callbacks &callbacks)\n{\n  tls_channel_.connect(callbacks);\n}\n\nstd::error_code TLSHandler::send(const u8 *data, int data_len)\n{\n  return tls_channel_.send(data, data_len);\n}\n\nvoid TLSHandler::close()\n{\n  tls_channel_.close();\n}\n\nstd::error_code TLSHandler::flush()\n{\n  return tls_channel_.flush();\n}\n\nin_addr_t const *TLSHandler::connected_address() const\n{\n  return tls_channel_.get_tcp_channel().connected_address();\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/tls_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/network_channel.h>\n#include <channel/tls_channel.h>\n#include <channel/tls_over_tcp_channel.h>\n#include <map>\n\nnamespace channel {\n\nclass TLSHandler : public NetworkChannel {\npublic:\n  /**\n   * c'tor\n   * Throws if:\n   *   1. connection error on tcp_channel_\n   *   2. call to connect_tls throws\n   *\n   */\n  TLSHandler(\n      uv_loop_t &loop,\n      std::string addr,\n      std::string port,\n      std::string agent_key = \"\",\n      std::string agent_crt = \"\",\n      std::string server_hostname = \"\",\n      std::optional<config::HttpProxyConfig> proxy = {});\n\n  /**\n   * Connects to an endpoint and starts negotiating\n   * @param callbacks: the callbacks to use during this connection\n   */\n  void connect(Callbacks &callbacks) override;\n\n  std::error_code send(const u8 *data, int data_len) override;\n\n  /**\n   * disconnects the channel\n   */\n  void close() override;\n  std::error_code flush() override;\n\n  in_addr_t const *connected_address() const override;\n\n  bool is_open() const override { return tls_channel_.is_open(); }\n\nprivate:\n  TLSChannel::Credentials creds_;\n  TlsOverTcpChannel tls_channel_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "channel/upstream_connection.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/upstream_connection.h>\n\n#include <channel/component.h>\n#include <util/log.h>\n\nnamespace channel {\n\nUpstreamConnection::UpstreamConnection(\n    std::size_t buffer_size, bool allow_compression, NetworkChannel &primary_channel, Channel *secondary_channel)\n    : primary_channel_(primary_channel),\n      lz4_channel_(primary_channel_, buffer_size),\n      allow_compression_(allow_compression),\n      double_write_channel_(lz4_channel_, secondary_channel ? *secondary_channel : lz4_channel_),\n      buffered_writer_(secondary_channel ? static_cast<Channel &>(double_write_channel_) : lz4_channel_, buffer_size)\n{}\n\nvoid UpstreamConnection::connect(Callbacks &callbacks)\n{\n  buffered_writer_.reset();\n  primary_channel_.connect(callbacks);\n}\n\nstd::error_code UpstreamConnection::send(const u8 *data, int data_len)\n{\n  auto buffer = buffered_writer_.start_write(data_len);\n  if (!buffer) {\n    return buffer.error();\n  }\n  memcpy(*buffer, data, data_len);\n  buffered_writer_.finish_write();\n  return {};\n}\n\nstd::error_code UpstreamConnection::flush()\n{\n  return buffered_writer_.flush();\n}\n\nvoid UpstreamConnection::close()\n{\n  buffered_writer_.reset();\n  primary_channel_.close();\n}\n\nvoid UpstreamConnection::set_compression(bool enabled)\n{\n  buffered_writer_.flush();\n\n  LOG::trace_in(\n      Component::upstream,\n      \"UpstreamConnection: {} ({}allowed) LZ4 compression\",\n      enabled ? \"enabling\" : \"disabling\",\n      allow_compression_ ? \"\" : \"not \");\n\n  lz4_channel_.set_compression(enabled && allow_compression_);\n}\n\nBufferedWriter &UpstreamConnection::buffered_writer()\n{\n  return buffered_writer_;\n}\n\nin_addr_t const *UpstreamConnection::connected_address() const\n{\n  return primary_channel_.connected_address();\n}\n\n} // namespace channel\n"
  },
  {
    "path": "channel/upstream_connection.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/buffered_writer.h>\n#include <channel/callbacks.h>\n#include <channel/double_write_channel.h>\n#include <channel/lz4_channel.h>\n#include <channel/network_channel.h>\n#include <platform/platform.h>\n\nnamespace channel {\n\nclass UpstreamConnection : public NetworkChannel {\npublic:\n  UpstreamConnection(\n      std::size_t buffer_size, bool allow_compression, NetworkChannel &primary_channel, Channel *secondary_channel = nullptr);\n\n  /**\n   * Connects to an endpoint and starts negotiating\n   * @param callbacks: the callbacks to use during this connection\n   * @param addr: ip address or hostname\n   * @param port: string holding port number\n   */\n  void connect(Callbacks &callbacks) override;\n\n  std::error_code send(const u8 *data, int data_len) override;\n\n  /**\n   * Flushes the internal buffers.\n   */\n  std::error_code flush() override;\n\n  /**\n   * disconnects the channel\n   */\n  void close() override;\n\n  /**\n   * Enables/disables compression.\n   */\n  void set_compression(bool enabled);\n\n  BufferedWriter &buffered_writer();\n\n  in_addr_t const *connected_address() const override;\n\n  bool is_open() const override { return primary_channel_.is_open(); }\n\nprivate:\n  NetworkChannel &primary_channel_;\n  Lz4Channel lz4_channel_;\n  bool allow_compression_;\n  DoubleWriteChannel double_write_channel_;\n  BufferedWriter buffered_writer_;\n};\n\n} // namespace channel\n"
  },
  {
    "path": "clang-format.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nCLANG_FORMAT=\"clang-format-19\"\nif ! command -v ${CLANG_FORMAT}\nthen\n  echo \"ERROR: requires ${CLANG_FORMAT}\"\n  exit 1\nfi\n\nRC=0\nCMD=\"${CLANG_FORMAT} -Werror -i -style=file\"\nfunction format_file\n{\n  if ! ${CMD} $1\n  then\n    RC=1\n  fi\n}\n\n# Check that C and C++ source files are properly clang-formatted\nFILES=$(find ./geoip ./reducer ./test ./collector/kernel ./common ./tools \\\n\t-type f                                                           \\\n\t\\( -name \"*.c\"                                                    \\\n\t-o -name \"*.cc\"                                                   \\\n\t-o -name \"*.h\"                                                    \\\n\t-o -name \"*.inl\" \\)                                               \\\n\t-print)\n\nfor FILE in ${FILES}\ndo\n  format_file ${FILE}\ndone\n\nexit ${RC}\n"
  },
  {
    "path": "cmake/abseil.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(absl REQUIRED)\n"
  },
  {
    "path": "cmake/aws-sdk.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(AWSSDK REQUIRED)\nset(AWS_SERVICES ec2 s3 core)\nAWSSDK_DETERMINE_LIBS_TO_LINK(AWS_SERVICES AWSSDK_LIBS)\n\nmessage(STATUS \"Found AWS SDK Services: ${AWSSDK_SERVICES}\")\nmessage(STATUS \"Found AWS SDK Libraries: ${AWSSDK_LIBS}\")\n\n\nadd_library(aws-sdk-cpp INTERFACE)\ntarget_link_libraries(\n  aws-sdk-cpp\n  INTERFACE\n    ${AWSSDK_LIBS}\n    ${AWSSDK_LIBS}\n    crypto\n    s2n\n    dl\n)\ntarget_include_directories(\n  aws-sdk-cpp\n  INTERFACE\n    ${AWSSDK_INCLUDE_DIR}\n)\n"
  },
  {
    "path": "cmake/cargo-test.cmake",
    "content": "include_guard()\n\n# Adds a single target to run all Rust tests in the workspace.\n# Tests run from the source root, with artifacts under ${CMAKE_BINARY_DIR}/target.\nif(NOT TARGET cargo-test)\n  add_custom_target(\n    cargo-test\n    COMMAND\n      ${CMAKE_COMMAND} -E chdir ${PROJECT_SOURCE_DIR}\n      ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${CMAKE_BINARY_DIR}/target cargo test\n    VERBATIM\n  )\nendif()\n\n"
  },
  {
    "path": "cmake/cargo_build_rust.cmake",
    "content": "cmake_minimum_required(VERSION 3.16)\n\nif(NOT DEFINED LINK_FILE)\n  message(FATAL_ERROR \"LINK_FILE not provided to cargo_build_rust.cmake\")\nendif()\nif(NOT DEFINED BIN_DIR)\n  message(FATAL_ERROR \"BIN_DIR not provided to cargo_build_rust.cmake\")\nendif()\nif(NOT DEFINED PROJ_DIR)\n  message(FATAL_ERROR \"PROJ_DIR not provided to cargo_build_rust.cmake\")\nendif()\nif(NOT DEFINED RUST_BIN_TARGET_DIR)\n  message(FATAL_ERROR \"RUST_BIN_TARGET_DIR not provided to cargo_build_rust.cmake\")\nendif()\nif(NOT DEFINED RUST_PACKAGE)\n  message(FATAL_ERROR \"RUST_PACKAGE not provided to cargo_build_rust.cmake\")\nendif()\n\n# Read the CMake-generated link command for the dummy/existing C++ target\nfile(READ \"${LINK_FILE}\" LINK_CONTENT)\nget_filename_component(LINK_DIR \"${LINK_FILE}\" DIRECTORY)\n# Derive the target binary directory (two levels up from CMakeFiles/<target>.dir)\nget_filename_component(_cmakefiles_dir \"${LINK_DIR}\" DIRECTORY)\nget_filename_component(TARGET_BIN_DIR \"${_cmakefiles_dir}\" DIRECTORY)\n\n# Seed library search paths with known build output dirs and system dirs\nset(SEARCH_DIRS\n  \"${BIN_DIR}/collector/kernel\"\n  \"${BIN_DIR}/collector\"\n  \"${TARGET_BIN_DIR}\"\n  \"${BIN_DIR}/render\"\n  \"${BIN_DIR}/channel\"\n  \"${BIN_DIR}/config\"\n  \"${BIN_DIR}/platform\"\n  \"${BIN_DIR}/scheduling\"\n  \"${BIN_DIR}/util\"\n  \"${BIN_DIR}/otlp\"\n  \"${BIN_DIR}/geoip\"\n  \"${BIN_DIR}/reducer\"\n  \"${BIN_DIR}/reducer/util\"\n  \"/install/lib\"\n  \"/install/usr/lib64\"\n  \"/usr/lib/x86_64-linux-gnu\"\n)\n\n# Extract -L entries from link line\nstring(REGEX MATCHALL \"-L([^ \\t\\n]+)\" LFLAGS \"${LINK_CONTENT}\")\nforeach(LF IN LISTS LFLAGS)\n  string(REGEX REPLACE \"^-L\" \"\" LF_PATH \"${LF}\")\n  list(APPEND SEARCH_DIRS \"${LF_PATH}\")\nendforeach()\n\n# Extract directories of static/shared libraries present on the link line\n# Include both absolute and relative paths; resolve relatives against LINK_DIR.\nstring(REGEX MATCHALL \"([^ \\t\\n]*lib[^ \\t\\n]+\\\\.(a|so)(\\\\.[0-9.]+)?)\" ALL_LIB_PATHS \"${LINK_CONTENT}\")\nforeach(LP IN LISTS ALL_LIB_PATHS)\n  set(LIB_DIR \"${LP}\")\n  if(NOT IS_ABSOLUTE \"${LIB_DIR}\")\n    get_filename_component(LIB_DIR \"${LINK_DIR}/${LIB_DIR}\" DIRECTORY)\n  else()\n    get_filename_component(LIB_DIR \"${LIB_DIR}\" DIRECTORY)\n  endif()\n  list(APPEND SEARCH_DIRS \"${LIB_DIR}\")\nendforeach()\n\nlist(REMOVE_DUPLICATES SEARCH_DIRS)\n\n# Build library list specs\nset(LIB_SPECS)\n\n# 1) Static libs by path (.a), absolute or relative\nstring(REGEX MATCHALL \"([^ \\t\\n]*lib[^ \\t\\n]+\\\\.a)\" ANY_A \"${LINK_CONTENT}\")\nforeach(LIB IN LISTS ANY_A)\n  get_filename_component(FNAME \"${LIB}\" NAME)\n  string(REGEX REPLACE \"^lib\" \"\" NAME_NO_PREFIX \"${FNAME}\")\n  string(REGEX REPLACE \"\\\\.a$\" \"\" NAME_NO_EXT \"${NAME_NO_PREFIX}\")\n  if(NOT NAME_NO_EXT STREQUAL \"encoder_ebpf_net_all\")\n    list(APPEND LIB_SPECS \"static=${NAME_NO_EXT}\")\n  endif()\nendforeach()\n\n# 2) Shared libs by absolute path (.so)\nstring(REGEX MATCHALL \"(/[^ \\t\\n]*/lib[^ \\t\\n]+\\\\.so(\\\\.[0-9.]+)?)\" ABS_SO \"${LINK_CONTENT}\")\nforeach(LIB IN LISTS ABS_SO)\n  get_filename_component(FNAME \"${LIB}\" NAME)\n  string(REGEX REPLACE \"^lib\" \"\" NAME_NO_PREFIX \"${FNAME}\")\n  string(REGEX REPLACE \"\\\\.so(\\\\..*)?$\" \"\" NAME_NO_EXT \"${NAME_NO_PREFIX}\")\n  list(APPEND LIB_SPECS \"dylib=${NAME_NO_EXT}\")\nendforeach()\n\n# 3) -l flags (match standalone tokens only, not substrings like -static-libgcc or paths)\nstring(REGEX MATCHALL \"(^|[ \\t\\n])-l[A-Za-z0-9_+\\-]+\" LFLAG_MATCHES \"${LINK_CONTENT}\")\nforeach(TOK IN LISTS LFLAG_MATCHES)\n  string(STRIP \"${TOK}\" TOK_CLEAN)\n  string(REGEX REPLACE \"^(-l)\" \"\" LNAME \"${TOK_CLEAN}\")\n  list(APPEND LIB_SPECS \"dylib=${LNAME}\")\nendforeach()\n\n# Ensure C++ runtime bits\nlist(APPEND LIB_SPECS \"dylib=stdc++\" \"dylib=gcc_s\")\n\n# Deduplicate while preserving first occurrence\nlist(REMOVE_DUPLICATES LIB_SPECS)\n\n# Linker args for static group ordering\nset(LINK_ARGS \"-Wl,--start-group;-Wl,--end-group\")\n\n# Compose env var strings\nstring(JOIN \":\" OTN_LINK_SEARCH ${SEARCH_DIRS})\nstring(JOIN \";\" OTN_LINK_LIBS ${LIB_SPECS})\nset(OTN_LINK_ARGS \"${LINK_ARGS}\")\n\nmessage(STATUS \"OTN_LINK_SEARCH=${OTN_LINK_SEARCH}\")\nmessage(STATUS \"OTN_LINK_LIBS=${OTN_LINK_LIBS}\")\n\n# Escape semicolons for passing via CMake -E env\nstring(REPLACE \";\" \"\\\\;\" OTN_LINK_LIBS_ESC \"${OTN_LINK_LIBS}\")\nstring(REPLACE \";\" \"\\\\;\" OTN_LINK_ARGS_ESC \"${OTN_LINK_ARGS}\")\n\nexecute_process(\n  COMMAND ${CMAKE_COMMAND} -E env\n    CARGO_TARGET_DIR=${RUST_BIN_TARGET_DIR}\n    OTN_LINK_SEARCH=${OTN_LINK_SEARCH}\n    OTN_LINK_LIBS=${OTN_LINK_LIBS_ESC}\n    OTN_LINK_ARGS=${OTN_LINK_ARGS_ESC}\n    cargo build --release --package ${RUST_PACKAGE} --manifest-path ${PROJ_DIR}/Cargo.toml\n  WORKING_DIRECTORY ${PROJ_DIR}\n  RESULT_VARIABLE CARGO_RES\n  OUTPUT_VARIABLE CARGO_OUT\n  ERROR_VARIABLE CARGO_ERR\n)\n\nif(NOT CARGO_RES EQUAL 0)\n  message(STATUS \"Cargo stdout:\\n${CARGO_OUT}\")\n  message(STATUS \"Cargo stderr:\\n${CARGO_ERR}\")\n  message(FATAL_ERROR \"Cargo build failed with exit code ${CARGO_RES}\")\nendif()\n"
  },
  {
    "path": "cmake/ccache.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# use ccache if available. thanks to http://stackoverflow.com/a/24305849\nfind_program(CCACHE_FOUND ccache)\nif(CCACHE_FOUND)\n  set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)\n  set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)\nendif(CCACHE_FOUND)\n"
  },
  {
    "path": "cmake/civetweb.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nset(CIVETWEB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/ext/civetweb/include)\n\nadd_library(\n  civetweb\n  OBJECT\n    ext/civetweb/include/CivetServer.h\n    ext/civetweb/include/civetweb.h\n    ext/civetweb/src/CivetServer.cpp\n    ext/civetweb/src/civetweb.c\n    ext/civetweb/src/handle_form.inl\n    ext/civetweb/src/md5.inl\n)\n\ntarget_compile_definitions(\n  civetweb\n  PRIVATE\n    USE_IPV6\n    NDEBUG\n    NO_CGI\n    NO_CACHING\n    NO_SSL\n    NO_FILES\n)\n\ntarget_include_directories(civetweb PUBLIC ${CIVETWEB_INCLUDE_DIR})\n\nfind_library(LIBDL dl)\ntarget_link_libraries(civetweb PRIVATE ${LIBDL})\n\nadd_library(civetweb-interface INTERFACE)\ntarget_include_directories(civetweb-interface INTERFACE ${CIVETWEB_INCLUDE_DIR})\n"
  },
  {
    "path": "cmake/clang.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(CLANG REQUIRED CONFIG NAMES Clang)\nmessage(STATUS \"Found Clang ${CLANG_VERSION}\")\nmessage(STATUS \"Using ClangConfig.cmake in: ${CLANG_CONFIG}\")\n\noption(ENABLE_LLVM_SHARED \"Enable linking LLVM as a shared library\" OFF)\n\nif(NOT ENABLE_LLVM_SHARED)\n  #\n  # Overwrite libclang's INTERFACE_LINK_LIBRARIES property to link with static LLVM libraries.\n  #\n  set_target_properties(clangBasic PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"LLVMCore;LLVMMC;LLVMSupport\"\n  )\n  set_target_properties(clangLex PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;LLVMSupport\"\n  )\n  set_target_properties(clangParse PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangLex;clangSema;LLVMMC;LLVMMCParser;LLVMSupport\"\n  )\n  set_target_properties(clangAST PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangLex;LLVMBinaryFormat;LLVMSupport\"\n  )\n  set_target_properties(clangDynamicASTMatchers PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangASTMatchers;clangBasic;LLVMSupport\"\n  )\n  set_target_properties(clangASTMatchers PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;LLVMSupport\"\n  )\n  set_target_properties(clangCrossTU PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangFrontend;clangIndex;LLVMSupport\"\n  )\n  set_target_properties(clangSema PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangAnalysis;clangBasic;clangEdit;clangLex;LLVMSupport\"\n  )\n  set_target_properties(clangCodeGen PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAnalysis;clangAST;clangBasic;clangFrontend;clangLex;clangSerialization;LLVMAnalysis;LLVMBitReader;LLVMBitWriter;LLVMCore;LLVMCoroutines;LLVMCoverage;LLVMipo;LLVMIRReader;LLVMAggressiveInstCombine;LLVMInstCombine;LLVMInstrumentation;LLVMLTO;LLVMLinker;LLVMMC;LLVMObjCARCOpts;LLVMObject;LLVMPasses;LLVMProfileData;LLVMScalarOpts;LLVMSupport;LLVMTarget;LLVMTransformUtils\"\n  )\n  set_target_properties(clangAnalysis PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangASTMatchers;clangBasic;clangLex;LLVMSupport\"\n  )\n  set_target_properties(clangEdit PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangLex;LLVMSupport\"\n  )\n  set_target_properties(clangRewrite PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangLex;LLVMSupport\"\n  )\n  set_target_properties(clangARCMigrate PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangAnalysis;clangBasic;clangEdit;clangFrontend;clangLex;clangRewrite;clangSema;clangSerialization;clangStaticAnalyzerCheckers;clangStaticAnalyzerCore;LLVMSupport\"\n  )\n  set_target_properties(clangDriver PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;LLVMBinaryFormat;LLVMOption;LLVMSupport\"\n  )\n  set_target_properties(clangSerialization PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangLex;clangSema;LLVMBitReader;LLVMSupport\"\n  )\n  set_target_properties(clangRewriteFrontend PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangEdit;clangFrontend;clangLex;clangRewrite;clangSerialization;LLVMSupport\"\n  )\n  set_target_properties(clangFrontend PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangDriver;clangEdit;clangLex;clangParse;clangSema;clangSerialization;LLVMBitReader;LLVMOption;LLVMProfileData;LLVMSupport\"\n  )\n  set_target_properties(clangFrontendTool PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangCodeGen;clangDriver;clangFrontend;clangRewriteFrontend;clangARCMigrate;clangStaticAnalyzerFrontend;LLVMOption;LLVMSupport\"\n  )\n  set_target_properties(clangToolingCore PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangLex;clangRewrite;LLVMSupport\"\n  )\n  set_target_properties(clangToolingInclusions PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangLex;clangRewrite;clangToolingCore;LLVMSupport\"\n  )\n  set_target_properties(clangToolingASTDiff PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangAST;clangLex;LLVMSupport\"\n  )\n  set_target_properties(clangTooling PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangASTMatchers;clangBasic;clangDriver;clangFormat;clangFrontend;clangLex;clangRewrite;clangSerialization;clangToolingCore;LLVMOption;LLVMSupport\"\n  )\n  set_target_properties(clangIndex PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangBasic;clangFormat;clangFrontend;clangLex;clangRewrite;clangSerialization;clangToolingCore;LLVMCore;LLVMSupport\"\n  )\n  set_target_properties(clangStaticAnalyzerCore PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangASTMatchers;clangAnalysis;clangBasic;clangCrossTU;clangLex;clangRewrite;LLVMSupport\"\n  )\n  set_target_properties(clangStaticAnalyzerCheckers PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangASTMatchers;clangAnalysis;clangBasic;clangLex;clangStaticAnalyzerCore;LLVMSupport\"\n  )\n  set_target_properties(clangStaticAnalyzerFrontend PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangAST;clangAnalysis;clangBasic;clangCrossTU;clangFrontend;clangLex;clangStaticAnalyzerCheckers;clangStaticAnalyzerCore;LLVMSupport\"\n  )\n  set_target_properties(clangFormat PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangLex;clangToolingCore;clangToolingInclusions;LLVMSupport\"\n  )\n  set_target_properties(clangHandleCXX PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"clangBasic;clangCodeGen;clangFrontend;clangLex;clangSerialization;clangTooling;LLVMBPFCodeGen;LLVMBPFAsmParser;LLVMBPFAsmPrinter;LLVMBPFDesc;LLVMBPFDisassembler;LLVMBPFInfo;LLVMX86CodeGen;LLVMX86AsmParser;LLVMX86AsmPrinter;LLVMX86Desc;LLVMX86Disassembler;LLVMX86Info;LLVMX86Utils;LLVMSupport\"\n  )\n  set_target_properties(clangHandleLLVM PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"LLVMAnalysis;LLVMCodeGen;LLVMCore;LLVMExecutionEngine;LLVMipo;LLVMIRReader;LLVMMC;LLVMMCJIT;LLVMObject;LLVMRuntimeDyld;LLVMSelectionDAG;LLVMSupport;LLVMTarget;LLVMTransformUtils;LLVMX86CodeGen;LLVMX86AsmParser;LLVMX86AsmPrinter;LLVMX86Desc;LLVMX86Disassembler;LLVMX86Info;LLVMX86Utils\"\n  )\nendif(NOT ENABLE_LLVM_SHARED)\n"
  },
  {
    "path": "cmake/cpp-compiler.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# resolves the command line option for limiting error output\n# this is extremely useful for debugging compilation errors\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n  set(CXX_ERROR_LIMIT_FLAG \"-ferror-limit\")\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n  set(CXX_ERROR_LIMIT_FLAG \"-fmax-errors\")\nendif()\n\nmessage(STATUS \"C++ compiler: ${CMAKE_CXX_COMPILER}\")\nmessage(STATUS \"C++ compiler version: ${CMAKE_CXX_COMPILER_VERSION}\")\n\nexecute_process(\n  COMMAND ${CMAKE_CXX_COMPILER} --version\n  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n  OUTPUT_VARIABLE CXX_COMPILER_NATIVE_VERSION\n  OUTPUT_STRIP_TRAILING_WHITESPACE\n)\nmessage(STATUS \"C++ compiler native version string: ${CXX_COMPILER_NATIVE_VERSION}\")\n\n# most expressive debugging, and some optimization\n# also, disabling -Wno-stringop-truncation where necessary given that it warns about\n#   behavior we intend to get out of strncpy\nset(EBPF_NET_COMMON_COMPILE_FLAGS \"-ggdb3 -Wall -Werror -fno-omit-frame-pointer -Wno-stringop-truncation ${CXX_ERROR_LIMIT_FLAG}=1 -pthread\")\nset(EBPF_NET_COMMON_C_FLAGS \"${EBPF_NET_COMMON_COMPILE_FLAGS}\")\nset(EBPF_NET_COMMON_CXX_FLAGS \"${EBPF_NET_COMMON_COMPILE_FLAGS}\")\n\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} ${EBPF_NET_COMMON_C_FLAGS}\")\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} ${EBPF_NET_COMMON_CXX_FLAGS}\")\nset(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} ${EBPF_NET_COMMON_LINKER_FLAGS} -static-libgcc -static-libstdc++ -pthread\")\n\nif(OPTIMIZE)\n  set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} -O2\")\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -O2\")\nendif()\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_EXPORT_COMPILE_COMMANDS \"ON\")\n\nfunction (harden_executable TARGET)\n  target_compile_options(\n    ${TARGET}\n    PUBLIC\n      -Wl,-z,relro,-z,now\n      -fstack-protector\n      -static-pie\n      -fpie\n      -fPIE\n  )\nendfunction()\n"
  },
  {
    "path": "cmake/curl.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(CURL)\nif(NOT CURL_FOUND)\n  message(FATAL_ERROR \"cURL not found! Please install cURL development files.\")\nendif()\n\nmessage(STATUS \"Found cURL: ${CURL_LIBRARIES}\")\n"
  },
  {
    "path": "cmake/curlpp.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_path(CURLPP_INCLUDE_DIR NAMES curlpp/cURLpp.hpp)\nfind_library(CURLPP_LIBRARY NAMES curlpp)\n\n\nif (NOT CURLPP_INCLUDE_DIR OR NOT CURLPP_LIBRARY)\n  message(FATAL_ERROR \"cURLpp not found! Please install cURLpp development files.\")\nendif()\n\nmessage(STATUS \"curlpp library: ${CURLPP_LIBRARY}\")\nadd_library(curl-cpp INTERFACE)\ntarget_include_directories(\n  curl-cpp\n  INTERFACE\n    \"${CURLPP_INCLUDE_DIR}\"\n)\ntarget_link_libraries(\n  curl-cpp\n  INTERFACE\n    \"${CURLPP_LIBRARY}\"\n    CURL::libcurl\n)\n"
  },
  {
    "path": "cmake/debug.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfunction(strip_binary TARGET)\n  cmake_parse_arguments(ARG \"\" \"\" \"DEPENDS\" ${ARGN})\n\n  add_custom_target(\n    ${TARGET}-stripped\n    DEPENDS ${ARG_DEPENDS}\n    COMMAND\n      ${CMAKE_SOURCE_DIR}/dev/strip-symbols.sh $<TARGET_FILE:${TARGET}>\n  )\n\n  set_property(\n    TARGET\n      ${TARGET}-stripped\n    PROPERTY\n      \"OUTPUT\" $<TARGET_FILE:${TARGET}>-stripped\n  )\nendfunction()\n"
  },
  {
    "path": "cmake/defaults.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# Default build type is debug for fast iterations\nif(NOT CMAKE_BUILD_TYPE)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE STRING \"Build type\" FORCE)\nendif()\n\n# If building inside the container, help CMake find dependencies in /install\nif(EXISTS \"/install\")\n  if(NOT DEFINED CMAKE_INSTALL_PREFIX OR CMAKE_INSTALL_PREFIX STREQUAL \"/usr/local\")\n    set(CMAKE_INSTALL_PREFIX \"/install\" CACHE PATH \"Install prefix\" FORCE)\n  endif()\n\n  if(NOT CMAKE_PREFIX_PATH)\n    set(CMAKE_PREFIX_PATH \"/install\" CACHE STRING \"Prefix search path\" FORCE)\n  elseif(NOT CMAKE_PREFIX_PATH MATCHES \"(^|;)/install(;|$)\")\n    set(CMAKE_PREFIX_PATH \"${CMAKE_PREFIX_PATH};/install\" CACHE STRING \"Prefix search path\" FORCE)\n  endif()\n\n  if(EXISTS \"/install/openssl\" AND (NOT DEFINED OPENSSL_ROOT_DIR OR OPENSSL_ROOT_DIR STREQUAL \"\"))\n    set(OPENSSL_ROOT_DIR \"/install/openssl\" CACHE PATH \"OpenSSL root\" FORCE)\n  endif()\n\n  # Ensure pkg-config sees libraries installed under /install\n  set(_pkg_paths \"/install/lib64/pkgconfig:/install/lib/pkgconfig:/install/usr/lib64/pkgconfig:/install/usr/lib/pkgconfig:/install/share/pkgconfig:/install/usr/share/pkgconfig\")\n  if(DEFINED ENV{PKG_CONFIG_PATH} AND NOT \"$ENV{PKG_CONFIG_PATH}\" STREQUAL \"\")\n    set(ENV{PKG_CONFIG_PATH} \"$ENV{PKG_CONFIG_PATH}:${_pkg_paths}\")\n  else()\n    set(ENV{PKG_CONFIG_PATH} \"${_pkg_paths}\")\n  endif()\nendif()\n\n# Provide explicit package locations commonly installed in this image.\n# These hints short-circuit expensive probing inside find_package.\nif(NOT DEFINED LLVM_DIR AND EXISTS \"/usr/lib/llvm-19/cmake\")\n  set(LLVM_DIR \"/usr/lib/llvm-19/cmake\" CACHE PATH \"LLVM config dir\" FORCE)\nendif()\nif(NOT DEFINED gRPC_DIR AND EXISTS \"/usr/lib/x86_64-linux-gnu/cmake/grpc\")\n  set(gRPC_DIR \"/usr/lib/x86_64-linux-gnu/cmake/grpc\" CACHE PATH \"gRPC config dir\" FORCE)\nendif()\nif(NOT DEFINED absl_DIR AND EXISTS \"/usr/lib/x86_64-linux-gnu/cmake/absl\")\n  set(absl_DIR \"/usr/lib/x86_64-linux-gnu/cmake/absl\" CACHE PATH \"abseil config dir\" FORCE)\nendif()\n\n# Make git tolerant to container bind-mount ownership (mirrors build script behavior)\nexecute_process(\n  COMMAND git config --global --add safe.directory ${CMAKE_SOURCE_DIR}\n  ERROR_QUIET\n  OUTPUT_QUIET\n)\n\n# Derive version defaults from version.sh if not provided\nset(_need_version OFF)\nforeach(v IN ITEMS EBPF_NET_MAJOR_VERSION EBPF_NET_MINOR_VERSION EBPF_NET_PATCH_VERSION EBPF_NET_COLLECTOR_BUILD_NUMBER)\n  if(NOT DEFINED ${v} OR ${v} STREQUAL \"\")\n    set(_need_version ON)\n  endif()\nendforeach()\n\nif(_need_version)\n  find_program(BASH_EXECUTABLE bash)\n  if(BASH_EXECUTABLE AND EXISTS \"${CMAKE_SOURCE_DIR}/version.sh\")\n    execute_process(\n      COMMAND ${BASH_EXECUTABLE} -c \"source '${CMAKE_SOURCE_DIR}/version.sh' >/dev/null 2>&1; printf '%s %s %s %s' \\\"$EBPF_NET_MAJOR_VERSION\\\" \\\"$EBPF_NET_MINOR_VERSION\\\" \\\"$EBPF_NET_PATCH_VERSION\\\" \\\"$EBPF_NET_COLLECTOR_BUILD_NUMBER\\\"\"\n      OUTPUT_VARIABLE _ver\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n      ERROR_QUIET\n    )\n  endif()\n\n  if(DEFINED _ver AND NOT _ver STREQUAL \"\")\n    string(REGEX REPLACE \" +\" \";\" _ver_list \"${_ver}\")\n    list(LENGTH _ver_list _len)\n    if(_len GREATER_EQUAL 4)\n      list(GET _ver_list 0 _maj)\n      list(GET _ver_list 1 _min)\n      list(GET _ver_list 2 _pat)\n      list(GET _ver_list 3 _bld)\n      if(NOT DEFINED EBPF_NET_MAJOR_VERSION OR EBPF_NET_MAJOR_VERSION STREQUAL \"\")\n        set(EBPF_NET_MAJOR_VERSION \"${_maj}\" CACHE STRING \"Major version\" FORCE)\n      endif()\n      if(NOT DEFINED EBPF_NET_MINOR_VERSION OR EBPF_NET_MINOR_VERSION STREQUAL \"\")\n        set(EBPF_NET_MINOR_VERSION \"${_min}\" CACHE STRING \"Minor version\" FORCE)\n      endif()\n      if(NOT DEFINED EBPF_NET_PATCH_VERSION OR EBPF_NET_PATCH_VERSION STREQUAL \"\")\n        set(EBPF_NET_PATCH_VERSION \"${_pat}\" CACHE STRING \"Patch version\" FORCE)\n      endif()\n      if(NOT DEFINED EBPF_NET_COLLECTOR_BUILD_NUMBER OR EBPF_NET_COLLECTOR_BUILD_NUMBER STREQUAL \"\")\n        set(EBPF_NET_COLLECTOR_BUILD_NUMBER \"${_bld}\" CACHE STRING \"Collector build number\" FORCE)\n      endif()\n    endif()\n  endif()\nendif()\n\n# Final fallback to keep project(VERSION ...) valid\nif(NOT DEFINED EBPF_NET_MAJOR_VERSION OR EBPF_NET_MAJOR_VERSION STREQUAL \"\")\n  set(EBPF_NET_MAJOR_VERSION \"0\" CACHE STRING \"Major version\" FORCE)\nendif()\nif(NOT DEFINED EBPF_NET_MINOR_VERSION OR EBPF_NET_MINOR_VERSION STREQUAL \"\")\n  set(EBPF_NET_MINOR_VERSION \"0\" CACHE STRING \"Minor version\" FORCE)\nendif()\nif(NOT DEFINED EBPF_NET_PATCH_VERSION OR EBPF_NET_PATCH_VERSION STREQUAL \"\")\n  set(EBPF_NET_PATCH_VERSION \"0\" CACHE STRING \"Patch version\" FORCE)\nendif()\nif(NOT DEFINED EBPF_NET_COLLECTOR_BUILD_NUMBER OR EBPF_NET_COLLECTOR_BUILD_NUMBER STREQUAL \"\")\n  set(EBPF_NET_COLLECTOR_BUILD_NUMBER \"0\" CACHE STRING \"Collector build number\" FORCE)\nendif()\n"
  },
  {
    "path": "cmake/docker-utils.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\noption(RUN_DOCKER_COMMANDS \"when disabled, prepares docker images to be built but stop short of running `docker` commands\" ON)\n\nadd_custom_target(docker)\nadd_custom_target(docker-registry)\n\n################\n# DOCKER IMAGE #\n################\n\nfunction(build_custom_docker_image IMAGE_NAME)\n  cmake_parse_arguments(ARG \"\" \"DOCKERFILE_PATH;DOCKERFILE_NAME;OUT_DIR\" \"ARGS;IMAGE_TAGS;FILES;BINARIES;DIRECTORIES;DEPENDS;OUTPUT_OF;ARTIFACTS_OF;DOCKER_REGISTRY;DEPENDENCY_OF\" ${ARGN})\n\n  # Dockerfile's directory defaults to the one containing CMakeLists.txt\n  if (NOT DEFINED ARG_DOCKERFILE_PATH)\n    set(ARG_DOCKERFILE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}\")\n  endif()\n\n  if (NOT DEFINED ARG_DOCKERFILE_NAME)\n    set(ARG_DOCKERFILE_NAME \"Dockerfile\")\n  endif()\n\n  if (NOT DEFINED ARG_IMAGE_TAGS)\n    if (DEFINED ENV{IMAGE_TAGS})\n      set(ARG_IMAGE_TAGS \"$ENV{IMAGE_TAGS}\")\n    else()\n      set(ARG_IMAGE_TAGS \"latest\")\n    endif()\n  endif()\n\n  if (NOT DEFINED ARG_DOCKER_REGISTRY)\n    if (DEFINED ENV{DOCKER_REGISTRY})\n      set(ARG_DOCKER_REGISTRY \"$ENV{DOCKER_REGISTRY}\")\n    else()\n      set(ARG_DOCKER_REGISTRY \"localhost:5000\")\n    endif()\n  endif()\n\n  foreach (FILE ${ARG_FILES})\n    if (FILE MATCHES \"^[/~]\")\n      list(APPEND FILES_LIST \"${FILE}\")\n    else()\n      list(APPEND FILES_LIST \"${CMAKE_CURRENT_SOURCE_DIR}/${FILE}\")\n    endif()\n  endforeach()\n\n  foreach (OUTPUT_OF ${ARG_OUTPUT_OF})\n    get_target_property(OUTPUT ${OUTPUT_OF} OUTPUT)\n    list(APPEND FILES_LIST \"${OUTPUT}\")\n  endforeach()\n\n  # everything in BINARIES is relative to current binary dir\n  foreach (BINARY ${ARG_BINARIES})\n    list(APPEND BINARIES_LIST \"${CMAKE_CURRENT_BINARY_DIR}/${BINARY}\")\n  endforeach()\n\n  foreach (ARTIFACTS_OF ${ARG_ARTIFACTS_OF})\n    list(APPEND FILES_LIST $<TARGET_FILE:${ARTIFACTS_OF}>)\n  endforeach()\n\n  set(DOCKER_ARGS \"\")\n  foreach (ARG ${ARG_ARGS})\n    list(APPEND DOCKER_ARGS \"--build-arg\" \"${ARG}\")\n  endforeach()\n\n  set(out_path \"${CMAKE_BINARY_DIR}/docker.out/${IMAGE_NAME}\")\n\n  if (NOT DEFINED ARG_OUT_DIR)\n    set(files_path \"${out_path}\")\n  else()\n    set(files_path \"${out_path}/${ARG_OUT_DIR}\")\n  endif()\n\n  ################\n  # docker build #\n  ################\n\n  add_custom_target(\n    \"${IMAGE_NAME}-docker\"\n    DEPENDS\n      ${ARG_DEPENDS}\n      ${ARG_OUTPUT_OF}\n      ${ARG_ARTIFACTS_OF}\n  )\n\n  add_dependencies(\n    docker\n      \"${IMAGE_NAME}-docker\"\n  )\n\n  foreach (DEPENDENT_TARGET ${ARG_DEPENDENCY_OF})\n    add_dependencies(\"${DEPENDENT_TARGET}-docker\" \"${IMAGE_NAME}-docker\")\n  endforeach()\n\n  add_custom_command(\n    TARGET\n      \"${IMAGE_NAME}-docker\"\n    POST_BUILD\n    COMMAND\n      ${CMAKE_COMMAND} -E make_directory \"${out_path}\"\n    COMMAND\n      ${CMAKE_COMMAND} -E make_directory \"${files_path}\"\n  )\n\n  add_custom_command(\n    TARGET\n      \"${IMAGE_NAME}-docker\"\n    POST_BUILD\n    WORKING_DIRECTORY\n      \"${out_path}\"\n    COMMAND\n      ${CMAKE_COMMAND} -E copy_if_different ${ARG_DOCKERFILE_PATH}/${ARG_DOCKERFILE_NAME} ${out_path}\n  )\n\n  if (DEFINED FILES_LIST)\n    add_custom_command(\n      TARGET\n        \"${IMAGE_NAME}-docker\"\n      POST_BUILD\n      WORKING_DIRECTORY\n        \"${out_path}\"\n      COMMAND\n        ${CMAKE_COMMAND} -E copy_if_different ${FILES_LIST} ${files_path}\n    )\n  endif()\n\n  if (DEFINED BINARIES_LIST)\n    add_custom_command(\n      TARGET\n        \"${IMAGE_NAME}-docker\"\n      POST_BUILD\n      WORKING_DIRECTORY\n        \"${out_path}\"\n      COMMAND\n        ${CMAKE_COMMAND} -E copy_if_different ${BINARIES_LIST} ${files_path}\n    )\n  endif()\n\n  foreach (DIRECTORY ${ARG_DIRECTORIES})\n    get_filename_component(DIR_NAME ${DIRECTORY} NAME)\n\n    add_custom_command(\n      TARGET\n        \"${IMAGE_NAME}-docker\"\n      POST_BUILD\n      WORKING_DIRECTORY\n        \"${out_path}\"\n      COMMAND\n        ${CMAKE_COMMAND} -E copy_directory\n          \"${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}\"\n          \"${files_path}/${DIR_NAME}\"\n    )\n  endforeach()\n\n  if (RUN_DOCKER_COMMANDS)\n    add_custom_command(\n      TARGET\n        \"${IMAGE_NAME}-docker\"\n      POST_BUILD\n      WORKING_DIRECTORY\n        \"${out_path}\"\n      # Intentionally build with host networking to avoid relying on\n      # rootless networking helpers (pasta/slirp4netns) inside nested CI\n      # containers. This improves reliability of podman builds in GitHub\n      # Actions and similar environments.\n    COMMAND\n        podman build --network host -t \"${IMAGE_NAME}\" ${DOCKER_ARGS} -f \"${ARG_DOCKERFILE_NAME}\" .\n    )\n  endif()\n\n  ###########################\n  # push to docker registry #\n  ###########################\n\n  add_custom_target(\n    \"${IMAGE_NAME}-docker-registry\"\n    DEPENDS\n      docker-registry-login\n      \"${IMAGE_NAME}-docker\"\n      ${ARG_DEPENDS}\n  )\n\n  add_dependencies(\n    docker-registry\n      \"${IMAGE_NAME}-docker-registry\"\n  )\n\n  foreach (DEPENDENT_TARGET ${ARG_DEPENDENCY_OF})\n    add_dependencies(\n      \"${DEPENDENT_TARGET}-docker-registry\"\n        \"${IMAGE_NAME}-docker-registry\"\n    )\n  endforeach()\n\n  if (RUN_DOCKER_COMMANDS)\n    # TODO: merge docker-registry-push.sh and push_docker_image.sh\n    foreach (IMAGE_TAG ${ARG_IMAGE_TAGS})\n      add_custom_command(\n        TARGET\n          \"${IMAGE_NAME}-docker-registry\"\n        POST_BUILD\n        COMMAND\n          podman tag \"${IMAGE_NAME}\" \"${IMAGE_NAME}:${IMAGE_TAG}\"\n        COMMAND\n          \"${CMAKE_SOURCE_DIR}/dev/docker-registry-push.sh\"\n            \"${IMAGE_NAME}\" \"${IMAGE_TAG}\" --no-login \"${ARG_DOCKER_REGISTRY}\"\n      )\n    endforeach()\n  endif()\nendfunction()\n\n###################\n# DOCKER REGISRY ##\n###################\n\nadd_custom_target(\n  docker-registry-login\n  COMMAND\n    \"${CMAKE_SOURCE_DIR}/dev/docker-registry-login.sh\" --no-vault env\n)\n"
  },
  {
    "path": "cmake/executable.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n###########################\n# static compilation target\nadd_library(static-executable INTERFACE)\ntarget_link_libraries(\n  static-executable\n  INTERFACE\n    address_sanitizer-static\n    -static-libstdc++\n)\n\n###########################\n# shared compilation target\nadd_library(shared-executable INTERFACE)\ntarget_link_libraries(\n  shared-executable\n  INTERFACE\n    address_sanitizer-shared\n)\n"
  },
  {
    "path": "cmake/geoip.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_path(LIBMAXMINDDB_INCLUDE_DIR NAMES maxminddb.h)\nfind_library(LIBMAXMINDDB_LIBRARIES_C NAMES \"libmaxminddb.a\")\nfind_package_handle_standard_args(LIBMAXMINDDB DEFAULT_MSG LIBMAXMINDDB_LIBRARIES_C LIBMAXMINDDB_INCLUDE_DIR)\nif(NOT LIBMAXMINDDB_FOUND)\n    message(FATAL_ERROR \"Could not find libmaxminddb. Build container should already have that set up\")\nendif()\nmessage(STATUS \"libmaxminddb INCLUDE_DIR: ${LIBMAXMINDDB_INCLUDE_DIR}\")\nmessage(STATUS \"libmaxminddb LIBRARY: ${LIBMAXMINDDB_LIBRARIES_C}\")\nadd_library(libmaxminddb INTERFACE)\ntarget_include_directories(libmaxminddb INTERFACE \"${LIBMAXMINDDB_INCLUDE_DIR}\")\ntarget_link_libraries(libmaxminddb INTERFACE \"${LIBMAXMINDDB_LIBRARIES_C}\")\n"
  },
  {
    "path": "cmake/libbpf.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Find libbpf library and create target\n\n# Since the .pc file has wrong prefix, let's find libbpf manually\n# Prefer static library over shared library\nfind_library(LIBBPF_LIBRARY\n  NAMES libbpf.a bpf\n  PATHS \n    ${CMAKE_INSTALL_PREFIX}/usr/lib64\n    ${CMAKE_INSTALL_PREFIX}/lib64\n  NO_DEFAULT_PATH\n  REQUIRED\n)\n\nfind_path(LIBBPF_INCLUDE_DIR\n  NAMES bpf/libbpf.h\n  PATHS \n    ${CMAKE_INSTALL_PREFIX}/usr/include\n    ${CMAKE_INSTALL_PREFIX}/include\n  NO_DEFAULT_PATH\n  REQUIRED\n)\n\nif(LIBBPF_LIBRARY AND LIBBPF_INCLUDE_DIR)\n  message(STATUS \"Found libbpf: ${LIBBPF_LIBRARY}\")\n  message(STATUS \"Found libbpf headers: ${LIBBPF_INCLUDE_DIR}\")\n  set(LIBBPF_FOUND TRUE)\nelse()\n  message(FATAL_ERROR \"libbpf not found in ${CMAKE_INSTALL_PREFIX}\")\nendif()\n\n# Create imported target for libbpf\nif(NOT TARGET libbpf::libbpf)\n  add_library(libbpf::libbpf UNKNOWN IMPORTED)\n  set_target_properties(libbpf::libbpf PROPERTIES\n    IMPORTED_LOCATION \"${LIBBPF_LIBRARY}\"\n    INTERFACE_INCLUDE_DIRECTORIES \"${LIBBPF_INCLUDE_DIR}\"\n  )\n  \n  # Add dependencies (libelf and zlib as mentioned in .pc file)\n  find_package(PkgConfig REQUIRED)\n  pkg_check_modules(LIBELF REQUIRED libelf)\n  pkg_check_modules(ZLIB REQUIRED zlib)\n  \n  set_property(TARGET libbpf::libbpf APPEND PROPERTY\n    INTERFACE_LINK_LIBRARIES \"${LIBELF_LIBRARIES}\" \"${ZLIB_LIBRARIES}\"\n  )\nendif()"
  },
  {
    "path": "cmake/libelf.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\npkg_check_modules(LIBELF REQUIRED libelf)\nadd_library(libelf INTERFACE)\ntarget_compile_options(libelf INTERFACE \"${LIBELF_CFLAGS}\")\ntarget_link_libraries(libelf INTERFACE \"${LIBELF_LINK_LIBRARIES}\")\nmessage(STATUS \"libelf CFLAGS: ${LIBELF_CFLAGS}\")\nmessage(STATUS \"libelf LIBRARIES: ${LIBELF_LINK_LIBRARIES}\")\n"
  },
  {
    "path": "cmake/llvm.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(LLVM REQUIRED CONFIG)\nmessage(STATUS \"Found LLVM ${LLVM_PACKAGE_VERSION}\")\nmessage(STATUS \"Using LLVMConfig.cmake in: ${LLVM_DIR}\")\n\nadd_library(llvm INTERFACE)\nadd_library(llvm-shared INTERFACE)\nadd_library(llvm-interface INTERFACE)\ntarget_include_directories(\n  llvm-interface\n  INTERFACE\n    ${LLVM_INCLUDE_DIRS}\n)\nllvm_map_components_to_libnames(\n  LLVM_LIBS\n    core\n    mcjit\n    native\n    executionengine\n    scalaropts\n)\ntarget_compile_definitions(llvm-interface INTERFACE ${LLVM_DEFINITIONS})\ntarget_link_libraries(llvm INTERFACE llvm-interface ${LLVM_LIBS})\ntarget_link_libraries(llvm-shared INTERFACE llvm-interface -L${LLVM_LIBRARY_DIRS} -lLLVM)\n"
  },
  {
    "path": "cmake/lz4.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_path(LZ4_INCLUDE_DIR lz4.h)\nfind_library(LZ4_LIBRARY NAMES \"liblz4.a\")\nfind_package_handle_standard_args(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR)\nif(NOT LZ4_FOUND)\n  message(FATAL_ERROR \"Could not find lz4. Build container should already have that set up\")\nendif()\nmessage(STATUS \"lz4 INCLUDE_DIR: ${LZ4_INCLUDE_DIR}\")\nmessage(STATUS \"lz4 LIBRARY: ${LZ4_LIBRARY}\")\nadd_library(lz4 INTERFACE)\ntarget_include_directories(lz4 INTERFACE \"${LZ4_INCLUDE_DIR}\")\ntarget_link_libraries(lz4 INTERFACE \"${LZ4_LIBRARY}\")\n"
  },
  {
    "path": "cmake/openssl.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_package(OpenSSL REQUIRED)\n"
  },
  {
    "path": "cmake/protobuf.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n\n\nfind_package(Protobuf REQUIRED)\n# Avoid the very slow pkg-config-based RE2 lookup inside gRPC's Findre2.cmake\n# by predefining the imported target when possible. This short-circuits the\n# module and prevents multiple costly pkg-config invocations.\nif(NOT TARGET re2::re2)\n  find_library(_RE2_LIB NAMES re2)\n  find_path(_RE2_INCLUDE_DIR NAMES re2/re2.h)\n  if(_RE2_LIB AND _RE2_INCLUDE_DIR)\n    add_library(re2::re2 INTERFACE IMPORTED)\n    set_property(TARGET re2::re2 PROPERTY INTERFACE_LINK_LIBRARIES \"${_RE2_LIB}\")\n    set_property(TARGET re2::re2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES \"${_RE2_INCLUDE_DIR}\")\n    message(STATUS \"Using preconfigured RE2 (no pkg-config): ${_RE2_LIB}\")\n  endif()\nendif()\nfind_package(gRPC CONFIG REQUIRED)\n\nfind_program(GRPC_CPP_PLUGIN_LOCATION grpc_cpp_plugin REQUIRED)\nif(GRPC_CPP_PLUGIN_LOCATION)\n    message(STATUS \"Found grpc_cpp_plugin: ${GRPC_CPP_PLUGIN_LOCATION}\")\nelse()\n    message(FATAL_ERROR \"grpc_cpp_plugin not found\")\nendif()\n\nset(GO_PROTOBUF_ANNOTATIONS_DIR /usr/local/go/src/github.com/grpc-ecosystem/grpc-gateway)\nset(GO_PROTOBUF_GOOGLEAPIS_DIR /usr/local/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis)\n\n# GO should be given the go-module name associated with the built packages\nfunction (build_protobuf NAME)\n  cmake_parse_arguments(ARG \"CPP;GRPC;GRPC_GATEWAY\" \"GO\" \"DEPENDS;DEPENDENCY_OF\" ${ARGN})\n\n  set(TARGET_PREPARE \"${NAME}-protobuf-prepare\")\n  set(TARGET \"${NAME}-protobuf\")\n\n  add_custom_target(\n    \"${TARGET_PREPARE}\"\n    DEPENDS\n      ${ARG_DEPENDS}\n  )\n\n  set(\n    PROTOBUF_ARGS\n      -I\"${CMAKE_CURRENT_SOURCE_DIR}\"\n  )\n\n  if (ARG_CPP)\n    list(\n      APPEND\n      PROTOBUF_ARGS\n        -I/usr/local/include\n        --cpp_out=\"${CMAKE_CURRENT_BINARY_DIR}/generated\"\n    )\n\n    add_custom_command(\n      TARGET\n        \"${TARGET_PREPARE}\"\n      POST_BUILD\n      COMMAND\n        ${CMAKE_COMMAND} -E make_directory\n          \"${CMAKE_CURRENT_BINARY_DIR}/generated\"\n    )\n\n    set(\n      GEN_FILES_CPP\n        \"${CMAKE_CURRENT_BINARY_DIR}/generated/${NAME}.pb.h\"\n        \"${CMAKE_CURRENT_BINARY_DIR}/generated/${NAME}.pb.cc\"\n    )\n\n    if (ARG_GRPC)\n      list(\n        APPEND\n        PROTOBUF_ARGS\n          --plugin=protoc-gen-grpc=\"${GRPC_CPP_PLUGIN_LOCATION}\"\n          --grpc_out=\"${CMAKE_CURRENT_BINARY_DIR}/generated\"\n      )\n\n      list(\n        APPEND\n        GEN_FILES_CPP\n          \"${CMAKE_CURRENT_BINARY_DIR}/generated/${NAME}.grpc.pb.h\"\n          \"${CMAKE_CURRENT_BINARY_DIR}/generated/${NAME}.grpc.pb.cc\"\n      )\n    endif()\n  endif()\n\n  if (DEFINED ARG_GO)\n    get_target_property(MOD_BUILD_DIR \"${ARG_GO}-go-module\" MOD_BUILD_DIR)\n    \n    list(\n      APPEND\n      PROTOBUF_ARGS\n        --go_out=\"${GO_PATH_SRC}\"\n        --go-grpc_out=\"${GO_PATH_SRC}\"\n    )\n\n    add_dependencies(\n      \"${TARGET_PREPARE}\"\n        \"${ARG_GO}-go-module\"\n    )\n\n    set(\n      GEN_FILES_GO\n        \"${MOD_BUILD_DIR}/${NAME}.pb.go\"\n        \"${MOD_BUILD_DIR}/${NAME}_grpc.pb.go\"\n    )\n\n    if (ARG_GRPC)\n      list(\n        APPEND\n        PROTOBUF_ARGS\n          -I\"${GO_PROTOBUF_ANNOTATIONS_DIR}\"\n          -I\"${GO_PROTOBUF_GOOGLEAPIS_DIR}\"\n          --grpc-gateway_out=\"logtostderr=true:${GO_PATH_SRC}\"\n      )\n\n      list(\n        APPEND\n        GEN_FILES_GO\n          \"${MOD_BUILD_DIR}/${NAME}.pb.gw.go\"\n      )\n    endif()\n  endif()\n\n  list(\n    APPEND\n    PROTOBUF_ARGS\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.proto\"\n  )\n\n  set(GEN_FILES_ALL ${GEN_FILES_CPP})\n  if (DEFINED ARG_GO)\n    list(APPEND GEN_FILES_ALL ${GEN_FILES_GO})\n  endif()\n\n  add_custom_command(\n    OUTPUT\n      ${GEN_FILES_ALL}\n    COMMAND\n      protoc\n        ${PROTOBUF_ARGS}\n    DEPENDS\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.proto\"\n      ${TARGET_PREPARE}\n  )\n\n  add_custom_target(\n    \"${TARGET}\"\n    DEPENDS\n      ${GEN_FILES_ALL}\n  )\n\n  if (ARG_CPP)\n    set(CPP_TARGET \"${NAME}-cpp-protobuf\")\n\n    set_source_files_properties(\n      ${GEN_FILES_CPP}\n      PROPERTIES\n        GENERATED TRUE\n    )\n\n    add_library(\n      \"${CPP_TARGET}\"\n      STATIC\n        ${GEN_FILES_CPP}\n    )\n\n    target_link_libraries(\n      \"${CPP_TARGET}\"\n        protobuf::libprotobuf\n        gRPC::grpc++\n    )\n\n    target_include_directories(\n      \"${CPP_TARGET}\"\n      PUBLIC\n        \"${CMAKE_CURRENT_BINARY_DIR}\"\n    )\n\n  endif()\n\n  if (DEFINED ARG_GO)\n    set(GO_TARGET \"${NAME}-go-protobuf\")\n\n    set_source_files_properties(\n    ${GEN_FILES_GO}\n    PROPERTIES\n      GENERATED TRUE\n    )\n\n    add_custom_target(\n      \"${GO_TARGET}\"\n      DEPENDS\n        ${GEN_FILES_GO}\n    )\n  endif()\n\n  foreach(DEPENDENCY ${ARG_DEPENDENCY_OF})\n    add_dependencies(${DEPENDENCY} ${TARGET_PREPARE})\n  endforeach()\nendfunction()\n"
  },
  {
    "path": "cmake/render.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# Aggregate target to copy all generated Rust crates (apps + aggregator)\nif(NOT TARGET render_source_dir)\n  add_custom_target(render_source_dir)\nendif()\n\nfunction(render_compile INPUT_DIR)\n  cmake_parse_arguments(ARG \"\" \"OUTPUT_DIR;PACKAGE;COMPILER\" \"APPS;DEPENDS\" ${ARGN})\n\n  if(DEFINED ARG_OUTPUT_DIR)\n    set(OUTPUT_DIR ${ARG_OUTPUT_DIR})\n  else()\n    set(OUTPUT_DIR \"${CMAKE_BINARY_DIR}/generated\")\n  endif()\n\n  if(DEFINED ARG_PACKAGE)\n    set(PACKAGE ${ARG_PACKAGE})\n  else()\n    set(PACKAGE \"default\")\n  endif()\n\n  if(DEFINED ARG_COMPILER)\n    set(RENDER_COMPILER ${ARG_COMPILER})\n  else()\n    get_property(\n      RENDER_COMPILER\n      TARGET\n        render_compiler\n      PROPERTY\n        RENDER_COMPILER_PATH\n    )\n  endif()\n\n  set(RENDER_${PACKAGE}_OUTPUTS \"\")\n\n  foreach(APP ${ARG_APPS})\n    set(\n      RENDER_${PACKAGE}_${APP}_DESCRIPTOR\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/descriptor.cc\"\n    )\n    set(\n      RENDER_${PACKAGE}_${APP}_HASH\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/hash.c\"\n    )\n    set(\n      RENDER_${PACKAGE}_${APP}_WRITER\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/writer.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/writer.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/encoder.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/encoder.cc\"\n    )\n    set(\n      RENDER_${PACKAGE}_${APP}_OUTPUTS\n        ${RENDER_${PACKAGE}_${APP}_HASH}\n        ${RENDER_${PACKAGE}_${APP}_DESCRIPTOR}\n        ${RENDER_${PACKAGE}_${APP}_WRITER}\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/index.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/index.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/containers.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/containers.inl\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/containers.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/keys.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/handles.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/handles.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/auto_handles.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/auto_handles.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/weak_refs.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/weak_refs.inl\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/weak_refs.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/modifiers.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/modifiers.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/spans.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/spans.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/span_base.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/connection.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/connection.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/protocol.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/protocol.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/transform_builder.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/transform_builder.cc\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/parsed_message.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/wire_message.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/meta.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/bpf.h\"\n        # Rust per-app crate files live under src/\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/src/wire_messages.rs\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/src/parsed_message.rs\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/src/encoder.rs\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/src/hash.rs\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/Cargo.toml\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/src/lib.rs\"\n        # Headers that are also generated alongside sources\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/descriptor.h\"\n        \"${OUTPUT_DIR}/${PACKAGE}/${APP}/hash.h\"\n    )\n    list(\n      APPEND\n      RENDER_${PACKAGE}_OUTPUTS\n        ${RENDER_${PACKAGE}_${APP}_OUTPUTS}\n    )\n  endforeach()\n\n  # Per-package Rust aggregator crate outputs (generated by the render compiler)\n  list(APPEND RENDER_${PACKAGE}_OUTPUTS \"${OUTPUT_DIR}/${PACKAGE}/Cargo.toml\")\n  list(APPEND RENDER_${PACKAGE}_OUTPUTS \"${OUTPUT_DIR}/${PACKAGE}/src/lib.rs\")\n\n  list(\n    APPEND\n    RENDER_${PACKAGE}_OUTPUTS\n      \"${OUTPUT_DIR}/${PACKAGE}/metrics.h\"\n  )\n\n  set_source_files_properties(\n    ${RENDER_${PACKAGE}_OUTPUTS}\n    PROPERTIES\n      GENERATED TRUE\n  )\n\n  file(\n    GLOB\n    RENDER_INPUTS\n      \"${INPUT_DIR}/*.render\"\n  )\n\n  # Generate sources\n  #\n  add_custom_command(\n    OUTPUT\n      ${RENDER_${PACKAGE}_OUTPUTS}\n    WORKING_DIRECTORY\n      ${INPUT_DIR}\n    COMMAND\n      ${CMAKE_COMMAND} -E make_directory \"${OUTPUT_DIR}\"\n    COMMAND\n      java -jar ${RENDER_COMPILER} -i . -o \"${OUTPUT_DIR}\"\n    DEPENDS\n      ${RENDER_INPUTS}\n      ${RENDER_COMPILER}\n      ${ARG_DEPENDS}\n  )\n  add_custom_target(\n    render_compile_${PACKAGE}\n    DEPENDS\n      ${RENDER_${PACKAGE}_OUTPUTS}\n  )\n\n  # Generated sources interface library\n  #\n  add_library(\n    render_${PACKAGE}_artifacts\n    INTERFACE\n  )\n  target_include_directories(\n    render_${PACKAGE}_artifacts\n    INTERFACE\n      \"${OUTPUT_DIR}\"\n  )\n  add_dependencies(\n    render_${PACKAGE}_artifacts\n      render_compile_${PACKAGE}\n  )\n\n  # Generated sources app libraries\n  #\n  foreach(APP ${ARG_APPS})\n    add_library(\n      render_${PACKAGE}_${APP}\n      STATIC\n      EXCLUDE_FROM_ALL\n        ${RENDER_${PACKAGE}_${APP}_OUTPUTS}\n    )\n    target_include_directories(\n      render_${PACKAGE}_${APP}\n      PUBLIC\n        ${OUTPUT_DIR}\n    )\n    target_link_libraries(\n      render_${PACKAGE}_${APP}\n      PUBLIC\n        fixed_hash\n        render_${PACKAGE}_artifacts\n    )\n\n    add_library(\n      render_${PACKAGE}_${APP}_hash\n      STATIC\n      EXCLUDE_FROM_ALL\n        ${RENDER_${PACKAGE}_${APP}_HASH}\n    )\n    target_include_directories(\n      render_${PACKAGE}_${APP}_hash\n      PUBLIC\n        ${OUTPUT_DIR}\n    )\n    target_link_libraries(\n      render_${PACKAGE}_${APP}_hash\n      PUBLIC\n        render_${PACKAGE}_artifacts\n    )\n\n    add_library(\n      render_${PACKAGE}_${APP}_descriptor\n      STATIC\n      EXCLUDE_FROM_ALL\n        ${RENDER_${PACKAGE}_${APP}_DESCRIPTOR}\n    )\n    target_include_directories(\n      render_${PACKAGE}_${APP}_descriptor\n      PUBLIC\n        ${OUTPUT_DIR}\n    )\n    target_link_libraries(\n      render_${PACKAGE}_${APP}_descriptor\n      PUBLIC\n        render_${PACKAGE}_artifacts\n    )\n\n    add_library(\n      render_${PACKAGE}_${APP}_writer\n      STATIC\n      EXCLUDE_FROM_ALL\n        ${RENDER_${PACKAGE}_${APP}_WRITER}\n    )\n    target_include_directories(\n      render_${PACKAGE}_${APP}_writer\n      PUBLIC\n        ${OUTPUT_DIR}\n    )\n    target_link_libraries(\n      render_${PACKAGE}_${APP}_writer\n      PUBLIC\n        render_${PACKAGE}_artifacts\n    )\n\n  endforeach()\n\n  # Build a single Rust aggregator staticlib from the source workspace (crates/),\n  # not from the generated/ tree. The aggregator crate is expected at\n  # ${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE} and is updated manually via\n  # the render_source_dir targets below.\n  string(REPLACE \":\" \"_\" _pkg_safe \"${PACKAGE}\")\n  set(RUST_AGG_DIR \"${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE}\")\n\n  # Build the aggregator staticlib\n  set(RUST_AGG_TARGET_DIR \"${CMAKE_BINARY_DIR}/cargo/${PACKAGE}-agg\")\n  set(RUST_AGG_CRATE_NAME \"encoder_${_pkg_safe}_all\")\n  set(RUST_AGG_STATICLIB \"${RUST_AGG_TARGET_DIR}/release/lib${RUST_AGG_CRATE_NAME}.a\")\n\n  add_custom_command(\n    OUTPUT \"${RUST_AGG_STATICLIB}\"\n    WORKING_DIRECTORY \"${RUST_AGG_DIR}\"\n    COMMAND ${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${RUST_AGG_TARGET_DIR} RUSTFLAGS=-Cpanic=abort cargo build --release\n    VERBATIM\n  )\n  # Target `render_rust_${PACKAGE}_build` depends on the library being built\n  add_custom_target(render_rust_${PACKAGE}_build\n    DEPENDS \"${RUST_AGG_STATICLIB}\")\n\n  # Users can add `render_rust_${PACKAGE}` to their link libraries to link the Rust staticlib\n  add_library(render_rust_${PACKAGE} STATIC IMPORTED GLOBAL)\n  set_target_properties(render_rust_${PACKAGE} PROPERTIES IMPORTED_LOCATION \"${RUST_AGG_STATICLIB}\")\n  add_dependencies(render_rust_${PACKAGE} render_rust_${PACKAGE}_build)\n\n  # Note: do not implicitly link the Rust staticlib into writer libs.\n  # Consumers that need the Rust aggregator (e.g., reducer) should link\n  # target `render_rust_${PACKAGE}` explicitly.\n\n  # Developer-only: copy generated Rust crates into the source tree for review/commit\n  # This target is safe in CI because it is not part of the default build.\n  # Destination will be: ${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE}/${APP}\n  set(_render_copy_targets)\n  foreach(APP ${ARG_APPS})\n    set(_src_dir  \"${OUTPUT_DIR}/${PACKAGE}/${APP}\")\n    set(_dest_dir \"${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE}/${APP}\")\n    add_custom_target(\n      copy_render_crate_${PACKAGE}_${APP}\n      COMMAND ${CMAKE_COMMAND} -E remove_directory \"${_dest_dir}\"\n      COMMAND ${CMAKE_COMMAND} -E make_directory \"${_dest_dir}/src\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/Cargo.toml\" \"${_dest_dir}/Cargo.toml\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/src/lib.rs\" \"${_dest_dir}/src/lib.rs\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/src/encoder.rs\" \"${_dest_dir}/src/encoder.rs\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/src/wire_messages.rs\" \"${_dest_dir}/src/wire_messages.rs\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/src/parsed_message.rs\" \"${_dest_dir}/src/parsed_message.rs\"\n      COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_src_dir}/src/hash.rs\" \"${_dest_dir}/src/hash.rs\"\n      DEPENDS render_compile_${PACKAGE}\n      VERBATIM\n    )\n    list(APPEND _render_copy_targets copy_render_crate_${PACKAGE}_${APP})\n  endforeach()\n\n  # Also copy the generated aggregator crate into the source tree at\n  # ${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE}\n  set(_agg_src_dir  \"${OUTPUT_DIR}/${PACKAGE}\")\n  set(_agg_dest_dir \"${PROJECT_SOURCE_DIR}/crates/render/${PACKAGE}\")\n  add_custom_target(\n    copy_render_crate_${PACKAGE}_aggregator\n    COMMAND ${CMAKE_COMMAND} -E make_directory \"${_agg_dest_dir}/src\"\n    COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_agg_src_dir}/Cargo.toml\" \"${_agg_dest_dir}/Cargo.toml\"\n    COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${_agg_src_dir}/src/lib.rs\" \"${_agg_dest_dir}/src/lib.rs\"\n    DEPENDS render_compile_${PACKAGE}\n    VERBATIM\n  )\n  list(APPEND _render_copy_targets copy_render_crate_${PACKAGE}_aggregator)\n  # Developer entrypoint per package to avoid name collisions with other projects\n  add_custom_target(render_source_dir_${PACKAGE})\n  add_dependencies(render_source_dir_${PACKAGE} ${_render_copy_targets})\n  add_dependencies(render_source_dir render_source_dir_${PACKAGE})\nendfunction()\n"
  },
  {
    "path": "cmake/rust_cxxbridge.cmake",
    "content": "## Copyright The OpenTelemetry Authors\n## SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# add_rust_cxxbridge(<target_name> <rust_src>)\n# - Runs `cxxbridge` on the given Rust source to generate a header and C++ source.\n# - Generates:\n#   - Header: ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/<target_name>.h\n#   - Source: ${CMAKE_CURRENT_BINARY_DIR}/<target_name>.cc\n# - Produces a STATIC library <target_name> that compiles the generated C++ source\n#   and exposes the header directory as a PUBLIC include dir so consumers can\n#   include it via `#include <<target_name>.h>`.\nfunction(add_rust_cxxbridge TARGET_NAME RUST_SRC)\n  if(NOT TARGET_NAME)\n    message(FATAL_ERROR \"add_rust_cxxbridge requires a target name\")\n  endif()\n\n  if(NOT RUST_SRC)\n    message(FATAL_ERROR \"add_rust_cxxbridge requires a Rust source path\")\n  endif()\n\n  # Resolve Rust source to an absolute path for dependable dependency tracking.\n  if(NOT IS_ABSOLUTE \"${RUST_SRC}\")\n    set(RUST_SRC \"${CMAKE_CURRENT_SOURCE_DIR}/${RUST_SRC}\")\n  endif()\n\n  # Find the cxxbridge CLI installed via `cargo install cxxbridge-cmd`.\n  find_program(CXXBRIDGE_EXECUTABLE NAMES cxxbridge REQUIRED)\n\n  # Output paths\n  set(CXXBRIDGE_OUT_DIR \"${CMAKE_CURRENT_BINARY_DIR}/cxxbridge\")\n  set(CXXBRIDGE_HEADER \"${CXXBRIDGE_OUT_DIR}/${TARGET_NAME}.h\")\n  set(CXXBRIDGE_CC     \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.cc\")\n\n  # Ensure output directory exists at configure time\n  file(MAKE_DIRECTORY \"${CXXBRIDGE_OUT_DIR}\")\n\n  # Generate header\n  add_custom_command(\n    OUTPUT \"${CXXBRIDGE_HEADER}\"\n    COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${CXXBRIDGE_OUT_DIR}\"\n    COMMAND sh -lc \"\\\"${CXXBRIDGE_EXECUTABLE}\\\" \\\"${RUST_SRC}\\\" --header > \\\"${CXXBRIDGE_HEADER}\\\"\"\n    DEPENDS \"${RUST_SRC}\"\n    COMMENT \"Generating cxxbridge header ${CXXBRIDGE_HEADER} from ${RUST_SRC}\"\n    VERBATIM\n  )\n\n  # Generate C++ source (depends on header so include dir exists and header is fresh)\n  add_custom_command(\n    OUTPUT \"${CXXBRIDGE_CC}\"\n    COMMAND sh -lc \"\\\"${CXXBRIDGE_EXECUTABLE}\\\" \\\"${RUST_SRC}\\\" > \\\"${CXXBRIDGE_CC}\\\"\"\n    DEPENDS \"${RUST_SRC}\" \"${CXXBRIDGE_HEADER}\"\n    COMMENT \"Generating cxxbridge source ${CXXBRIDGE_CC} from ${RUST_SRC}\"\n    VERBATIM\n  )\n\n  # Build a static library from the generated C++ and expose the header dir\n  add_library(${TARGET_NAME} STATIC \"${CXXBRIDGE_CC}\")\n  target_include_directories(${TARGET_NAME} PUBLIC \"${CXXBRIDGE_OUT_DIR}\")\nendfunction()\n\n"
  },
  {
    "path": "cmake/rust_main.cmake",
    "content": "include_guard()\n\nfunction(add_rust_main)\n  cmake_parse_arguments(ARG \"\" \"TARGET;STRIPPED_TARGET;PACKAGE;BIN_NAME;PROJ_DIR;RUST_BIN_TARGET_DIR;DUMMY_TARGET\" \"LINK_LIBS\" ${ARGN})\n\n  if(NOT DEFINED ARG_TARGET)\n    message(FATAL_ERROR \"add_rust_main: TARGET is required\")\n  endif()\n  if(NOT DEFINED ARG_STRIPPED_TARGET)\n    message(FATAL_ERROR \"add_rust_main: STRIPPED_TARGET is required\")\n  endif()\n  if(NOT DEFINED ARG_PACKAGE)\n    message(FATAL_ERROR \"add_rust_main: PACKAGE is required (Cargo package)\")\n  endif()\n  if(NOT DEFINED ARG_BIN_NAME)\n    message(FATAL_ERROR \"add_rust_main: BIN_NAME is required (expected executable name)\")\n  endif()\n  if(NOT ARG_LINK_LIBS)\n    message(FATAL_ERROR \"add_rust_main: LINK_LIBS is required (C++ link libraries)\")\n  endif()\n\n  if(NOT DEFINED ARG_PROJ_DIR)\n    set(ARG_PROJ_DIR \"${PROJECT_SOURCE_DIR}\")\n  endif()\n  if(NOT DEFINED ARG_RUST_BIN_TARGET_DIR)\n    set(ARG_RUST_BIN_TARGET_DIR \"${CMAKE_BINARY_DIR}/target\")\n  endif()\n  if(NOT DEFINED ARG_DUMMY_TARGET)\n    set(ARG_DUMMY_TARGET \"${ARG_TARGET}-dummy\")\n  endif()\n\n  # 1) Create a tiny dummy main to force generation of a link.txt for the link line\n  set(_dummy_main \"${CMAKE_CURRENT_BINARY_DIR}/${ARG_DUMMY_TARGET}_main.cc\")\n  file(WRITE \"${_dummy_main}\" \"int main(int, char**) { return 0; }\\n\")\n\n  add_executable(${ARG_DUMMY_TARGET} \"${_dummy_main}\")\n  target_link_libraries(\n    ${ARG_DUMMY_TARGET}\n    PUBLIC\n      ${ARG_LINK_LIBS}\n      static-executable\n  )\n\n  # Path to dummy's link.txt produced by the generator\n  set(_link_file \"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${ARG_DUMMY_TARGET}.dir/link.txt\")\n\n  # 2) Build the Rust binary via cargo using link hints from the dummy\n  set(_rust_bin_path \"${ARG_RUST_BIN_TARGET_DIR}/release/${ARG_BIN_NAME}\")\n\n  add_custom_command(\n    OUTPUT \"${_rust_bin_path}\"\n    COMMAND ${CMAKE_COMMAND}\n      -DLINK_FILE=${_link_file}\n      -DBIN_DIR=${CMAKE_BINARY_DIR}\n      -DPROJ_DIR=${ARG_PROJ_DIR}\n      -DRUST_BIN_TARGET_DIR=${ARG_RUST_BIN_TARGET_DIR}\n      -DRUST_PACKAGE=${ARG_PACKAGE}\n      -P ${CMAKE_SOURCE_DIR}/cmake/cargo_build_rust.cmake\n    WORKING_DIRECTORY ${ARG_PROJ_DIR}\n    DEPENDS ${ARG_LINK_LIBS}\n    VERBATIM\n  )\n\n  add_custom_target(\n    ${ARG_TARGET}\n    DEPENDS \"${_rust_bin_path}\"\n  )\n\n  # 3) Stripped variant\n  add_custom_command(\n    OUTPUT ${_rust_bin_path}-stripped\n    COMMAND ${CMAKE_SOURCE_DIR}/dev/strip-symbols.sh ${_rust_bin_path}\n    DEPENDS ${_rust_bin_path}\n    VERBATIM\n  )\n  add_custom_target(\n    ${ARG_STRIPPED_TARGET}\n    DEPENDS \"${_rust_bin_path}-stripped\"\n  )\n  set_property(\n    TARGET ${ARG_STRIPPED_TARGET}\n    PROPERTY\n      \"OUTPUT\" \"${_rust_bin_path}-stripped\"\n  )\n\nendfunction()\n"
  },
  {
    "path": "cmake/sanitizer.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n######\n# ASAN\n######\n\noption(USE_ADDRESS_SANITIZER \"Use Address Sanitizer in compilation\" OFF)\n\nadd_library(address_sanitizer-static INTERFACE)\nadd_library(address_sanitizer-shared INTERFACE)\n\nmessage(STATUS \"Address Sanitizer is ${USE_ADDRESS_SANITIZER}\")\n\nif (USE_ADDRESS_SANITIZER)\n  target_compile_options(\n    address_sanitizer-shared\n    INTERFACE\n      -fsanitize=address\n      -U_FORTIFY_SOURCE\n      -fno-stack-protector\n      -fno-omit-frame-pointer\n      -U__SANITIZE_ADDRESS__\n  )\n  target_compile_definitions(\n    address_sanitizer-shared\n    INTERFACE\n      NDEBUG_SANITIZER\n  )\n\n  target_compile_options(\n    address_sanitizer-static\n    INTERFACE\n      -fsanitize=address\n      -U_FORTIFY_SOURCE\n      -fno-stack-protector\n      -fno-omit-frame-pointer\n      -U__SANITIZE_ADDRESS__\n  )\n  target_compile_definitions(\n    address_sanitizer-static\n    INTERFACE\n      NDEBUG_SANITIZER\n  )\n\n  target_link_libraries(\n    address_sanitizer-shared\n    INTERFACE\n      -static-libasan\n      -static-libstdc++\n      -fsanitize=address\n  )\n\n  target_link_libraries(\n    address_sanitizer-static\n    INTERFACE\n      -static-libasan\n      -static-libstdc++\n      -fsanitize=address\n  )\nendif()\n\n#######\n# UBSAN\n#######\n\noption(USE_UNDEFINED_BEHAVIOR_SANITIZER \"Use Undefined Behavior Sanitizer in compilation\" OFF)\n\nadd_library(undefined_behavior_sanitizer-static INTERFACE)\nadd_library(undefined_behavior_sanitizer-shared INTERFACE)\n\nmessage(STATUS \"Undefined Behavior Sanitizer is ${USE_UNDEFINED_BEHAVIOR_SANITIZER}\")\n\nif (USE_UNDEFINED_BEHAVIOR_SANITIZER)\n  target_compile_options(\n    undefined_behavior_sanitizer-shared\n    INTERFACE\n      -fsanitize=undefined\n      -U_FORTIFY_SOURCE\n      -fno-stack-protector\n      -fno-omit-frame-pointer\n  )\n\n  target_compile_options(\n    undefined_behavior_sanitizer-static\n    INTERFACE\n      -fsanitize=undefined\n      -U_FORTIFY_SOURCE\n      -fno-stack-protector\n      -fno-omit-frame-pointer\n  )\n\n  target_link_libraries(\n    undefined_behavior_sanitizer-shared\n    INTERFACE\n      -static-libubsan\n      -static-libstdc++\n      -fsanitize=undefined\n  )\n\n  target_link_libraries(\n    undefined_behavior_sanitizer-static\n    INTERFACE\n      -static-libubsan\n      -static-libstdc++\n      -fsanitize=undefined\n  )\nendif()\n"
  },
  {
    "path": "cmake/shell.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\n# Runs linters on the given set of shell scripts.\n#\n# The scripts are bundled as a single cmake target.\n#\n# Positional Arguments:\n#   1. TARGET:\n#     The name of the target to create for the bundle.\n#\n# Named Arguments:\n#   SOURCES:\n#     The list of scripts to add to the bundle, relative to the current source directory.\n#\n#   DEPENDS:\n#     The list of targets this bundle explicitly depends on.\n#\n# Output:\n#   The list of files linted (`SOURCES`) is exposed through the property `OUTPUT` and can be\n#   accessed with the expression `$<TARGET_PROPERTY:TARGET,OUTPUT>`, where `TARGET` is the\n#   target name given to the bundle. E.g.: `SET(script_list $<TARGET_PROPERTY:my_bundle,OUTPUT>)`.\n#\n# Usage:\n#   lint_shell_script_bundle(\n#     my_target\n#     SOURCES\n#       my_script_1.sh\n#       my_script_2.sh\n#     DEPENDS\n#       some_dependency_1\n#       some_dependency_2\n#   )\nfunction(lint_shell_script_bundle TARGET)\n  cmake_parse_arguments(ARG \"\" \"\" \"SOURCES;DEPENDS\" ${ARGN})\n\n  add_custom_target(\n    ${TARGET}\n    ALL\n    DEPENDS ${ARG_DEPENDS}\n  )\n\n  foreach(SOURCE ${ARG_SOURCES})\n    add_custom_command(\n      TARGET\n        ${TARGET}\n      POST_BUILD\n      WORKING_DIRECTORY\n        \"${CMAKE_CURRENT_SOURCE_DIR}\"\n      COMMAND\n        shellcheck -x \"${SOURCE}\"\n    )\n    list(APPEND OUTPUT_LIST \"${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}\")\n  endforeach()\n\n  set_property(TARGET ${TARGET} PROPERTY \"OUTPUT\" ${OUTPUT_LIST})\nendfunction()\n"
  },
  {
    "path": "cmake/spdlog.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nadd_library(spdlog INTERFACE)\ntarget_include_directories(spdlog INTERFACE \"${CMAKE_SOURCE_DIR}/ext/spdlog/include\")\ntarget_link_libraries(spdlog INTERFACE)\n"
  },
  {
    "path": "cmake/test.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nenable_testing()\n\nlink_directories(\"${CMAKE_INSTALL_PREFIX}/lib\")\n\n# This is for tests that aren't run as part of `make test`. These are useful\n# for manual or component tests that you don't want to necessarily run as\n# part of the unit test suite.\nfunction(add_standalone_gtest testName)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs SRCS DEPS)\n  cmake_parse_arguments(add_standalone_gtest\n      \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  add_executable(${testName}\n                 ${add_standalone_gtest_SRCS})\n  target_link_libraries(${testName}\n      gmock_main gtest gmock \"-pthread\"\n      ${add_standalone_gtest_DEPS})\nendfunction (add_standalone_gtest)\n\n# This function is use for gtest/gmock-dependent libraries for use in other\n# unit tests (i.e. does not link a `main` symbol).\nfunction(add_gtest_lib testName)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs SRCS DEPS)\n  cmake_parse_arguments(add_gtest_lib\n      \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  add_library(${testName}\n              ${add_gtest_lib_SRCS})\n  target_link_libraries(${testName}\n      gtest gmock \"-pthread\" ${add_gtest_lib_DEPS})\nendfunction (add_gtest_lib)\n\n# Adds a unit test named `${NAME}_test`.\n#\n# The file `${NAME}_test.cc` is implicitly added as a source.\n# Additional source files can be declared with the `SRCS` parameter.\n#\n# Test executable is implicitly linked with `gtest` and `gmock`.\n# Additional libraries can be linked with the `LIBS` parameter.\n#\n# Additional dependencies can be declares with the `DEPS` parameter.\nfunction(add_cpp_test NAME)\n  set(TEST_NAME \"${NAME}_test\")\n  cmake_parse_arguments(ARG \"\" \"\" \"SRCS;LIBS;DEPS\" ${ARGN})\n\n  add_executable(\n    ${TEST_NAME}\n      \"${TEST_NAME}.cc\"\n      ${ARG_SRCS}\n  )\n  add_test(${TEST_NAME} ${TEST_NAME})\n\n  target_link_libraries(\n    ${TEST_NAME}\n      ${ARG_LIBS}\n      gmock_main\n      gtest\n      gmock\n      shared-executable\n  )\nendfunction(add_cpp_test)\n\n# Adds a unit test named `${NAME}_test`, which is part of the `unit_tests` target.\n#\n# The file `${NAME}_test.cc` is implicitly added as a source.\n# Additional source files can be declared with the `SRCS` parameter.\n#\n# Test executable is implicitly linked with `gtest` and `gmock`.\n# Additional libraries can be linked with the `LIBS` parameter.\n#\n# Additional dependencies can be declares with the `DEPS` parameter.\nadd_custom_target(unit_tests)\nfunction(add_unit_test NAME)\n  cmake_parse_arguments(ARG \"\" \"\" \"SRCS;LIBS;DEPS\" ${ARGN})\n  add_cpp_test(${NAME} SRCS ${ARG_SRCS} LIBS ${ARG_LIBS} DEPS ${ARG_DEPS})\n  add_dependencies(unit_tests \"${NAME}_test\")\nendfunction(add_unit_test)\n\n# For tests that use eBPF components and need to run in a container that supports eBPF. Does everything in add_unit_test()\n# and labels test with the eBPFContainer label.  These tests will not be run by default, but only if the RUN_EBPF_TESTS\n# environment variable is set to TRUE.\nfunction(add_ebpf_unit_test NAME)\n  set(TEST_NAME \"${NAME}_test\")\n  cmake_parse_arguments(ARG \"\" \"\" \"SRCS;LIBS;DEPS\" ${ARGN})\n\n  add_executable(\n    ${TEST_NAME}\n      \"${TEST_NAME}.cc\"\n      ${ARG_SRCS}\n  )\n\n  # https://stackoverflow.com/questions/61572034/ctest-disable-a-set-of-labeled-tests-by-default-but-run-them-when-explicitly\n  add_test(NAME ${TEST_NAME} COMMAND sh -c \"if [ \\\"\\${RUN_EBPF_TESTS:-}\\\" ]; then $<TARGET_FILE:${TEST_NAME}>; else exit 127; fi\")\n  set_tests_properties(${TEST_NAME} PROPERTIES SKIP_RETURN_CODE 127)\n\n  target_link_libraries(\n    ${TEST_NAME}\n      ${ARG_LIBS}\n      gmock_main\n      gtest\n      gmock\n      shared-executable\n  )\n\n  add_dependencies(unit_tests \"${NAME}_test\")\n  set_property(TEST ${TEST_NAME} PROPERTY LABELS eBPFContainer)\nendfunction(add_ebpf_unit_test)\n\n# Ensure running `test` builds all unit test binaries first.\n# The `test` target is available when testing is enabled.\nif (TARGET test)\n  add_dependencies(test unit_tests)\nendif ()\n"
  },
  {
    "path": "cmake/tool.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nadd_custom_target(tools)\n\nfunction(add_tool_executable EXEC_NAME)\n  cmake_parse_arguments(P \"\" \"\" \"SRCS;DEPS\" ${ARGN})\n\n  add_executable(\"${EXEC_NAME}\" \"${P_SRCS}\")\n  add_dependencies(tools \"${EXEC_NAME}\")\n\n  install(\n    TARGETS \"${EXEC_NAME}\"\n    RUNTIME\n    DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n    COMPONENT tools\n  )\n\n  if(DEFINED P_DEPS)\n    target_link_libraries(\"${EXEC_NAME}\" \"${P_DEPS}\")\n  endif()\nendfunction(add_tool_executable)\n"
  },
  {
    "path": "cmake/uv.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_path(LIBUV_INCLUDE_DIR uv.h)\nfind_library(LIBUV_LIBS NAMES uv libuv)\nfind_library(LIBUV_STATIC_LIBRARY NAMES libuv.a)\nfind_package_handle_standard_args(LIBUV DEFAULT_MSG\n                                      LIBUV_LIBS\n                                      LIBUV_INCLUDE_DIR\n                                      LIBUV_STATIC_LIBRARY)\nif((NOT LIBUV_INCLUDE_DIR) OR (NOT LIBUV_LIBS) OR (NOT LIBUV_STATIC_LIBRARY))\n  message(FATAL_ERROR \"Could not find libuv. Build container should already have that set up\")\nendif()\n\nmessage(STATUS \"libuv INCLUDE_DIR: ${LIBUV_INCLUDE_DIR}\")\nmessage(STATUS \"libuv LIBS: ${LIBUV_LIBS}\")\nmessage(STATUS \"libuv STATIC_LIBRARY: ${LIBUV_STATIC_LIBRARY}\")\n\nadd_library(libuv-interface INTERFACE)\ntarget_include_directories(\n  libuv-interface\n  INTERFACE\n    \"${LIBUV_INCLUDE_DIR}\"\n)\n\nadd_library(libuv-shared INTERFACE)\ntarget_link_libraries(\n  libuv-shared\n  INTERFACE\n    ${LIBUV_LIBS}\n    libuv-interface\n)\n\nadd_library(libuv-static INTERFACE)\ntarget_link_libraries(\n  libuv-static\n  INTERFACE\n    ${LIBUV_STATIC_LIBRARY}\n)\ntarget_include_directories(\n  libuv-static\n  INTERFACE\n    \"${LIBUV_INCLUDE_DIR}\"\n    libuv-interface\n)\n"
  },
  {
    "path": "cmake/xxd.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfunction (add_xxd FIL GENERATED_NAME)\n  cmake_parse_arguments(MOD \"\" \"OUTPUT\" \"DEPENDS\" ${ARGN})\n\n  get_filename_component(ABS_FIL ${FIL} ABSOLUTE BASENAME ${CMAKE_CURRENT_SOURCE_DIR})\n  get_filename_component(FIL_N ${FIL} NAME)\n  if (MOD_OUTPUT)\n    set(DST_FILENAME \"${CMAKE_BINARY_DIR}/generated/${MOD_OUTPUT}\")\n  else ()\n    set(DST_FILENAME \"${CMAKE_BINARY_DIR}/generated/${FIL_N}.xxd\")\n  endif()\n  get_filename_component(FIL_D ${ABS_FIL} DIRECTORY)\n\n  add_custom_command(\n    OUTPUT \"${DST_FILENAME}\"\n    COMMAND ${CMAKE_COMMAND} -E make_directory \"${CMAKE_BINARY_DIR}/generated\"\n    COMMAND xxd\n    ARGS -i ${FIL_N} > \"${DST_FILENAME}.generated\"\n    COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${DST_FILENAME}.generated\" \"${DST_FILENAME}\"\n    DEPENDS ${FIL} \"${MOD_DEPENDS}\"    \n    WORKING_DIRECTORY ${FIL_D}\n    COMMENT \"Generating XXD from ${FIL} -> ${DST_FILENAME}\"\n    VERBATIM \n  )\n  set_source_files_properties(\"${DST_FILENAME}\" PROPERTIES GENERATED TRUE)\n  set(${GENERATED_NAME} \"${DST_FILENAME}\" PARENT_SCOPE)\nendfunction()     \n"
  },
  {
    "path": "cmake/yamlcpp.cmake",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude_guard()\n\nfind_path(YAMLCPP_INCLUDE_DIR yaml-cpp/yaml.h)\nfind_library(YAMLCPP_LIBRARY NAMES \"libyaml-cpp.a\")\nfind_package_handle_standard_args(YAMLCPP DEFAULT_MSG YAMLCPP_LIBRARY YAMLCPP_INCLUDE_DIR)\nif(NOT YAMLCPP_FOUND)\n  message(FATAL_ERROR \"Could not find yaml-cpp. Build container should already have that set up\")\nendif()\nmessage(STATUS \"yaml-cpp INCLUDE_DIR: ${YAMLCPP_INCLUDE_DIR}\")\nmessage(STATUS \"yamp-cpp LIBRARY: ${YAMLCPP_LIBRARY}\")\nadd_library(yamlcpp INTERFACE)\ntarget_include_directories(yamlcpp INTERFACE \"${YAMLCPP_INCLUDE_DIR}\")\ntarget_link_libraries(yamlcpp INTERFACE \"${YAMLCPP_LIBRARY}\")\n"
  },
  {
    "path": "collector/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_custom_target(collectors)\nadd_custom_target(collectors-docker)\nadd_custom_target(collectors-docker-registry)\n\ninclude(rust_main)\n\nadd_rust_main(\n  TARGET           k8s-collector\n  STRIPPED_TARGET  k8s-collector-stripped\n  PACKAGE          k8s-collector-bin\n  BIN_NAME         k8s-collector\n  LINK_LIBS        versions\n)\n\nadd_dependencies(collectors k8s-collector k8s-collector-stripped)\n\nbuild_custom_docker_image(\n  k8s-collector\n  DOCKERFILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}\n  DOCKERFILE_NAME Dockerfile.k8s-collector\n  OUT_DIR srv\n  ARGS\n    \"EBPF_NET_MAJOR_VERSION=${EBPF_NET_MAJOR_VERSION}\"\n    \"EBPF_NET_MINOR_VERSION=${EBPF_NET_MINOR_VERSION}\"\n    \"EBPF_NET_PATCH_VERSION=${EBPF_NET_PATCH_VERSION}\"\n  OUTPUT_OF\n    k8s-collector-stripped\n  BINARIES\n    debug-info.conf\n  FILES\n    ../NOTICE.txt\n    ../LICENSE.txt\n  DEPENDENCY_OF\n    collectors\n)\n\nadd_subdirectory(cloud)\nadd_subdirectory(kernel)\n"
  },
  {
    "path": "collector/Dockerfile.k8s-collector",
    "content": "FROM docker.io/bitnami/minideb:trixie@sha256:0766a3b76750541ae2c5f3b806e5201e1b2ca1549a0a036a057726dc9e5dfa4d\n\nLABEL org.label-schema.name=\"opentelemetry-network-k8s-collector\"\nLABEL org.label-schema.description=\"OpenTelemetry Network Kubernetes metadata collector\"\nLABEL org.label-schema.vcs-url=\"https://github.com/open-telemetry/opentelemetry-network\"\nLABEL org.label-schema.schema-version=\"1.0\"\n\n# Embed the collector version so the binary can report it in handshakes.\nARG EBPF_NET_MAJOR_VERSION\nARG EBPF_NET_MINOR_VERSION\nARG EBPF_NET_PATCH_VERSION\nENV EBPF_NET_MAJOR_VERSION=${EBPF_NET_MAJOR_VERSION} \\\n    EBPF_NET_MINOR_VERSION=${EBPF_NET_MINOR_VERSION} \\\n    EBPF_NET_PATCH_VERSION=${EBPF_NET_PATCH_VERSION}\n\nCOPY srv /srv\nWORKDIR /srv\n\n# Transitional compatibility: allow overriding the watcher container image\n# by exposing the expected binary name used by the chart.\nRUN if [ ! -e /srv/k8s-collector ]; then \\\n      ln -s /srv/k8s-collector-stripped /srv/k8s-collector; \\\n    fi \\\n && ln -sf /srv/k8s-collector /srv/k8s-watcher\n\nENTRYPOINT [\"/srv/k8s-collector\"]\n"
  },
  {
    "path": "collector/agent_log.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME AgentLogKind\n#define ENUM_TYPE uint32_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(BPF, 0, \"\")                                                                                                                \\\n  X(UDP, 1, \"\")                                                                                                                \\\n  X(DNS, 2, \"\")                                                                                                                \\\n  X(TCP, 3, \"\")                                                                                                                \\\n  X(HTTP, 4, \"\")                                                                                                               \\\n  X(NAT, 6, \"\")                                                                                                                \\\n  X(DOCKER, 7, \"\")                                                                                                             \\\n  X(FLOW, 8, \"\")                                                                                                               \\\n  X(CGROUPS, 9, \"\")                                                                                                            \\\n  X(PERF, 10, \"\")                                                                                                              \\\n  X(PID, 11, \"\")                                                                                                               \\\n  X(PROTOCOL, 12, \"\")                                                                                                          \\\n  X(TRACKED_PROCESS, 13, \"\")                                                                                                   \\\n  X(NOMAD, 14, \"\")                                                                                                             \\\n  X(SOCKET, 15, \"\")\n#define ENUM_DEFAULT BPF\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "collector/cloud/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_library(\n  cloud_agentlib\n  STATIC\n    entrypoint.cc\n    collector.cc\n    enumerator.cc\n    ingest_connection.cc\n)\ntarget_include_directories(\n  cloud_agentlib\n  PRIVATE\n    ${PROJECT_SOURCE_DIR}\n    ${CMAKE_CURRENT_BINARY_DIR}\n)\ntarget_link_libraries(\n  cloud_agentlib\n  PUBLIC\n    signal_handler \n    render_ebpf_net_cloud_collector\n)\n\nset(CLOUD_COLLECTOR_LINK_LIBRARIES\n    cloud_agentlib\n    render_ebpf_net_cloud_collector\n    render_ebpf_net_ingest_writer\n    aws-sdk-cpp\n    reconnecting_channel\n    connection_caretaker\n    resource_usage_reporter\n    config_file\n    ip_address\n    scheduling\n    libuv-static\n    args_parser\n    system_ops\n    aws_instance_metadata\n    spdlog\n    static-executable\n    protobuf::libprotobuf\n    ZLIB::ZLIB\n    versions\n)\n\n\nlint_shell_script_bundle(\n  cloud-collector-scripts\n  SOURCES\n    entrypoint.sh\n)\n\ninclude(rust_main)\nadd_rust_main(\n  TARGET           cloud-collector\n  STRIPPED_TARGET  cloud-collector-stripped\n  PACKAGE          cloud-collector-bin\n  BIN_NAME         cloud-collector\n  LINK_LIBS        ${CLOUD_COLLECTOR_LINK_LIBRARIES}\n)\n\nadd_dependencies(collectors cloud-collector cloud-collector-stripped)\n\nif(\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n  build_custom_docker_image(\n    cloud-collector\n    OUT_DIR srv\n    OUTPUT_OF\n      cloud-collector-scripts\n      cloud-collector-stripped\n    BINARIES\n      debug-info.conf\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n    DEPENDENCY_OF\n      collectors\n  )\nelse()\n  build_custom_docker_image(\n    cloud-collector\n    OUT_DIR srv\n    OUTPUT_OF\n      cloud-collector-scripts\n      cloud-collector-stripped\n    BINARIES\n      debug-info.conf\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n    DEPENDENCY_OF\n      collectors\n  )\nendif()\n"
  },
  {
    "path": "collector/cloud/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nFROM docker.io/bitnami/minideb:trixie@sha256:0766a3b76750541ae2c5f3b806e5201e1b2ca1549a0a036a057726dc9e5dfa4d\n\nLABEL org.label-schema.name=\"opentelemetry-ebpf-cloud-collector\"\nLABEL org.label-schema.description=\"OpenTelemetry eBPF cloud metadata collector\"\nLABEL org.label-schema.vcs-url=\"https://github.com/open-telemetry/opentelemetry-ebpf\"\nLABEL org.label-schema.schema-version=\"1.0\"\n\n# ca-certificates are required by libcurl\nRUN install_packages ca-certificates libprotobuf32 libgrpc++1.51 libcurl4 libcurlpp0\nENV SSL_CERT_DIR=/etc/ssl/certs\n\nENTRYPOINT [ \"/srv/entrypoint.sh\" ]\n\nCOPY srv /srv\nWORKDIR /srv\nRUN if [ ! -e /srv/cloud-collector ]; then \\\n      ln /srv/cloud-collector-stripped /srv/cloud-collector; \\\n    fi\n"
  },
  {
    "path": "collector/cloud/README.md",
    "content": "# Cloud Collector\n\nThe `cloud-collector` collects cloud information that cannot be otherwise collected by our kernel\ncollector, and feeds that information to the reducer.\n\nThe collector is comprised of two components:\n- the agent that periodically queries AWS and pushes the information to the pipeline server;\n- a span within the pipeline server that consumes messages sent by `cloud-collector` and enriches\n  the flow.\n\n# Running:\nThe `cloud-collector` requires two pieces of settings to run: auth config and AWS credentials.\n\nThe TL;DR to build and run in a test environment within `benv` is this:\n```bash\ncd ~/out/\nmake -j cloud-collector\nexport AWS_ACCESS_KEY_ID=\"your_access_key\"\nexport AWS_SECRET_ACCESS_KEY=\"your_secret_access_key\"\n# make sure the server is running, as well as stunnel\nsrc/collector/cloud/cloud-collector --auth-config=$HOME/src/misc/localhost_auth_config.yaml\n```\n\n## AWS Credentials\nCheck the AWS SDK Developer Guide for [recommended ways to provide credentials](https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html).\n\nFor testing, you can supply two environment variables:\n```bash\nexport AWS_ACCESS_KEY_ID=your_access_key_id\nexport AWS_SECRET_ACCESS_KEY=your_secret_access_key\n```\n"
  },
  {
    "path": "collector/cloud/collector.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/cloud/collector.h>\n\n#include <scheduling/interval_scheduler.h>\n\n#include <util/jitter.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <functional>\n#include <stdexcept>\n#include <thread>\n\nnamespace collector::cloud {\n\nnamespace {\n\nconstexpr auto RECONNECT_DELAY = 5s;\n// explicitly using milliseconds for a finer grained jitter\nconstexpr std::chrono::milliseconds RECONNECT_JITTER = 1s;\n\n} // namespace\n\nCloudCollector::CloudCollector(\n    ::uv_loop_t &loop,\n    std::string_view hostname,\n    std::chrono::milliseconds aws_metadata_timeout,\n    std::chrono::milliseconds heartbeat_interval,\n    std::size_t buffer_size,\n    config::IntakeConfig intake_config,\n    std::chrono::milliseconds poll_interval)\n    : loop_(loop),\n      connection_(\n          hostname,\n          loop_,\n          aws_metadata_timeout,\n          heartbeat_interval,\n          std::move(intake_config),\n          buffer_size,\n          *this,\n          std::bind(&CloudCollector::on_connected, this)),\n      log_(connection_.writer()),\n      enumerator_(log_, connection_.index(), connection_.writer()),\n      scheduler_(loop_, std::bind(&CloudCollector::callback, this)),\n      poll_interval_(poll_interval)\n{}\n\nCloudCollector::~CloudCollector()\n{\n  ::uv_loop_close(&loop_);\n}\n\nvoid CloudCollector::run_loop()\n{\n  connection_.connect();\n\n  while (::uv_run(&loop_, UV_RUN_DEFAULT))\n    ;\n\n  scheduler_.stop();\n}\n\nscheduling::JobFollowUp CloudCollector::callback()\n{\n  auto result = enumerator_.enumerate();\n  connection_.flush();\n  return result;\n}\n\nvoid CloudCollector::on_error(int err)\n{\n  scheduler_.stop();\n\n  enumerator_.free_handles();\n\n  std::this_thread::sleep_for(add_jitter(RECONNECT_DELAY, -RECONNECT_JITTER, RECONNECT_JITTER));\n}\n\nvoid CloudCollector::on_connected()\n{\n  scheduler_.start(poll_interval_, poll_interval_);\n}\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/cloud/collector.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <collector/cloud/enumerator.h>\n#include <collector/cloud/ingest_connection.h>\n\n#include <channel/callbacks.h>\n#include <scheduling/interval_scheduler.h>\n#include <scheduling/job.h>\n#include <util/curl_engine.h>\n\n#include <chrono>\n#include <string>\n\nnamespace collector::cloud {\n\nstruct CloudCollector : channel::Callbacks {\n  CloudCollector(\n      ::uv_loop_t &loop,\n      std::string_view hostname,\n      std::chrono::milliseconds aws_metadata_timeout,\n      std::chrono::milliseconds heartbeat_interval,\n      std::size_t buffer_size,\n      config::IntakeConfig intake_config,\n      std::chrono::milliseconds poll_interval);\n\n  ~CloudCollector();\n\n  void run_loop();\n\n  ::uv_loop_t &get_loop() { return loop_; }\n\nprivate:\n  scheduling::JobFollowUp callback();\n\n  void on_error(int err);\n  void on_connected();\n\n  ::uv_loop_t &loop_;\n  IngestConnection connection_;\n  logging::Logger log_;\n  NetworkInterfacesEnumerator enumerator_;\n  scheduling::IntervalScheduler scheduler_;\n  std::chrono::milliseconds const poll_interval_;\n};\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/cloud/entrypoint.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/cloud/collector.h>\n\n#include <channel/component.h>\n#include <collector/constants.h>\n#include <common/cloud_platform.h>\n#include <config/config_file.h>\n#include <config/intake_config.h>\n#include <util/agent_id.h>\n#include <util/args_parser.h>\n#include <util/log.h>\n#include <util/log_whitelist.h>\n#include <util/signal_handler.h>\n#include <util/system_ops.h>\n#include <util/utility.h>\n\n#include <aws/core/Aws.h>\n\n#include <chrono>\n#include <string>\n\n#include <csignal>\n\n/**\n * Cloud Collector Agent\n *\n * Requires AWS Access Key ID and Secret Access Key to be set up in the\n * environment:\n * https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html\n *\n * In production, in an EC2 instance, this should work automagically.\n *\n * The easiest way to achieve that in a development environment is by setting up\n * environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.\n */\n\nstatic int cloud_collector_run(int argc, char **argv)\n{\n  ::uv_loop_t loop;\n  if (auto const error = ::uv_loop_init(&loop)) {\n    throw std::runtime_error(::uv_strerror(error));\n  }\n\n  // args parsing\n\n  cli::ArgsParser parser(\"Cloud Collector Agent\");\n\n  args::HelpFlag help(*parser, \"help\", \"Display this help menu\", {'h', \"help\"});\n\n  args::ValueFlag<std::string> conf_file(*parser, \"config_file\", \"The location of the custom config file\", {\"config-file\"}, \"\");\n\n  args::ValueFlag<std::chrono::milliseconds::rep> ec2_poll_interval_ms(\n      *parser,\n      \"ec2_poll_interval_ms\",\n      \"How often, in milliseconds, to enumerate interfaces in EC2.\",\n      {\"ec2-poll-interval-ms\"},\n      std::chrono::milliseconds(1s).count());\n\n  args::ValueFlag<u64> aws_metadata_timeout_ms(\n      *parser, \"milliseconds\", \"Milliseconds to wait for AWS instance metadata\", {\"aws-timeout\"}, 1 * 1000);\n\n  parser.new_handler<LogWhitelistHandler<channel::Component>>(\"channel\");\n  parser.new_handler<LogWhitelistHandler<CloudPlatform>>(\"cloud-platform\");\n  parser.new_handler<LogWhitelistHandler<Utility>>(\"utility\");\n\n  auto &intake_config_handler = parser.new_handler<config::IntakeConfig::ArgsHandler>();\n\n  SignalManager signal_manager(loop, \"cloud-collector\");\n\n  if (auto result = parser.process(argc, argv); !result.has_value()) {\n    return result.error();\n  }\n\n  // Initialize minimal signal handler behavior (ignore SIGPIPE, disable core dumps)\n  signal_manager.handle();\n\n  if (ec2_poll_interval_ms.Get() == 0) {\n    LOG::error(\"--ec2-poll-interval-ms cannot be 0\");\n    return EXIT_FAILURE;\n  }\n\n  // resolve hostname\n  std::string const hostname = get_host_name(MAX_HOSTNAME_LENGTH).recover([](auto &error) {\n    LOG::error(\"Unable to retrieve host information from uname: {}\", log_waive(error.message()));\n    return \"(unknown)\";\n  });\n\n  auto curl_engine = CurlEngine::create(&loop);\n\n  auto agent_id = gen_agent_id();\n\n  config::ConfigFile configuration_data(config::ConfigFile::YamlFormat(), conf_file.Get());\n  config::IntakeConfig intake_config = configuration_data.intake_config();\n  intake_config_handler.read_config(intake_config);\n\n  LOG::info(\"Cloud Collector version {} ({}) started on host {}\", versions::release, release_mode_string, hostname);\n  LOG::info(\"Cloud Collector agent ID is {}\", agent_id);\n\n  // aws sdk init\n\n  Aws::InitAPI({});\n\n  // main\n\n  collector::cloud::CloudCollector collector{\n      loop,\n      hostname,\n      std::chrono::milliseconds(aws_metadata_timeout_ms.Get()),\n      HEARTBEAT_INTERVAL,\n      WRITE_BUFFER_SIZE,\n      std::move(intake_config),\n      std::chrono::milliseconds(ec2_poll_interval_ms.Get())};\n\n  signal_manager.handle_signals({SIGINT, SIGTERM} // TODO: close gracefully\n  );\n\n  collector.run_loop();\n\n  // shutdown\n  Aws::ShutdownAPI({});\n\n  return EXIT_SUCCESS;\n}\n\nextern \"C\" int otn_cloud_collector_main(int argc, const char **argv)\n{\n  return cloud_collector_run(argc, const_cast<char **>(argv));\n}\n"
  },
  {
    "path": "collector/cloud/entrypoint.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\n# shellcheck disable=SC1091\n[[ ! -e ./debug-info.conf ]] || source ./debug-info.conf\n\n# to run the collector under gdb, set `EBPF_NET_RUN_UNDER_GDB` to the flavor of gdb\n# you want (e.g.: `cgdb` or `gdb`) - this is intended for development purposes\nif [[ -n \"${EBPF_NET_RUN_UNDER_GDB}\" ]]; then\n  apt-get update -y\n  apt-get install -y --no-install-recommends \"${EBPF_NET_RUN_UNDER_GDB}\"\n\n  if [[ \"${#EBPF_NET_GDB_COMMANDS[@]}\" -lt 1 ]]; then\n    # default behavior is to run the agent, print a stack trace after it exits\n    # and exit gdb without confirmation\n    EBPF_NET_GDB_COMMANDS=( \\\n      'set pagination off'\n      'handle SIGPIPE nostop pass'\n      'handle SIGUSR1 nostop pass'\n      'handle SIGUSR2 nostop pass'\n      run\n      bt\n      'server q'\n    )\n  fi\n\n  GDB_ARGS=()\n  for gdb_cmd in \"${EBPF_NET_GDB_COMMANDS[@]}\"; do\n    GDB_ARGS+=(-ex \"${gdb_cmd}\")\n  done\n\n  (set -x; exec \"${EBPF_NET_RUN_UNDER_GDB}\" -q \"${GDB_ARGS[@]}\" \\\n    --args /srv/cloud-collector \"$@\" \\\n  )\nelse\n  (set -x; exec /srv/cloud-collector \"$@\")\nfi\n"
  },
  {
    "path": "collector/cloud/enumerator.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/cloud/enumerator.h>\n\n#include <aws/ec2/model/DescribeNetworkInterfacesRequest.h>\n#include <aws/ec2/model/DescribeNetworkInterfacesResponse.h>\n#include <aws/ec2/model/DescribeRegionsRequest.h>\n#include <aws/ec2/model/DescribeRegionsResponse.h>\n\n#include <generated/ebpf_net/cloud_collector/index.h>\n#include <generated/ebpf_net/ingest/wire_message.h>\n\n#include <util/ip_address.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/resource_usage_reporter.h>\n#include <util/stop_watch.h>\n\n#include <array>\n#include <type_traits>\n#include <utility>\n\n#include <cstdint>\n\nnamespace collector::cloud {\n\nNetworkInterfacesEnumerator::NetworkInterfacesEnumerator(\n    logging::Logger &log, ebpf_net::cloud_collector::Index &index, ebpf_net::ingest::Writer &writer)\n    : index_(index), writer_(writer), log_(log)\n{}\n\nNetworkInterfacesEnumerator::~NetworkInterfacesEnumerator()\n{\n  free_handles();\n}\n\nvoid NetworkInterfacesEnumerator::set_handles(std::vector<ebpf_net::cloud_collector::handles::aws_network_interface> handles)\n{\n  free_handles();\n  handles_ = std::move(handles);\n}\n\nvoid NetworkInterfacesEnumerator::free_handles()\n{\n  for (auto &handle : handles_) {\n    handle.put(index_);\n  }\n\n  handles_.clear();\n}\n\nvoid translate_interfaces_to_spans(\n    ebpf_net::cloud_collector::Index &index,\n    Aws::Vector<Aws::EC2::Model::NetworkInterface> const &interfaces,\n    std::vector<ebpf_net::cloud_collector::handles::aws_network_interface> &handles)\n{\n  for (auto const &interface : interfaces) {\n    auto const &attachment = interface.GetAttachment();\n    auto const &association = interface.GetAssociation();\n    auto const &ip_owner_id = association.GetIpOwnerId();\n    auto const &vpc_id = interface.GetVpcId();\n    auto const &az = interface.GetAvailabilityZone();\n\n    auto const &interface_id = interface.GetNetworkInterfaceId();\n    auto const raw_interface_type = static_cast<std::uint16_t>(interface.GetInterfaceType());\n    auto const &instance_id = attachment.GetInstanceId();\n    auto const &instance_owner_id = attachment.GetInstanceOwnerId();\n    auto const &public_dns_name = association.GetPublicDnsName();\n    auto const &private_dns_name = interface.GetPrivateDnsName();\n    auto const &description = interface.GetDescription();\n\n    auto const add_entry = [&](IPv6Address const &ipv6) {\n      auto handle = index.aws_network_interface.by_key(ebpf_net::cloud_collector::keys::aws_network_interface{ipv6.as_int()});\n\n      LOG::trace(\n          \"network_interface_info:\"\n          \" ip={}\"\n          \" ip_owner_id={}\"\n          \" vpc_id={}\"\n          \" az={}\"\n          \" interface_id={} interface_type={} instance_id={} instance_owner_id={}\"\n          \" public_dns_name={} private_dns_name={} description={}\",\n          ipv6,\n          ip_owner_id,\n          vpc_id,\n          az,\n          interface_id,\n          raw_interface_type,\n          instance_id,\n          instance_owner_id,\n          public_dns_name,\n          private_dns_name,\n          description);\n\n      handle.network_interface_info(\n          jb_blob{ip_owner_id},\n          jb_blob{vpc_id},\n          jb_blob{az},\n          jb_blob{interface_id},\n          raw_interface_type,\n          jb_blob{instance_id},\n          jb_blob{instance_owner_id},\n          jb_blob{public_dns_name},\n          jb_blob{private_dns_name},\n          jb_blob{description});\n\n      handles.emplace_back(handle.to_handle());\n    };\n\n    if (auto const public_ip = IPv4Address::parse(association.GetPublicIp().c_str())) {\n      add_entry(public_ip->to_ipv6());\n    }\n\n    for (auto const &ipv4 : interface.GetPrivateIpAddresses()) {\n      auto const private_ip = IPv4Address::parse(ipv4.GetPrivateIpAddress().c_str());\n\n      if (private_ip) {\n        add_entry(private_ip->to_ipv6());\n      }\n    }\n\n    for (auto const &address : interface.GetIpv6Addresses()) {\n      if (auto const ipv6 = IPv6Address::parse(address.GetIpv6Address().c_str())) {\n        add_entry(*ipv6);\n      }\n    }\n  }\n}\n\nvoid NetworkInterfacesEnumerator::handle_ec2_error(\n    CollectorStatus status, Aws::Client::AWSError<Aws::EC2::EC2Errors> const &error)\n{\n  if (error.GetErrorType() == Aws::EC2::EC2Errors::THROTTLING) {\n    log_.error(\"{} - AWS API call throttled: {}\", to_string(status), error.GetMessage());\n    return;\n  }\n\n  auto const http_status = static_cast<std::underlying_type_t<Aws::Http::HttpResponseCode>>(error.GetResponseCode());\n\n  LOG::trace(\"reporting cloud collector as unhealthy (status={} detail={})\", to_string(status), http_status);\n  writer_.collector_health(integer_value(status), http_status);\n\n  if (http_status >= 400 && http_status < 500) {\n    log_.error(\n        \"{} -  API call failed with http status {}. Double check that AWS credentials are\"\n        \" properly set up for this pod. Check setup instructions for more information.\"\n        \" Error message from AWS: {}\",\n        to_string(status),\n        http_status,\n        error.GetMessage());\n  } else {\n    log_.error(\n        \"{} -  API call failed with http status {}. Error message from AWS: {}\",\n        to_string(status),\n        http_status,\n        error.GetMessage());\n  }\n}\n\nscheduling::JobFollowUp NetworkInterfacesEnumerator::enumerate()\n{\n  ResourceUsageReporter::report(writer_);\n\n  auto const regions_response = ec2_.DescribeRegions({});\n  if (!regions_response.IsSuccess()) {\n    handle_ec2_error(CollectorStatus::aws_describe_regions_error, regions_response.GetError());\n    return scheduling::JobFollowUp::backoff;\n  }\n\n  std::vector<ebpf_net::cloud_collector::handles::aws_network_interface> handles;\n  auto result = scheduling::JobFollowUp::ok;\n\n  LOG::trace(\"starting AWS network interfaces enumeration\");\n  StopWatch<> watch;\n  for (auto const &region : regions_response.GetResult().GetRegions()) {\n    LOG::trace(\"enumerating network interfaces in region '{}'\", region.GetRegionName());\n\n    Aws::Client::ClientConfiguration client_config;\n    client_config.region = region.GetRegionName();\n\n    Aws::EC2::EC2Client client(client_config);\n\n    auto const interfaces_response = client.DescribeNetworkInterfaces({});\n\n    if (!interfaces_response.IsSuccess()) {\n      handle_ec2_error(CollectorStatus::aws_describe_network_interfaces_error, interfaces_response.GetError());\n      result = scheduling::JobFollowUp::backoff;\n      continue;\n    }\n\n    auto const &interfaces = interfaces_response.GetResult().GetNetworkInterfaces();\n\n    LOG::trace(\"found {} network interfaces in region '{}'\", interfaces.size(), region.GetRegionName());\n\n    translate_interfaces_to_spans(index_, interfaces, handles);\n  }\n  LOG::trace(\"finished AWS network interfaces enumeration after {}\", watch.elapsed<std::chrono::milliseconds>());\n\n  set_handles(std::move(handles));\n\n  LOG::trace(\"network interface live span count: {}\", handles_.size());\n\n  if (result == scheduling::JobFollowUp::ok) {\n    LOG::trace(\"reporting cloud collector as healthy\");\n    writer_.collector_health(integer_value(CollectorStatus::healthy), 0);\n  }\n\n  return result;\n}\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/cloud/enumerator.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/collector_status.h>\n#include <generated/ebpf_net/cloud_collector/handles.h>\n#include <scheduling/job.h>\n#include <util/logger.h>\n\n#include <aws/ec2/EC2Client.h>\n\n#include <functional>\n#include <vector>\n\nnamespace collector::cloud {\n\nstruct NetworkInterfacesEnumerator {\n\n  NetworkInterfacesEnumerator(logging::Logger &log, ebpf_net::cloud_collector::Index &index, ebpf_net::ingest::Writer &writer);\n  ~NetworkInterfacesEnumerator();\n\n  scheduling::JobFollowUp enumerate();\n\n  void free_handles();\n\nprivate:\n  void set_handles(std::vector<ebpf_net::cloud_collector::handles::aws_network_interface> handles);\n\n  void handle_ec2_error(CollectorStatus status, Aws::Client::AWSError<Aws::EC2::EC2Errors> const &error);\n\n  Aws::EC2::EC2Client ec2_;\n  ebpf_net::cloud_collector::Index &index_;\n  ebpf_net::ingest::Writer &writer_;\n  logging::Logger &log_;\n  std::vector<ebpf_net::cloud_collector::handles::aws_network_interface> handles_;\n};\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/cloud/ingest_connection.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/cloud/ingest_connection.h>\n\n#include <util/boot_time.h>\n\nnamespace collector::cloud {\n\nIngestConnection::IngestConnection(\n    std::string_view hostname,\n    ::uv_loop_t &loop,\n    std::chrono::milliseconds aws_metadata_timeout,\n    std::chrono::milliseconds heartbeat_interval,\n    config::IntakeConfig intake_config,\n    std::size_t buffer_size,\n    channel::Callbacks &connection_callback,\n    std::function<void()> on_connected_cb)\n    : curl_(CurlEngine::create(&loop)),\n      channel_(std::move(intake_config), loop, buffer_size),\n      connection_callback_(connection_callback),\n      encoder_(channel_.intake_config().make_encoder()),\n      writer_(channel_.buffered_writer(), monotonic, get_boot_time(), encoder_.get()),\n      caretaker_(\n          hostname,\n          ClientType::cloud,\n          {},\n          &loop,\n          writer_,\n          aws_metadata_timeout,\n          heartbeat_interval,\n          std::bind(&channel::ReconnectingChannel::flush, &channel_),\n          std::bind(&channel::ReconnectingChannel::set_compression, &channel_, std::placeholders::_1),\n          std::move(on_connected_cb)),\n      index_({writer_})\n{\n  channel_.register_pipeline_observer(this);\n}\n\nvoid IngestConnection::connect()\n{\n  channel_.start_connect();\n}\n\nvoid IngestConnection::flush()\n{\n  channel_.flush();\n}\n\nu32 IngestConnection::received_data(const u8 *data, int data_len)\n{\n  return connection_callback_.received_data(data, data_len);\n}\n\nvoid IngestConnection::on_error(int err)\n{\n  caretaker_.set_disconnected();\n\n  connection_callback_.on_error(err);\n}\n\nvoid IngestConnection::on_closed()\n{\n  connection_callback_.on_closed();\n}\n\nvoid IngestConnection::on_connect()\n{\n  caretaker_.set_connected();\n\n  connection_callback_.on_connect();\n}\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/cloud/ingest_connection.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/callbacks.h>\n#include <channel/connection_caretaker.h>\n#include <channel/reconnecting_channel.h>\n#include <generated/ebpf_net/cloud_collector/index.h>\n#include <generated/ebpf_net/ingest/writer.h>\n\n#include <uv.h>\n\n#include <chrono>\n#include <functional>\n\nnamespace collector::cloud {\n\nclass IngestConnection : channel::Callbacks {\npublic:\n  IngestConnection(\n      std::string_view hostname,\n      ::uv_loop_t &loop,\n      std::chrono::milliseconds aws_metadata_timeout,\n      std::chrono::milliseconds heartbeat_interval,\n      config::IntakeConfig intake_config,\n      std::size_t buffer_size,\n      channel::Callbacks &connection_callback,\n      std::function<void()> on_connected_cb);\n\n  void connect();\n  void flush();\n\n  ebpf_net::ingest::Writer &writer() { return writer_; }\n\n  ebpf_net::cloud_collector::Index &index() { return index_; }\n\nprivate:\n  u32 received_data(const u8 *data, int data_len);\n  void on_error(int err);\n  void on_closed();\n  void on_connect();\n\n  std::unique_ptr<CurlEngine> curl_;\n  channel::ReconnectingChannel channel_;\n  channel::Callbacks &connection_callback_;\n  std::unique_ptr<::ebpf_net::ingest::Encoder> encoder_;\n  ebpf_net::ingest::Writer writer_;\n  channel::ConnectionCaretaker caretaker_;\n  ebpf_net::cloud_collector::Index index_;\n};\n\n} // namespace collector::cloud\n"
  },
  {
    "path": "collector/constants.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/constants.h>\n#include <platform/types.h>\n\nconstexpr auto HEARTBEAT_INTERVAL = 2s;\nconstexpr auto WRITE_BUFFER_SIZE = 16 * 1024;\n"
  },
  {
    "path": "collector/kernel/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\n# Kernel collector legacy executable\n#\nadd_executable(\n  kernel-collector-legacy\n    main.cc\n)\nset(KERNEL_COLLECTOR_LINK_LIBRARIES\n    agentlib\n    render_ebpf_net_ingest_writer\n    render_rust_ebpf_net\n    fastpass_util\n    file_ops\n    config_file\n    libuv-static\n    args_parser\n    system_ops\n    spdlog\n)\ntarget_link_libraries(\n  kernel-collector-legacy\n  PUBLIC\n    ${KERNEL_COLLECTOR_LINK_LIBRARIES}\n    static-executable\n)\ntarget_include_directories(\n    kernel-collector-legacy\n\n  PRIVATE\n    ${PROJECT_SOURCE_DIR}\n    ${CMAKE_BINARY_DIR}\n)\n\n# DNS library\n#\nadd_library(\n  agentdnslib\n  STATIC\n    dns/ares_expand_name.c\n    dns/ares_parse_a_aaaa_reply.c\n    dns/ares_parse_query.c\n)\nadd_dependencies(agentdnslib render_ebpf_net_artifacts)\ntarget_compile_options(agentdnslib PRIVATE -fPIC)\n\n# Agent library\n#\nadd_library(\n  agentlib\n  STATIC\n    perf_reader.cc\n    perf_poller.cc\n    buffered_poller.cc\n    dns_requests.cc\n    proc_reader.cc\n    process_prober.cc\n    process_handler.cc\n    socket_prober.cc\n    fd_reader.cc\n    proc_net_reader.cc\n    proc_cmdline.cc\n    probe_handler.cc\n    kernel_collector.cc\n    kernel_collector_restarter.cc\n    bpf_handler.cc\n    cgroup_prober.cc\n    cgroup_handler.cc\n    nat_prober.cc\n    nat_handler.cc\n    troubleshooting.cc\n    tcp_data_handler.cc\n    kernel_symbols.cc\n    protocols/protocol_handler_base.cc\n    protocols/protocol_handler_unknown.cc\n    protocols/protocol_handler_http.cc\n    entrypoint.cc\n)\ntarget_link_libraries(\n  agentlib\n  PUBLIC\n    render_ebpf_net_agent_internal_hash\n    render_ebpf_net_ingest_writer\n    render_ebpf_net_kernel_collector\n    agentdnslib\n    yamlcpp\n    curl_engine\n    agent_id\n    resource_usage_reporter\n    scheduling\n    libuv-interface\n    element_queue_writer\n    file_channel\n    upstream_connection\n    aws_instance_metadata\n    gcp_instance_metadata\n    docker_host_config_metadata\n    k8s_metadata\n    nomad_metadata\n    ip_address\n    intake_config\n    proc_ops\n    system_ops\n    time\n    absl::flat_hash_map\n    absl::flat_hash_set\n    stdc++fs\n    libbpf::libbpf\n    versions\n    signal_handler\n)\ntarget_compile_options(agentlib PRIVATE -fPIC)\n# some agentlib sources include ${BPF_DEBUG_INFO}\nadd_dependencies(agentlib generate_bpf_skeleton)\n\n################################################################################\n# Rust binary integration (generalized)\n#\ninclude(rust_main)\nadd_rust_main(\n  TARGET           kernel-collector\n  STRIPPED_TARGET  kernel-collector-stripped\n  PACKAGE          kernel-collector-bin\n  BIN_NAME         kernel-collector\n  LINK_LIBS        ${KERNEL_COLLECTOR_LINK_LIBRARIES}\n)\n\nadd_dependencies(collectors kernel-collector kernel-collector-stripped)\n\n################################################################################\n# libbpf skeleton\n#\n\nset(BPF_DEPENDENCIES\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/render_bpf.c\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/render_bpf.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_debug.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_http_protocol.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_inet_csk_accept.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_memory.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_tcp_events.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_tcp_processor.c\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_tcp_send_recv.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_tcp_socket.h\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/tcp-processor/bpf_types.h\"\n  \"${CMAKE_BINARY_DIR}/generated/ebpf_net/agent_internal/wire_message.h\"\n  \"${CMAKE_BINARY_DIR}/generated/ebpf_net/agent_internal/bpf.h\"\n  \"${PROJECT_SOURCE_DIR}/jitbuf/jb.h\"\n  render_compile_ebpf_net\n)\n\nset(BPF_SKEL_HEADER \"${CMAKE_BINARY_DIR}/generated/render_bpf.skel.h\")\nset(BPF_OBJECT_FILE \"${CMAKE_BINARY_DIR}/generated/render_bpf.bpf.o\")\nset(BPFTOOL \"${CMAKE_INSTALL_PREFIX}/usr/local/sbin/bpftool\")\nset(CLANG clang)\n\n# Get Clang's system includes for BPF compilation\nexecute_process(\n  COMMAND ${CLANG} -v -E - \n  INPUT_FILE /dev/null\n  ERROR_VARIABLE CLANG_VERBOSE_OUTPUT\n  OUTPUT_QUIET\n)\nstring(REGEX MATCH \"#include <\\\\.\\\\.\\\\.> search starts here:(.*)End of search list\\\\.\" _ \"${CLANG_VERBOSE_OUTPUT}\")\nstring(REGEX REPLACE \"\\n\" \";\" CLANG_INCLUDES \"${CMAKE_MATCH_1}\")\nset(CLANG_BPF_SYS_INCLUDES \"\")\nforeach(INCLUDE_PATH ${CLANG_INCLUDES})\n  string(STRIP \"${INCLUDE_PATH}\" INCLUDE_PATH)\n  if(NOT \"${INCLUDE_PATH}\" STREQUAL \"\")\n    list(APPEND CLANG_BPF_SYS_INCLUDES \"-idirafter\" \"${INCLUDE_PATH}\")\n  endif()\nendforeach()\n\n# Determine architecture for vmlinux.h\nset(ARCH_RAW \"${CMAKE_HOST_SYSTEM_PROCESSOR}\")\nstring(REGEX REPLACE \"x86_64\" \"x86\" ARCH \"${ARCH_RAW}\")\nstring(REGEX REPLACE \"arm.*\" \"arm\" ARCH \"${ARCH}\")\nstring(REGEX REPLACE \"aarch64\" \"arm64\" ARCH \"${ARCH}\")\nstring(REGEX REPLACE \"ppc64le\" \"powerpc\" ARCH \"${ARCH}\")\nstring(REGEX REPLACE \"mips.*\" \"mips\" ARCH \"${ARCH}\")\nstring(REGEX REPLACE \"riscv64\" \"riscv\" ARCH \"${ARCH}\")\nstring(REGEX REPLACE \"loongarch64\" \"loongarch\" ARCH \"${ARCH}\")\n\nset(VMLINUX_H \"${PROJECT_SOURCE_DIR}/ext/vmlinux.h/include/${ARCH}/vmlinux.h\")\nget_filename_component(VMLINUX_DIR \"${VMLINUX_H}\" DIRECTORY)\n\n\n# Compile BPF source to object file for libbpf skeleton\nadd_custom_command(\n  OUTPUT\n    \"${BPF_OBJECT_FILE}\"\n  COMMAND\n    \"${CLANG}\"\n    -g -O2 -target bpf\n    -D__TARGET_ARCH_${ARCH}\n    -D_PROCESSING_BPF=1\n    -I \"${CMAKE_BINARY_DIR}/generated\"\n    -I \"${CMAKE_BINARY_DIR}\"\n    -I \"${CMAKE_INSTALL_PREFIX}/usr/lib64\"\n    -I \"${CMAKE_INSTALL_PREFIX}/usr/include\"\n    -I \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src\"\n    -I \"${PROJECT_SOURCE_DIR}\"\n    -I \"${VMLINUX_DIR}\"\n    ${CLANG_BPF_SYS_INCLUDES}\n    -c \"${CMAKE_CURRENT_SOURCE_DIR}/bpf_src/render_bpf.c\"\n    -o \"${BPF_OBJECT_FILE}.tmp\"\n  COMMAND\n    \"${BPFTOOL}\" gen object \"${BPF_OBJECT_FILE}\" \"${BPF_OBJECT_FILE}.tmp\"\n  DEPENDS \"${BPF_DEPENDENCIES}\" \"${VMLINUX_H}\"\n  COMMENT \"Compiling BPF source to object file for skeleton generation\"\n  VERBATIM\n)\n\n# Generate BPF skeleton header\nadd_custom_command(\n  OUTPUT\n    \"${BPF_SKEL_HEADER}\"\n  COMMAND\n    \"${BPFTOOL}\" gen skeleton \"${BPF_OBJECT_FILE}\" > \"${BPF_SKEL_HEADER}\"\n  DEPENDS\n    \"${BPF_OBJECT_FILE}\"\n  COMMENT \"Generating BPF skeleton header\"\n  VERBATIM\n)\n\nset_source_files_properties(\"${BPF_SKEL_HEADER}\" PROPERTIES GENERATED TRUE)\n\n# Target for libbpf skeleton generation\nadd_custom_target(\n  generate_bpf_skeleton\n  DEPENDS\n    \"${BPF_SKEL_HEADER}\"\n)\n\n################################################################################\n\n# Shell scripts\n#\nlint_shell_script_bundle(\n  kernel-collector-scripts\n  SOURCES\n    entrypoint.sh\n    entrypoint-kct.sh\n)\n\n# Docker image\n#\nif(\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n  build_custom_docker_image(\n    kernel-collector\n    OUT_DIR srv\n    OUTPUT_OF\n      kernel-collector-scripts\n      kernel-collector-stripped\n    BINARIES\n      debug-info.conf\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n    DEPENDENCY_OF\n      collectors\n    ARGS\n      BUILD_TYPE=\"Debug\"\n  )\n\n  build_custom_docker_image(\n    kernel-collector-test\n    DOCKERFILE_PATH\n      \"${CMAKE_CURRENT_SOURCE_DIR}/kernel_collector_test_docker\"\n    OUT_DIR srv\n    ARTIFACTS_OF\n      bpf_wire_to_json\n      intake_wire_to_json\n      kernel_collector_test\n    OUTPUT_OF\n      kernel-collector-scripts\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n    ARGS\n      BUILD_TYPE=\"Debug\"\n  )\nelse()\n  build_custom_docker_image(\n    kernel-collector\n    OUT_DIR srv\n    OUTPUT_OF\n      kernel-collector-scripts\n      kernel-collector-stripped\n    BINARIES\n      debug-info.conf\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n    DEPENDENCY_OF\n      collectors\n  )\n\n  build_custom_docker_image(\n    kernel-collector-test\n    DOCKERFILE_PATH\n      \"${CMAKE_CURRENT_SOURCE_DIR}/kernel_collector_test_docker\"\n    OUT_DIR srv\n    ARTIFACTS_OF\n      bpf_wire_to_json\n      intake_wire_to_json\n      kernel_collector_test\n    OUTPUT_OF\n      kernel-collector-scripts\n    FILES\n      ../../NOTICE.txt\n      ../../LICENSE.txt\n  )\nendif()\n\n# Unit Tests\n#\nadd_unit_test(cgroup_handler LIBS agentlib test_channel render_ebpf_net_ingest_writer render_rust_ebpf_net)\nadd_unit_test(kernel_symbols LIBS agentlib)\nadd_ebpf_unit_test(kernel_collector LIBS signal_handler agentlib fastpass_util file_ops config_file libuv-static system_ops static-executable test_channel render_ebpf_net_ingest_writer render_rust_ebpf_net)\n"
  },
  {
    "path": "collector/kernel/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nFROM docker.io/bitnami/minideb:trixie@sha256:0766a3b76750541ae2c5f3b806e5201e1b2ca1549a0a036a057726dc9e5dfa4d\n\nLABEL org.label-schema.name=\"opentelemetry-ebpf-kernel-collector\"\nLABEL org.label-schema.description=\"OpenTelemetry eBPF kernel information collector\"\nLABEL org.label-schema.vcs-url=\"https://github.com/open-telemetry/opentelemetry-ebpf\"\nLABEL org.label-schema.schema-version=\"1.0\"\n\n# ca-certificates are required by libcurl\n# libllvm is not required at runtime; drop libllvm16 to avoid apt failures on bookworm\nRUN install_packages ca-certificates libcurlpp0 libgrpc++1.51 libelf1\nENV SSL_CERT_DIR=/etc/ssl/certs\n\nENV EBPF_NET_INSTALL_DIR=/srv\nENV EBPF_NET_HOST_DIR=/hostfs\nENV EBPF_NET_DATA_DIR=/var/run/ebpf_net\n\nENTRYPOINT [ \"/srv/entrypoint.sh\" ]\n\nARG BUILD_TYPE\nRUN if [ \"$BUILD_TYPE\" = \"Debug\" ]; then \\\n      install_packages bc cgdb gawk gdb gzip iputils-ping jq netcat-openbsd procps ripgrep vim valgrind; \\\n    fi\n\nCOPY srv /srv\nWORKDIR /srv\nRUN if [ ! -e /srv/kernel-collector ]; then \\\n      ln /srv/kernel-collector-stripped /srv/kernel-collector; \\\n    fi\n"
  },
  {
    "path": "collector/kernel/bpf_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n#include <util/log.h>\n\n#include <collector/kernel/bpf_handler.h>\n#include <collector/kernel/cgroup_prober.h>\n#include <collector/kernel/nat_prober.h>\n#include <collector/kernel/process_prober.h>\n#include <collector/kernel/socket_prober.h>\n#include <common/host_info.h>\n\nBPFHandler::BPFHandler(\n    uv_loop_t &loop,\n    const BpfConfiguration &bpf_config,\n    bool enable_http_metrics,\n    FileDescriptor &bpf_dump_file,\n    logging::Logger &log,\n    ::ebpf_net::ingest::Encoder *encoder,\n    HostInfo const &host_info)\n    : loop_(loop),\n      probe_handler_(log),\n      bpf_skel_(nullptr),\n      perf_(),\n      encoder_(encoder),\n      buf_poller_(nullptr),\n      enable_http_metrics_(enable_http_metrics),\n      bpf_dump_file_(bpf_dump_file),\n      log_(log),\n      last_lost_count_(0),\n      host_info_(host_info)\n{\n  // Open the BPF skeleton (doesn't load yet)\n  bpf_skel_ = probe_handler_.open_bpf_skeleton();\n  if (!bpf_skel_) {\n    throw std::system_error(errno, std::generic_category(), \"ProbeHandler couldn't open BPF skeleton\");\n  }\n\n  // Configure global variables before loading\n  probe_handler_.configure_bpf_skeleton(bpf_skel_, bpf_config);\n\n  // Now load the skeleton and set up perf rings\n  int res = probe_handler_.load_bpf_skeleton(bpf_skel_, perf_);\n  if (res != 0) {\n    probe_handler_.destroy_bpf_skeleton(bpf_skel_);\n    bpf_skel_ = nullptr;\n    throw std::system_error(errno, std::generic_category(), \"ProbeHandler couldn't load BPF skeleton\");\n  }\n}\n\nBPFHandler::~BPFHandler()\n{\n  probe_handler_.cleanup_probes();\n  probe_handler_.cleanup_tail_calls(bpf_skel_);\n  buf_poller_.reset();\n  if (bpf_skel_) {\n    probe_handler_.destroy_bpf_skeleton(bpf_skel_);\n    bpf_skel_ = nullptr;\n  }\n}\n\nvoid BPFHandler::load_buffered_poller(\n    IBufferedWriter &buffered_writer,\n    u64 boot_time_adjustment,\n    CurlEngine &curl_engine,\n    u64 socket_stats_interval_sec,\n    CgroupHandler::CgroupSettings const &cgroup_settings,\n    KernelCollectorRestarter &kernel_collector_restarter)\n{\n  LOG::trace(\"--- Starting BufferedPoller ---\");\n  buf_poller_ = std::make_unique<BufferedPoller>(\n      loop_,\n      perf_,\n      buffered_writer,\n      boot_time_adjustment,\n      curl_engine,\n      bpf_dump_file_,\n      log_,\n      probe_handler_,\n      bpf_skel_,\n      socket_stats_interval_sec,\n      cgroup_settings,\n      encoder_,\n      kernel_collector_restarter);\n  last_lost_count_ = serv_lost_count();\n}\n\nvoid BPFHandler::load_probes(::ebpf_net::ingest::Writer &writer)\n{\n  probe_handler_.load_kernel_symbols();\n\n  CgroupProber cgroup_prober(\n      probe_handler_,\n      bpf_skel_,\n      host_info_,\n      [this]() { buf_poller_->start(1, 1); },\n      [this](std::string error_loc) { check_cb(error_loc); });\n\n  if (cgroup_prober.error_count() > 0) {\n    log_.warn(\"load_probes could not close {} directories\", cgroup_prober.error_count());\n  }\n\n  /* Start instrumentation for processes */\n  ProcessProber process_prober(\n      probe_handler_,\n      bpf_skel_,\n      [this]() { buf_poller_->start(1, 1); },\n      [this](std::string error_loc) { check_cb(error_loc); });\n\n  NatProber nat_prober(probe_handler_, bpf_skel_, [this]() { buf_poller_->start(1, 1); });\n\n  // one more poll to make sure the perf rings are clear\n  buf_poller_->start(1, 1);\n\n  // send a process_steady_state message\n  writer.process_steady_state(0);\n\n  // UDP (v4+v6) DNS Replies\n  // And receive udp statistics\n  ProbeAlternatives dns_probe_alternatives{\n      \"DNS\",\n      {\n          // skb_consume_udp was introduced in f970bd9e3a06f, i.e.,\n          // v4.10-rc1~202^2~423^2~1\n          {\"on_skb_consume_udp\", \"skb_consume_udp\"},\n          // __skb_free_datagram_locked was introduced in 627d2d6b55009, i.e.,\n          // v4.7-rc1~154^2~349^2\n          {\"on___skb_free_datagram_locked\", \"__skb_free_datagram_locked\"},\n          // skb_free_datagram_locked exists in udp_recvmsg since 9d410c7960676, i.e.,\n          // v2.6.32-rc6~9^2~3\n          {\"on_skb_free_datagram_locked\", \"skb_free_datagram_locked\"},\n      }};\n  probe_handler_.start_probe(bpf_skel_, dns_probe_alternatives);\n\n  // Start instrumentation for sockets\n  SocketProber socket_prober(\n      probe_handler_,\n      bpf_skel_,\n      [this]() { buf_poller_->start(1, 1); },\n      [this](std::string error_loc) { check_cb(error_loc); },\n      log_);\n\n  // one more poll to make sure the perf rings are clear\n  buf_poller_->start(1, 1);\n\n  // send a socket_steady_state message\n  writer.socket_steady_state(0);\n\n  // probe for steady-state data\n  ProbeAlternatives probe_alternatives{\n      \"tcp rtt estimator\",\n      {\n          {\"on_tcp_rtt_estimator\", \"tcp_rtt_estimator\"},\n          {\"on_tcp_rtt_estimator\", \"tcp_update_pacing_rate\"},\n          {\"on_tcp_rtt_estimator\", \"tcp_ack\"},\n      }};\n  probe_handler_.start_probe(bpf_skel_, probe_alternatives);\n\n  // SYN timeouts\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_retransmit_timer\", \"tcp_retransmit_timer\");\n\n  // SYN-ACK timeouts\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_syn_ack_timeout\", \"tcp_syn_ack_timeout\");\n\n  // TCP resets\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_reset\", \"tcp_reset\");\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_send_active_reset\", \"tcp_send_active_reset\");\n\n  buf_poller_->start(1, 1);\n  check_cb(\"loading rtt probes\");\n\n  /**\n   *  TODO: is it possible we're missing events from the new sock notification\n   *   to the point we start the instrumentation below? we read some socket\n   *   recv state when opening the socket\n   */\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_event_data_recv\", \"tcp_event_data_recv\");\n  probe_handler_.start_probe(bpf_skel_, \"on_tcp_rcv_established\", \"tcp_rcv_established\");\n  buf_poller_->start(1, 1);\n  check_cb(\"loading tcp steady-state probes\");\n\n  // set up tail calls table\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_ON_UDP_SEND_SKB__2, \"on_udp_send_skb__2\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_ON_UDP_V6_SEND_SKB__2, \"on_udp_v6_send_skb__2\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_ON_IP_SEND_SKB__2, \"on_ip_send_skb__2\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_ON_IP6_SEND_SKB__2, \"on_ip6_send_skb__2\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_HANDLE_RECEIVE_UDP_SKB, \"handle_receive_udp_skb\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_HANDLE_RECEIVE_UDP_SKB__2, \"handle_receive_udp_skb__2\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_CONTINUE_TCP_SENDMSG, \"continue_tcp_sendmsg\");\n  probe_handler_.register_tail_call(bpf_skel_, \"tail_calls\", TAIL_CALL_CONTINUE_TCP_RECVMSG, \"continue_tcp_recvmsg\");\n\n  // udp v4 send statistics and dns requests\n  ProbeAlternatives udp_v4_alternatives{\n      \"udp v4 send skb\",\n      {\n          {\"on_udp_send_skb\", \"udp_send_skb\"},\n          {\"on_ip_send_skb\", \"ip_send_skb\"},\n      }};\n  probe_handler_.start_probe(bpf_skel_, udp_v4_alternatives);\n\n  // udp v6 send statistics and dns requests\n  ProbeAlternatives udp_v6_alternatives{\n      \"udp v6 send skb\",\n      {\n          {\"on_udp_v6_send_skb\", \"udp_v6_send_skb\"},\n          {\"on_ip6_send_skb\", \"ip6_send_skb\"},\n      }};\n  probe_handler_.start_probe(bpf_skel_, udp_v6_alternatives);\n\n  // start the udp probes\n  buf_poller_->start(1, 1);\n  check_cb(\"loading udp steady-state probes\");\n\n  // Probes for tcp-processor\n  if (enable_http_metrics_ /*|| any other tcp-processor flags eventually */) {\n\n    // Using _tcpproc as the event suffix to ensure we don't collide our kprobes\n    // with anything in 'render_bpf'\n    //\n    // Keep kretprobes registered before kprobes to the same function to avoid\n    // races\n\n    LOG::debug(\"Adding TCP processor probes\");\n\n    probe_handler_.start_probe(bpf_skel_, \"handle_kprobe__tcp_init_sock\", \"tcp_init_sock\", \"_tcpproc\");\n    probe_handler_.start_probe(bpf_skel_, \"handle_kprobe__security_sk_free\", \"security_sk_free\", \"_tcpproc\");\n    probe_handler_.start_kretprobe(bpf_skel_, \"handle_kretprobe__inet_csk_accept\", \"inet_csk_accept\", \"_tcpproc\");\n    probe_handler_.start_probe(bpf_skel_, \"handle_kprobe__inet_csk_accept\", \"inet_csk_accept\", \"_tcpproc\");\n    probe_handler_.start_kretprobe(bpf_skel_, \"handle_kretprobe__tcp_sendmsg\", \"tcp_sendmsg\", \"_tcpproc\");\n    probe_handler_.start_probe(bpf_skel_, \"handle_kprobe__tcp_sendmsg\", \"tcp_sendmsg\", \"_tcpproc\");\n    probe_handler_.start_kretprobe(bpf_skel_, \"handle_kretprobe__tcp_recvmsg\", \"tcp_recvmsg\", \"_tcpproc\");\n    probe_handler_.start_probe(bpf_skel_, \"handle_kprobe__tcp_recvmsg\", \"tcp_recvmsg\", \"_tcpproc\");\n\n    buf_poller_->start(1, 1);\n    check_cb(\"tcp processor probes\");\n  }\n\n  buf_poller_->start(1, 1);\n  check_cb(\"end of load_probes()\");\n\n  buf_poller_->set_all_probes_loaded();\n\n  probe_handler_.clear_kernel_symbols();\n}\n\nvoid BPFHandler::start_poll(u64 interval_useconds, u64 n_intervals)\n{\n  buf_poller_->start(interval_useconds, n_intervals);\n}\n\nvoid BPFHandler::slow_poll()\n{\n  buf_poller_->slow_poll();\n}\n\nu64 BPFHandler::serv_lost_count()\n{\n  return buf_poller_->serv_lost_count();\n}\n\nvoid BPFHandler::check_cb(std::string error_loc)\n{\n  u64 lost_count = serv_lost_count();\n  if (lost_count > last_lost_count_) {\n    u64 diff_lost_count = lost_count - last_lost_count_;\n    last_lost_count_ = lost_count;\n    log_.warn(\"after {}, cumulative lost count non-zero: {} new, {} total\", error_loc, diff_lost_count, lost_count);\n  }\n}\n\n#ifndef NDEBUG\nvoid BPFHandler::debug_bpf_lost_samples()\n{\n  if (buf_poller_) {\n    buf_poller_->debug_bpf_lost_samples();\n  }\n}\n#endif\n"
  },
  {
    "path": "collector/kernel/bpf_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/host_info.h>\n#include <platform/platform.h>\n\n#include <collector/kernel/buffered_poller.h>\n#include <collector/kernel/probe_handler.h>\n#include <generated/ebpf_net/ingest/encoder.h>\n#include <util/curl_engine.h>\n#include <util/logger.h>\n#include <uv.h>\n\n#include <memory>\n\n// Forward declaration for the skeleton\nstruct render_bpf_bpf;\n\nclass BPFHandler {\n  friend class KernelCollectorTest;\n\npublic:\n  /**\n   * c'tor\n   *\n   * Will throw if:\n   * 1. PerfContainer cannot be allocated\n   * 2. ProbeHandler can't load BPF skeleton\n   */\n  BPFHandler(\n      uv_loop_t &loop,\n      const BpfConfiguration &bpf_config,\n      bool enable_http_metrics,\n      FileDescriptor &bpf_dump_file,\n      logging::Logger &log,\n      ::ebpf_net::ingest::Encoder *encoder,\n      HostInfo const &host_info_);\n\n  /**\n   * d'tor\n   */\n  virtual ~BPFHandler();\n\n  /**\n   * Loads the buffered poller\n   */\n  void load_buffered_poller(\n      IBufferedWriter &buffered_writer,\n      u64 boot_time_adjustment,\n      CurlEngine &curl_engine,\n      u64 socket_stats_interval_sec,\n      CgroupHandler::CgroupSettings const &cgroup_settings,\n      KernelCollectorRestarter &kernel_collector_restarter);\n\n  /**\n   * Loads BPF probes. Takes writer to send out steady_state msgs\n   * where necessary.\n   */\n  void load_probes(::ebpf_net::ingest::Writer &writer);\n\n  /**\n   * Calls start(interval_useconds, n_intervals) on buf_poller_\n   */\n  void start_poll(u64 interval_useconds, u64 n_intervals);\n\n  /**\n   * Calls less frequent cleanup operations on buf_poller_\n   */\n  void slow_poll();\n\n  /**\n   * Calls serv_lost_count() on buf_poller_\n   */\n  u64 serv_lost_count();\n\n  /**\n   * Callback passed to probers for checking lost count\n   */\n  void check_cb(std::string error_loc);\n\n#ifndef NDEBUG\n  /**\n   * Debug code for internal development to simulate lost BPF samples (PERF_RECORD_LOST) in BufferedPoller.\n   */\n  void debug_bpf_lost_samples();\n#endif\n\nprivate:\n  uv_loop_t &loop_;\n  ProbeHandler probe_handler_;\n  struct render_bpf_bpf *bpf_skel_;\n  PerfContainer perf_;\n  ::ebpf_net::ingest::Encoder *encoder_;\n  std::unique_ptr<BufferedPoller> buf_poller_;\n  bool enable_http_metrics_;\n  FileDescriptor &bpf_dump_file_;\n  logging::Logger &log_;\n  u64 last_lost_count_;\n  HostInfo const host_info_;\n};\n"
  },
  {
    "path": "collector/kernel/bpf_src/render_bpf.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// Global variables that can be set from userspace\nvolatile const long boot_time_adjustment = 0;\nvolatile const long filter_ns = 1000000000;    // Default 1 second in nanoseconds\nvolatile const int enable_tcp_data_stream = 0; // Set to 1 to enable TCP data stream processing\n\n#include <vmlinux.h>\n\n#include <bpf/bpf_core_read.h>\n#include <bpf/bpf_endian.h>\n#include <bpf/bpf_helpers.h>\n#include <bpf/bpf_tracing.h>\n\nextern int LINUX_KERNEL_VERSION __kconfig;\n\n// Networking macros\n#define tcp_sk(ptr) container_of(ptr, struct tcp_sock, inet_conn.icsk_inet.sk)\n#define inet_csk(ptr) container_of(ptr, struct inet_connection_sock, icsk_inet.sk)\n#define inet_sk(ptr) container_of(ptr, struct inet_sock, sk)\n#define sk_num __sk_common.skc_num\n#define sk_dport __sk_common.skc_dport\n#define sk_v6_daddr __sk_common.skc_v6_daddr\n#define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr\n#define sk_daddr __sk_common.skc_daddr\n#define sk_rcv_saddr __sk_common.skc_rcv_saddr\n#define sk_family __sk_common.skc_family\n#define sk_state __sk_common.skc_state\n#define AF_INET 2   /* Internet IP Protocol \t*/\n#define AF_INET6 10 /* IP version 6\t\t\t*/\n#define s6_addr16 in6_u.u6_addr16\n#define s6_addr32 in6_u.u6_addr32\n#define inet_num sk.__sk_common.skc_num\n#define fl4_sport uli.ports.sport\n#define fl4_dport uli.ports.dport\n#define fl6_sport uli.ports.sport\n#define fl6_dport uli.ports.dport\n#define rsk_listener __req_common.skc_listener\n\n// pointer error handling\n#define MAX_ERRNO 4095\n#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)\n\n#include \"vmlinux_extensions.h\"\n\n#include \"vmlinux_compat.h\"\n\n// Configuration\n#include \"config.h\"\n#include \"render_bpf.h\"\n\n// Perf events - per-CPU perf ring buffer\nstruct {\n  __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);\n  __uint(key_size, sizeof(u32));\n  __uint(value_size, sizeof(u32));\n} events SEC(\".maps\");\n#include \"ebpf_net/agent_internal/bpf.h\"\n\n// Common utility functions\n#include \"tcp-processor/bpf_debug.h\"\n#include \"tcp-processor/bpf_types.h\"\n\nstatic u64 abs_val(int val)\n{\n  return val < 0 ? -val : val;\n}\n\n/* HELPERS FOR FILLERS */\nstruct pkts_if_t {\n  u32 packets_out;\n  u32 sacked_out;\n  u32 lost_out;\n  u32 retrans_out;\n};\n\nstatic inline u32 packets_in_flight_helper(struct sock *sk)\n{\n  struct tcp_sock *tp = tcp_sk(sk);\n  struct pkts_if_t t = {};\n\n  bpf_probe_read(&t.packets_out, sizeof(u32), &tp->packets_out);\n  bpf_probe_read(&t.sacked_out, sizeof(u32), &tp->sacked_out);\n  bpf_probe_read(&t.lost_out, sizeof(u32), &tp->lost_out);\n  bpf_probe_read(&t.retrans_out, sizeof(u32), &tp->retrans_out);\n  return t.packets_out - (t.sacked_out + t.lost_out) + t.retrans_out;\n}\n\n/**\n * Tracking open sockets\n */\nstruct tcp_open_socket_t {\n  u32 tgid;\n  u32 rcv_holes;\n  u64 last_output;\n  u64 bytes_received; /* last observed */\n  u32 rcv_delivered;\n  u32 padding;\n#if TCP_STATS_ON_PARENT\n  struct sock *parent; /* parent listen socket if accepted, null otherwise */\n#endif\n};\n\nstruct udp_stats_t {\n  u64 last_output;\n  u32 laddr6[4];\n  u32 raddr6[4];\n  u16 lport;\n  u16 rport;\n  u8 addr_changed;\n  u32 bytes;   /* bytes seen on socket since last submit */\n  u32 packets; /* packets seen since last submit */\n  u32 drops;   /* drops total for socket as of last submit (receive side only) */\n};\n\n/**\n * Tracking open sockets\n */\nstruct udp_open_socket_t {\n  u32 tgid;\n  struct udp_stats_t stats[2];\n};\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, TGID);\n  __type(value, TGID);\n  __uint(max_entries, TABLE_SIZE__TGID_INFO);\n} tgid_info_table SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct task_struct *);\n  __type(value, struct task_struct *);\n  __uint(max_entries, TABLE_SIZE__DEAD_GROUP_TASKS);\n} dead_group_tasks SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, u32);\n  __type(value, u32);\n  __uint(max_entries, TABLE_SIZE__SEEN_INODES);\n  __uint(map_flags, BPF_F_NO_PREALLOC);\n} seen_inodes SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct nf_conn *);\n  __type(value, struct nf_conn *);\n  __uint(max_entries, TABLE_SIZE__SEEN_CONNTRACKS);\n} seen_conntracks SEC(\".maps\"); // Conntracks that we've reported to userspace\n\n// Stash skb per-CPU for __nf_conntrack_confirm entry to kretprobe path.\n// __nf_conntrack_confirm runs in softirq/non-sleepable context; not re-entrant per-CPU.\nstruct {\n  __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);\n  __uint(max_entries, 1);\n  __type(key, u32);\n  __type(value, struct sk_buff *);\n} nfct_confirm_skb SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct sock *);\n  __type(value, struct tcp_open_socket_t);\n  __uint(max_entries, TABLE_SIZE__TCP_OPEN_SOCKETS);\n} tcp_open_sockets SEC(\".maps\"); /* information on live sks */\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct sock *);\n  __type(value, struct udp_open_socket_t);\n  __uint(max_entries, TABLE_SIZE__UDP_OPEN_SOCKETS);\n} udp_open_sockets SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, u64);\n  __type(value, struct sock *);\n  __uint(max_entries, TABLE_SIZE__UDP_GET_PORT_HASH);\n} udp_get_port_hash SEC(\".maps\");\n\nBEGIN_DECLARE_SAVED_ARGS(cgroup_exit)\npid_t tgid;\nEND_DECLARE_SAVED_ARGS(cgroup_exit)\n\n/*\n * cgroups subsystem used for cgroup probing\n * This is used by cgroup related probes to filter out cgroups that aren't in the memory hierarchy.\n * See SUBSYS macro in /linux_kernel/kernel/cgroup/cgroup.c.\n */\nstatic __always_inline int get_flow_cgroup_subsys()\n{\n  return bpf_core_enum_value(enum cgroup_subsys_id, memory_cgrp_id);\n}\n\n#define FLOW_CGROUP_SUBSYS (get_flow_cgroup_subsys())\n\n/* forward declarations */\n\nstatic __always_inline void\nperf_check_and_submit_dns(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb, u8 proto, u16 sport, u16 dport, int is_rx);\n\n////////////////////////////////////////////////////////////////////////////////////\n/* PROCESSES */\n\nstatic int report_pid_exit(TIMESTAMP timestamp, struct pt_regs *ctx, struct task_struct *task)\n{\n  perf_submit_agent_internal__pid_exit(ctx, timestamp, task->tgid, task->pid, task->exit_code);\n  return 1;\n}\n\nstatic int set_task_group_dead(struct pt_regs *ctx, struct task_struct *tsk)\n{\n  int ret = bpf_map_update_elem(&dead_group_tasks, &tsk, &tsk, BPF_NOEXIST);\n  if (ret != 0) {\n    // Check if key already exists to distinguish duplicate vs table full\n    void *existing = bpf_map_lookup_elem(&dead_group_tasks, &tsk);\n    if (existing) {\n#if DEBUG_OTHER_MAP_ERRORS\n      bpf_trace_printk(\"set_task_group_dead: set_task_group_dead duplicate insert, dropping tsk tsk=%llx\\n\", tsk);\n#endif\n      bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_DEAD_GROUP_TASKS, 0, (u64)tsk);\n      return 0;\n    } else {\n#if DEBUG_OTHER_MAP_ERRORS\n      bpf_trace_printk(\"set_task_group_dead: set_task_group_dead table is full, dropping tsk tsk=%llx\\n\", tsk);\n#endif\n      bpf_log(ctx, BPF_LOG_TABLE_FULL, BPF_TABLE_DEAD_GROUP_TASKS, 0, (u64)tsk);\n      return 0;\n    }\n  }\n\n  return 1;\n}\n\nstatic int task_group_check_dead_and_remove(struct pt_regs *ctx, struct task_struct *tsk)\n{\n  int ret = bpf_map_delete_elem(&dead_group_tasks, &tsk);\n  if (ret != 0) {\n    // wasn't in map\n    return 0;\n  }\n\n  return 1;\n}\n\nstatic int task_is_group_leader(struct pt_regs *ctx, struct task_struct *tsk)\n{\n  int ret;\n\n  if (tsk == NULL) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return 0;\n  }\n\n  struct task_struct *group_leader = NULL;\n  ret = bpf_probe_read(&group_leader, sizeof(group_leader), &tsk->group_leader);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n  if (group_leader == NULL) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return 0;\n  }\n\n  if (tsk == group_leader) {\n    return 1;\n  }\n\n  return 0;\n}\n\nstatic u64 get_task_cgroup(struct pt_regs *ctx, struct task_struct *tsk)\n{\n  int ret;\n\n  if (tsk == NULL) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return 0;\n  }\n\n  struct cgroup *cgrp =\n      (struct cgroup *)BPF_CORE_READ((struct task_struct___with_css_set *)tsk, cgroups, subsys[FLOW_CGROUP_SUBSYS], cgroup);\n  if (cgrp == NULL) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return 0;\n  }\n\n  return (u64)cgrp;\n}\n\nstatic pid_t get_task_parent(struct pt_regs *ctx, struct task_struct *tsk)\n{\n  int ret = 0;\n\n  pid_t parent_tgid = BPF_CORE_READ(tsk, parent, tgid);\n  ret = 0;\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return -1;\n  }\n\n  return parent_tgid;\n}\n\n// insert_tgid_info:\n// adds a task to the 'tgid_info' table\n// returns 0 if the task was not inserted because it was a duplicate or table full\n// returns 1 if a task is new and was inserted\nstatic int insert_tgid_info(struct pt_regs *ctx, TGID tgid)\n{\n  int ret = bpf_map_update_elem(&tgid_info_table, &tgid, &tgid, BPF_NOEXIST);\n  if (ret != 0) {\n    // Check if key already exists to distinguish duplicate vs table full\n    void *existing = bpf_map_lookup_elem(&tgid_info_table, &tgid);\n    if (existing) {\n      // if we've already seen it, then don't make a duplicate msg\n      // could arise if a new task shows up during the initial scan\n      // or this is a thread inside an existing group, so we're seeing the tgid again\n      return 0;\n    } else {\n#if DEBUG_OTHER_MAP_ERRORS\n      bpf_trace_printk(\"insert_tgid_info: tgid_info table is full, dropping task tgid=%u\\n\", tgid);\n#endif\n      bpf_log(ctx, BPF_LOG_TABLE_FULL, BPF_TABLE_TGID_INFO, tgid, tgid);\n      return 0;\n    }\n  }\n\n  return 1;\n}\n\n// remove_tgid_info:\n// removes a task to the 'tgid_info' table\n// returns 0 if the tgid was not found in the table or if there was an error\n// returns 1 if a task was removed successfully\nstatic int remove_tgid_info(struct pt_regs *ctx, TGID tgid)\n{\n  int ret = bpf_map_delete_elem(&tgid_info_table, &tgid);\n  if (ret != 0) {\n#if DEBUG_OTHER_MAP_ERRORS\n    bpf_trace_printk(\"remove_tgid_info: can't remove missing tgid=%u\\n\", tgid);\n#endif\n    return 0;\n  }\n\n  return 1;\n}\n\n// note is the tgid is dead or not\n// used later by on_cgroup_exit handling\nSEC(\"kprobe/taskstats_exit\")\nint on_taskstats_exit(struct pt_regs *ctx)\n{\n  struct task_struct *tsk = (struct task_struct *)PT_REGS_PARM1(ctx);\n  int group_dead = (int)PT_REGS_PARM2(ctx);\n\n  if (group_dead) {\n    set_task_group_dead(ctx, tsk);\n  }\n  return 0;\n}\n\n// end\n// this routine is called by 'do_exit' near the end of the destruction of a task\n// but after all of the resources has been cleaned up, including file descriptor references\nSEC(\"kprobe/cgroup_exit\")\nint on_cgroup_exit(struct pt_regs *ctx)\n{\n  struct task_struct *tsk = (struct task_struct *)PT_REGS_PARM1(ctx);\n  int ret;\n  pid_t tgid = 0;\n  ret = bpf_probe_read(&tgid, sizeof(tgid), &(tsk->tgid));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  // only consider tasks that are the last task in the thread group\n  // since the process will not be terminating if there are more threads left\n  if (!task_group_check_dead_and_remove(ctx, tsk)) {\n    return 0;\n  }\n\n  GET_PID_TGID;\n\n  BEGIN_SAVE_ARGS(cgroup_exit)\n  SAVE_ARG(tgid)\n  END_SAVE_ARGS(cgroup_exit)\n\n  return 0;\n}\n\nSEC(\"kretprobe/cgroup_exit\")\nint onret_cgroup_exit(struct pt_regs *ctx)\n{\n  GET_PID_TGID;\n\n  GET_ARGS_MISSING_OK(cgroup_exit, args)\n  if (args == NULL) {\n    return 0;\n  }\n\n  pid_t tgid = args->tgid;\n\n  DELETE_ARGS(cgroup_exit);\n\n  // do some cleanup from our hashmap\n  // if we didn't record this tgid before, a log message will have\n  // fired, and won't send this along to the server\n  if (!remove_tgid_info(ctx, tgid)) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n\n  char comm[16] = {};\n  bpf_get_current_comm(comm, sizeof(comm));\n\n  perf_submit_agent_internal__pid_close(ctx, now, tgid, (u8 *)comm);\n\n  return 0;\n}\n\n// set_task_comm: notice when command line is set for a process\nSEC(\"kprobe/__set_task_comm\")\nint on_set_task_comm(struct pt_regs *ctx)\n{\n  struct task_struct *tsk = (struct task_struct *)PT_REGS_PARM1(ctx);\n  const char *buf = (const char *)PT_REGS_PARM2(ctx);\n  int ret;\n\n  // only interested tasks considered the 'leader' of the group,\n  // effectively userland processes, not threads\n  if (!task_is_group_leader(ctx, tsk)) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n\n  pid_t tgid = 0;\n  ret = bpf_probe_read(&tgid, sizeof(tgid), &tsk->tgid);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  perf_submit_agent_internal__pid_set_comm(ctx, now, tgid, (uint8_t *)buf);\n  return 0;\n}\n\n// start\nSEC(\"kprobe/wake_up_new_task\")\nint on_wake_up_new_task(struct pt_regs *ctx)\n{\n  struct task_struct *tsk = (struct task_struct *)PT_REGS_PARM1(ctx);\n  int ret;\n\n  pid_t tgid = 0;\n  ret = bpf_probe_read(&tgid, sizeof(tgid), &tsk->tgid);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  // mark the thread group id as seen, and if we've already seen it, return\n  if (!insert_tgid_info(ctx, tgid)) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n  u64 cgroup = get_task_cgroup(ctx, tsk);\n  pid_t parent_tgid = get_task_parent(ctx, tsk);\n\n  u8 comm[16] = {};\n  ret = bpf_probe_read(comm, sizeof(comm), tsk->comm);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  perf_submit_agent_internal__pid_info(ctx, now, tgid, comm, cgroup, parent_tgid);\n\n  return 0;\n}\n\n// existing\nSEC(\"kretprobe/get_pid_task\")\nint onret_get_pid_task(struct pt_regs *ctx)\n{\n  int ret;\n  struct task_struct *tsk = (struct task_struct *)PT_REGS_RC(ctx);\n\n  pid_t tgid = 0;\n  ret = bpf_probe_read(&tgid, sizeof(tgid), &tsk->tgid);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), (u64)tsk, 0);\n    return 0;\n  }\n\n  // mark the task as seen, and if we've already seen it, return\n  if (!insert_tgid_info(ctx, tgid)) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n  u64 cgroup = get_task_cgroup(ctx, tsk);\n  pid_t parent_tgid = get_task_parent(ctx, tsk);\n\n  u8 comm[16] = {};\n  ret = bpf_probe_read(comm, sizeof(comm), tsk->comm);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  perf_submit_agent_internal__pid_info(ctx, now, tgid, comm, cgroup, parent_tgid);\n\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* TCP SOCKETS */\nstatic inline u32 tcp_get_delivered(struct sock *sk)\n{\n  struct tcp_sock *tp = tcp_sk(sk);\n  /* delivered accounting was introduced in ddf1af6fa00e77, i.e.,\n   * v4.6-rc1~91^2~316^2~3 */\n  if (LINUX_KERNEL_VERSION < KERNEL_VERSION(4, 6, 0)) {\n    u32 packets_out = 0;\n    u32 sacked_out = 0;\n    bpf_probe_read(&packets_out, sizeof(packets_out), &tp->packets_out);\n    bpf_probe_read(&sacked_out, sizeof(sacked_out), &tp->sacked_out);\n    return packets_out - sacked_out;\n  } else {\n    u32 delivered = 0;\n    bpf_probe_read(&delivered, sizeof(delivered), &tp->delivered);\n    return delivered;\n  }\n}\n\nstatic inline void\nreport_rtt_estimator(struct pt_regs *ctx, struct sock *sk, struct tcp_open_socket_t *sk_info, u64 now, bool adjust)\n{\n  int ret;\n  sk_info->last_output = now; // update the time in place\n\n  u32 rcv_rtt_us = 0;\n  if (LINUX_KERNEL_VERSION < KERNEL_VERSION(4, 12, 0)) {\n    bpf_probe_read(&rcv_rtt_us, sizeof(rcv_rtt_us), &((struct tcp_sock___rcv_rtt_est_rtt *)tcp_sk(sk))->rcv_rtt_est.rtt);\n  } else {\n    bpf_probe_read(&rcv_rtt_us, sizeof(rcv_rtt_us), &tcp_sk(sk)->rcv_rtt_est.rtt_us);\n  }\n\n  // These values need to be taken from bpf_probe_read\n  u32 srtt = 0;\n  u32 snd_cwnd = 0;\n  u64 bytes_acked = 0;\n  u8 ca_state = 0;\n  u32 packets_retrans = 0;\n  u64 bytes_received = 0;\n\n  ret = bpf_probe_read(&srtt, sizeof(srtt), &(tcp_sk(sk)->srtt_us));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  ret = bpf_probe_read(&snd_cwnd, sizeof(snd_cwnd), &(tcp_sk(sk)->snd_cwnd));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  ret = bpf_probe_read(&bytes_acked, sizeof(bytes_acked), &(tcp_sk(sk)->bytes_acked));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  ret = bpf_probe_read(&ca_state, sizeof(ca_state), &(*(&(inet_csk(sk)->icsk_sync_mss) + 1)));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  ret = bpf_probe_read(&packets_retrans, sizeof(packets_retrans), &(tcp_sk(sk)->total_retrans));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  ret = bpf_probe_read(&bytes_received, sizeof(bytes_received), &(tcp_sk(sk)->bytes_received));\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return;\n  }\n\n  // adjustment for bytes reported when the connection is closed\n  // zero out last two bits to account for this\n  if (adjust) {\n    bytes_acked &= ~3ull;\n    bytes_received &= ~3ull;\n  }\n\n  perf_submit_agent_internal__rtt_estimator(\n      ctx,\n      now,\n      srtt,\n      snd_cwnd,\n      bytes_acked,\n      ca_state,\n      (__u64)sk,\n      packets_in_flight_helper(sk),\n      tcp_get_delivered(sk),\n      packets_retrans,\n      sk_info->rcv_holes,\n      bytes_received,\n      sk_info->rcv_delivered,\n      rcv_rtt_us);\n}\n\n// add tcp_open_socket\n//\n// should be paired with a `perf_submit_agent_internal__new_sock_created`\n// in most circumstances, however we don't do this in the case of already\n// existing sockets.\n//\n// returns 1 if it added the socket, 0 if it already existed,\n// and -1 if the table is full, broken, or out of memory\n// this operation is atomic and not susceptible to race conditions, because\n// map.insert is atomic\n\nstatic int add_tcp_open_socket(struct pt_regs *ctx, struct sock *sk, u32 tgid, u64 now, struct sock *parent)\n{\n  int ret;\n\n#if TRACE_TCP_SOCKETS\n  bpf_trace_printk(\"add_tcp_open_socket: %llx (tgid=%u)\\n\", sk, tgid);\n#endif\n\n  struct tcp_open_socket_t sk_info = {\n      .tgid = tgid,\n      .last_output = 0, // always do the first reporting\n      .rcv_holes = 0,\n      .rcv_delivered = 0,\n#if TCP_STATS_ON_PARENT\n      .parent = parent\n#endif\n  };\n\n  ret = bpf_probe_read(&sk_info.bytes_received, sizeof(sk_info.bytes_received), &tcp_sk(sk)->bytes_received);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return -1;\n  }\n\n  ret = bpf_map_update_elem(&tcp_open_sockets, &sk, &sk_info, BPF_NOEXIST);\n\n  if (ret != 0) {\n    // Check if key already exists to distinguish duplicate vs table full\n    void *existing = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n    if (existing) {\n      return 0;\n    } else {\n#if DEBUG_TCP_SOCKET_ERRORS\n      bpf_trace_printk(\"add_tcp_open_socket: tcp_open_sockets table is full, dropping socket sk=%llx (tgid=%u)\\n\", sk, tgid);\n#endif\n      bpf_log(ctx, BPF_LOG_TABLE_FULL, BPF_TABLE_TCP_OPEN_SOCKETS, tgid, (u64)sk);\n      return -1;\n    }\n  }\n  return 1;\n}\n\nstatic void remove_tcp_open_socket(struct pt_regs *ctx, struct sock *sk)\n{\n  struct tcp_open_socket_t *sk_info_p;\n  sk_info_p = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info_p) {\n    return;\n  }\n\n#if TRACE_TCP_SOCKETS\n  bpf_trace_printk(\"remove_tcp_open_socket: %llx\\n\", sk);\n#endif\n\n  // The lookup was successful.  Make a copy before deleting the entry, and only send related telemetry if the delete is\n  // successful.\n  struct tcp_open_socket_t sk_info = *sk_info_p;\n\n  // do some cleanup from our hashmap\n  int ret = bpf_map_delete_elem(&tcp_open_sockets, &sk);\n  if (ret != 0) {\n    // Another thread must have already deleted the entry for this sk.\n    return;\n  }\n\n  // always report last rtt estimator before close\n  // for short-lived connections, we won't see any data otherwise\n  u64 now = get_timestamp();\n  report_rtt_estimator(ctx, sk, &sk_info, now, true);\n\n  perf_submit_agent_internal__close_sock_info(ctx, now, (__u64)sk);\n}\n\nstatic inline void submit_set_state_ipv6(struct pt_regs *ctx, u64 now, int tx_rx, struct sock *sk)\n{\n  struct sock *skp = NULL;\n  bpf_probe_read(&skp, sizeof(skp), &sk);\n  if (!skp) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return;\n  }\n  u16 dport = 0;\n  u16 sport = 0;\n  uint8_t daddr[16] = {};\n  uint8_t saddr[16] = {};\n  // These values need to be taken from bpf_probe_read\n  bpf_probe_read(&dport, sizeof(dport), &(skp->sk_dport));\n  bpf_probe_read(&sport, sizeof(sport), &(skp->sk_num));\n  bpf_probe_read(daddr, sizeof(daddr), (uint8_t *)(sk->sk_v6_daddr.in6_u.u6_addr32));\n  bpf_probe_read(saddr, sizeof(saddr), (uint8_t *)(sk->sk_v6_rcv_saddr.in6_u.u6_addr32));\n  perf_submit_agent_internal__set_state_ipv6(ctx, now, daddr, saddr, bpf_ntohs(dport), sport, (__u64)sk, tx_rx);\n}\n\n// state - we want to get the 5-tuple as early as possible.\nstatic inline void submit_set_state_ipv4(struct pt_regs *ctx, u64 now, int tx_rx, struct sock *sk)\n{\n  struct sock *skp = NULL;\n  bpf_probe_read(&skp, sizeof(skp), &sk);\n  if (!skp) {\n    bpf_log(ctx, BPF_LOG_INVALID_POINTER, 0, 0, 0);\n    return;\n  }\n  u16 dport = 0;\n  u16 sport = 0;\n  u32 daddr = 0;\n  u32 saddr = 0;\n  // These values need to be taken from bpf_probe_read\n  bpf_probe_read(&dport, sizeof(dport), &(skp->sk_dport));\n  bpf_probe_read(&sport, sizeof(sport), &(skp->sk_num));\n  bpf_probe_read_kernel(&daddr, sizeof(daddr), &sk->sk_daddr);\n  bpf_probe_read_kernel(&saddr, sizeof(saddr), &sk->sk_rcv_saddr);\n\n  perf_submit_agent_internal__set_state_ipv4(ctx, now, daddr, saddr, bpf_ntohs(dport), sport, (__u64)sk, tx_rx);\n}\n\nstatic inline void submit_reset_tcp_counters(struct pt_regs *ctx, u64 now, u64 pid, struct sock *sk)\n{\n  int ret;\n  u64 bytes_acked = 0;\n  u32 packets_retrans = 0;\n  u64 bytes_received = 0;\n\n  bytes_acked = BPF_CORE_READ(tcp_sk(sk), bytes_acked);\n  packets_retrans = BPF_CORE_READ(tcp_sk(sk), total_retrans);\n  bytes_received = BPF_CORE_READ(tcp_sk(sk), bytes_received);\n\n  perf_submit_agent_internal__reset_tcp_counters(\n      ctx, now, (__u64)sk, bytes_acked, tcp_get_delivered(sk), packets_retrans, bytes_received, pid);\n}\n\n// common logic for handling existing sockets\nstatic int ensure_tcp_existing(struct pt_regs *ctx, struct sock *sk, u32 pid)\n{\n  if (!sk) {\n    return -1;\n  }\n\n  u16 family = BPF_CORE_READ(sk, sk_family);\n\n  if (family != AF_INET && family != AF_INET6) {\n    return -1;\n  }\n\n  u64 now = get_timestamp();\n\n  int tx_rx = 0;\n  u8 state = BPF_CORE_READ(sk, sk_state);\n  if (state == TCP_LISTEN) {\n    tx_rx = 2;\n  }\n\n  int ret = add_tcp_open_socket(ctx, sk, pid, now, NULL);\n  if (ret == 1) {\n    submit_reset_tcp_counters(ctx, now, pid, sk);\n    if (family == AF_INET6) {\n      submit_set_state_ipv6(ctx, now, tx_rx, sk);\n    } else {\n      submit_set_state_ipv4(ctx, now, tx_rx, sk);\n    }\n  }\n  return ret;\n}\n\n//\n// HACK: Add tcp sockets that should exist to the table but don't\n//\n\n#if TCP_EXISTING_HACK\nstatic struct tcp_open_socket_t *tcp_existing_hack(struct pt_regs *ctx, struct sock *sk)\n{\n  // Can only do this hack if we are in a userland process context\n  PID_TGID _pid_tgid = bpf_get_current_pid_tgid();\n  TGID _tgid = TGID_FROM_PID_TGID(_pid_tgid);\n  PID _pid = PID_FROM_PID_TGID(_pid_tgid);\n  if (_tgid == 0 || _pid == 0) {\n    return NULL;\n  }\n\n  // ensure the tcp socket exists\n  int ret = ensure_tcp_existing(ctx, sk, _tgid);\n#if DEBUG_TCP_SOCKET_ERRORS\n  if (ret == 1) {\n    bpf_trace_printk(\n        \"tcp_update_stats: add_tcp_open_socket of missing socket: %llx (tgid=%u, cpu=%u)\\n\",\n        sk,\n        _tgid,\n        bpf_get_smp_processor_id());\n    bpf_log(ctx, BPF_LOG_EXISTING_HACK, BPF_TABLE_TCP_OPEN_SOCKETS, (u64)sk, 0);\n  } else if (ret == 0) {\n    // already existing, okay\n  } else if (ret < 0) {\n    bpf_trace_printk(\"tcp_update_stats: add_tcp_open_socket failed: %llx (tgid=%u)\\n\", sk, _tgid);\n  }\n#endif\n  struct tcp_open_socket_t *sk_info_p = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  return sk_info_p;\n}\n#endif\n\n// Ends a tcp socket lifetime and starts a new one\n// Prep for Ticket #1166\nstatic void restart_tcp_socket(struct pt_regs *ctx, TIMESTAMP now, struct sock *sk)\n{\n  GET_PID_TGID;\n\n  // Connect to af_unspec starts a new span\n  remove_tcp_open_socket(ctx, sk);\n\n  // add an entry to our hash map\n  int ret = add_tcp_open_socket(ctx, sk, _tgid, now, NULL);\n  if (ret == 1) {\n    perf_submit_agent_internal__new_sock_created(ctx, now, _tgid, (__u64)sk);\n  } else if (ret == 0) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"tcp_init_sock: add_tcp_open_socket of existing socket: %llx (tgid=%u)\\n\", sk, _tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, _tgid, (u64)sk);\n  } else {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"tcp_init_sock: add_tcp_open_socket failed: %llx (tgid=%u)\\n\", sk, _tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, _tgid, abs_val(ret));\n  }\n}\n\n// connectors\nSEC(\"kprobe/tcp_connect\")\nint on_tcp_connect(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n#if TCP_EXISTING_HACK\n\n    sk_info = tcp_existing_hack(ctx, sk);\n    if (sk_info == NULL) {\n      return 0;\n    }\n\n#else\n\n    bpf_log(ctx, BPF_LOG_TABLE_MISSING_KEY, BPF_TABLE_TCP_OPEN_SOCKETS, (u64)sk, 0);\n    return 0;\n\n#endif\n  }\n\n  u64 now = get_timestamp();\n  int tx_rx = 1;\n  u16 family = BPF_CORE_READ(sk, sk_family);\n  if (family == AF_INET) {\n    submit_set_state_ipv4(ctx, now, tx_rx, sk);\n  } else if (family == AF_INET6) {\n    submit_set_state_ipv6(ctx, now, tx_rx, sk);\n  } else {\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n  }\n  return 0;\n}\n\n// listeners (acceptors)\n// XXX: this function might fail so maybe we want to do some sort of kretprobe?\n// I assume that if it fails, the user will handle things in a clever way\n// and destroy the socket. But who knows?\nSEC(\"kprobe/inet_csk_listen_start\")\nint on_inet_csk_listen_start(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  // filter out non-tcp connections\n  u16 family = BPF_CORE_READ(sk, sk_family);\n  if (family != AF_INET && family != AF_INET6)\n    return 0;\n\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n#if TCP_EXISTING_HACK\n\n    sk_info = tcp_existing_hack(ctx, sk);\n    if (sk_info == NULL) {\n      return 0;\n    }\n\n#else\n\n    bpf_log(ctx, BPF_LOG_TABLE_MISSING_KEY, BPF_TABLE_TCP_OPEN_SOCKETS, (u64)sk, 0);\n    return 0;\n\n#endif\n  }\n\n  u64 now = get_timestamp();\n  int tx_rx = 2; // this is the listener\n  if (family == AF_INET) {\n    submit_set_state_ipv4(ctx, now, tx_rx, sk);\n  } else if (family == AF_INET6) {\n    submit_set_state_ipv6(ctx, now, tx_rx, sk);\n  } else {\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n  }\n  return 0;\n}\n\n#if TCP_LIFETIME_HACK\n//\n// HACK: Remove open socket from table if it exists\n//\nstatic void tcp_lifetime_hack(struct pt_regs *ctx, struct sock *sk)\n{\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (sk_info) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"tcp_lifetime_hack: tcp_lifetime_hack, sk=%llx, cpu=%u\\n\", sk, bpf_get_smp_processor_id());\n    bpf_log(ctx, BPF_LOG_LIFETIME_HACK, BPF_TABLE_TCP_OPEN_SOCKETS, (u64)sk, 0);\n#endif\n    remove_tcp_open_socket(ctx, sk);\n  }\n}\n#endif\n\n// --- tcp_init_sock ----------------------------------------------------\n// Where the start of TCP socket lifetimes is for IPv4 and IPv6\nSEC(\"kprobe/tcp_init_sock\")\nint on_tcp_init_sock(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  GET_PID_TGID;\n\n  u64 now = get_timestamp();\n\n#if TCP_LIFETIME_HACK\n  // remove the entry from our hash map if it's there\n  tcp_lifetime_hack(ctx, sk);\n#endif\n\n  // add an entry to our hash map\n  int ret = add_tcp_open_socket(ctx, sk, _tgid, now, NULL);\n  if (ret == 1) {\n    perf_submit_agent_internal__new_sock_created(ctx, now, _tgid, (__u64)sk);\n  } else if (ret == 0) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"tcp_init_sock: add_tcp_open_socket of existing socket: %llx (tgid=%u)\\n\", sk, _tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, _tgid, (u64)sk);\n  } else {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"tcp_init_sock: add_tcp_open_socket failed: %llx (tgid=%u)\\n\", sk, _tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, _tgid, abs_val(ret));\n  }\n\n  return 0;\n}\n\n// --- inet_csk_accept --------------------------------------------------\n// Called when a listen socket accepts gets a connection\n\nBEGIN_DECLARE_SAVED_ARGS(on_inet_csk_accept)\nstruct sock *sk;\nint flags;\nu32 _pad_0; // required alignment\nint *err;\nEND_DECLARE_SAVED_ARGS(on_inet_csk_accept)\n\nSEC(\"kprobe/inet_csk_accept\")\nint on_inet_csk_accept(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  int flags = (int)PT_REGS_PARM2(ctx);\n  int *err = (int *)PT_REGS_PARM3(ctx);\n\n  // In kernels before 4.11, there's no bool kern parameter\n  // The kern parameter doesn't exist, so we ignore it\n  // bool kern = (bool)PT_REGS_PARM4(ctx);\n  GET_PID_TGID;\n\n#if TCP_STATS_ON_PARENT\n#if TCP_EXISTING_HACK // Only need to do this if we are using the parent stats reporting and need the socket to exist\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n\n    sk_info = tcp_existing_hack(ctx, sk);\n    if (sk_info == NULL) {\n      return 0;\n    }\n  }\n#endif\n#endif\n\n  // Link us to our kretprobe\n  BEGIN_SAVE_ARGS(on_inet_csk_accept)\n  SAVE_ARG(sk)\n  SAVE_ARG(flags)\n  SAVE_ARG(err)\n  END_SAVE_ARGS(on_inet_csk_accept)\n\n  return 0;\n}\n\nSEC(\"kretprobe/inet_csk_accept\")\nint onret_inet_csk_accept(struct pt_regs *ctx)\n{\n  GET_PID_TGID;\n\n  // Ensure the accept succeeded\n  struct sock *newsk = (struct sock *)PT_REGS_RC(ctx);\n  if (!newsk) {\n#if TRACE_TCP_SOCKETS\n    DEBUG_PRINTK(\"onret_inet_csk_accept: accept failed, tgid=%u\\n\", _tgid);\n#endif\n    // Unlink the kprobe/kretprobe\n    DELETE_ARGS(on_inet_csk_accept);\n    return 0;\n  }\n\n  // filter out non-tcp connections\n  u16 family = 0;\n  bpf_probe_read(&family, sizeof(family), &newsk->sk_family);\n  if (family != AF_INET && family != AF_INET6) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"onret_inet_csk_accept: family is not ipv4 or ipv6 sk=%llx\\n\", newsk);\n#endif\n    bpf_log(ctx, BPF_LOG_UNEXPECTED_TYPE, (u64)newsk, (u64)family, 0);\n    DELETE_ARGS(on_inet_csk_accept);\n    return 0;\n  }\n\n  // Link us from our kprobe\n  GET_ARGS(on_inet_csk_accept, args);\n  if (args == NULL) {\n    // Race condition where we might have been inside an accept when the probe\n    // was inserted, ignore\n    return 0;\n  }\n\n  // Set up the child socket\n  u64 now = get_timestamp();\n\n#if TCP_LIFETIME_HACK\n  // remove the entry from our hash map if it's there\n  tcp_lifetime_hack(ctx, newsk);\n#endif\n\n#if TCP_STATS_ON_PARENT\n  int ret = add_tcp_open_socket(ctx, newsk, _tgid, now, args->sk);\n#else\n  int ret = add_tcp_open_socket(ctx, newsk, _tgid, now, NULL);\n#endif\n  if (ret == 1) {\n    // Socket doesn't exist yet, create it in our table\n    perf_submit_agent_internal__new_sock_created(ctx, now, _tgid, (__u64)newsk);\n  } else if (ret == 0) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"onret_inet_csk_accept: add_tcp_open_socket of existing socket: %llx (tgid=%u)\\n\", newsk, _tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, _tgid, (u64)newsk);\n    DELETE_ARGS(on_inet_csk_accept);\n    return 0;\n  } else {\n    // no log here because we already had another log inside add_tcp_open_socket for this\n    DELETE_ARGS(on_inet_csk_accept);\n    return 0;\n  }\n\n  // Set the state\n  u16 dport = 0;\n  u16 sport = 0;\n  bpf_probe_read(&dport, sizeof(dport), &newsk->sk_dport);\n  bpf_probe_read(&sport, sizeof(sport), &newsk->sk_num);\n\n  if (family == AF_INET) {\n    u32 daddr = 0;\n    u32 saddr = 0;\n    bpf_probe_read_kernel(&daddr, sizeof(daddr), &newsk->sk_daddr);\n    bpf_probe_read_kernel(&saddr, sizeof(saddr), &newsk->sk_rcv_saddr);\n    perf_submit_agent_internal__set_state_ipv4(ctx, now, daddr, saddr, bpf_ntohs(dport), sport, (__u64)newsk, 2);\n  } else if (family == AF_INET6) {\n    uint8_t daddr[16] = {};\n    uint8_t saddr[16] = {};\n    bpf_probe_read(daddr, sizeof(daddr), (uint8_t *)(newsk->sk_v6_daddr.in6_u.u6_addr32));\n    bpf_probe_read(saddr, sizeof(saddr), (uint8_t *)(newsk->sk_v6_rcv_saddr.in6_u.u6_addr32));\n    perf_submit_agent_internal__set_state_ipv6(ctx, now, daddr, saddr, bpf_ntohs(dport), sport, (__u64)newsk, 2);\n  }\n\n#if TRACE_TCP_SOCKETS\n  DEBUG_PRINTK(\"on_inet_csk_accept exit: accepted socket %llx, tgid=%u\\n\", newsk, _tgid);\n#endif\n\n  DELETE_ARGS(on_inet_csk_accept);\n  return 0;\n}\n\n// existing\nstatic int tcp46_seq_show_impl(struct pt_regs *ctx, struct seq_file *seq, void *v)\n{\n  struct sock *sk = v;\n\n  u32 ino = BPF_CORE_READ(sk, sk_socket, file, f_inode, i_ino);\n\n  u32 *lookup_tgid = bpf_map_lookup_elem(&seen_inodes, &ino);\n  if (!lookup_tgid) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_tcp46_seq_show: ignoring socket %llx because of missing inode %u\\n\", sk, ino);\n#endif\n    return 0;\n  }\n  u32 tgid = *lookup_tgid;\n\n#if DEBUG_TCP_SOCKET_ERRORS\n  bpf_trace_printk(\"on_tcp46_seq_show: creating socket %llx with inode %u\\n\", sk, ino);\n#endif\n\n  /* we don't want to explore this inode again */\n  bpf_map_delete_elem(&seen_inodes, &ino);\n\n  int ret = ensure_tcp_existing(ctx, sk, tgid);\n  if (ret == 0) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_tcp46_seq_show: add_tcp_open_socket of existing socket: %llx (tgid=%u)\\n\", sk, tgid);\n    bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, tgid, (u64)sk);\n#endif\n  } else if (ret < 0) {\n#if DEBUG_TCP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_tcp46_seq_show: add_tcp_open_socket failed: %llx (tgid=%u)\\n\", sk, tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_OPEN_SOCKETS, tgid, abs_val(ret));\n  }\n\n  return 0;\n}\n\nSEC(\"kprobe/tcp4_seq_show\")\nint on_tcp4_seq_show(struct pt_regs *ctx)\n{\n  struct seq_file *seq = (struct seq_file *)PT_REGS_PARM1(ctx);\n  void *v = (void *)PT_REGS_PARM2(ctx);\n  return tcp46_seq_show_impl(ctx, seq, v);\n}\n\nSEC(\"kprobe/tcp6_seq_show\")\nint on_tcp6_seq_show(struct pt_regs *ctx)\n{\n  struct seq_file *seq = (struct seq_file *)PT_REGS_PARM1(ctx);\n  void *v = (void *)PT_REGS_PARM2(ctx);\n  return tcp46_seq_show_impl(ctx, seq, v);\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* UDP SOCKETS */\n\n// add udp_open_socket\n//\n// should be paired with a `perf_submit_agent_internal__udp_new_socket`\n// in most circumstances, however we don't do this in the case of already\n// existing sockets.\n//\n// returns 1 if it added the socket, 0 if it already existed,\n// and -1 if the table is full, broken, or out of memory\n// this operation is atomic and not susceptible to race conditions, because\n// map.insert is atomic\n\nstatic __always_inline int add_udp_open_socket(struct pt_regs *ctx, struct sock *sk, u32 tgid, u64 now)\n{\n#if TRACE_UDP_SOCKETS\n  bpf_trace_printk(\"add_udp_open_socket: %llx (tgid=%u, cpu=%u)\\n\", sk, tgid, bpf_get_smp_processor_id());\n#endif\n\n  struct udp_open_socket_t sk_info = {\n      .tgid = tgid,\n  };\n  int ret = bpf_map_update_elem(&udp_open_sockets, &sk, &sk_info, BPF_NOEXIST);\n  if (ret != 0) {\n    // Check if key already exists to distinguish duplicate vs table full\n    void *existing = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n    if (existing) {\n      return 0;\n    } else {\n#if DEBUG_UDP_SOCKET_ERRORS\n      bpf_trace_printk(\"add_udp_open_socket: udp_open_sockets table is full, dropping socket sk=%llx (tgid=%u)\\n\", sk, tgid);\n      bpf_log(ctx, BPF_LOG_TABLE_FULL, BPF_TABLE_UDP_OPEN_SOCKETS, (u64)tgid, (u64)sk);\n#endif\n      return -1;\n    }\n  }\n  return 1;\n}\n\nstatic __always_inline int ensure_udp_existing(struct pt_regs *ctx, struct sock *sk, u32 tgid)\n{\n  u16 family = BPF_CORE_READ(sk, sk_family);\n\n  if (family != AF_INET && family != AF_INET6) {\n    return -1;\n  }\n\n  u64 now = get_timestamp();\n\n  int ret = add_udp_open_socket(ctx, sk, tgid, now);\n  if (ret == 1) {\n    struct in6_addr addr;\n\n    if (family == AF_INET6) {\n      addr = BPF_CORE_READ(sk, sk_v6_rcv_saddr);\n    } else {\n      addr.s6_addr32[0] = addr.s6_addr32[1] = 0;\n      addr.s6_addr16[4] = 0;\n      addr.s6_addr16[5] = 0xffff;\n      addr.s6_addr32[3] = BPF_CORE_READ(sk, sk_rcv_saddr);\n    }\n\n    u16 lport = 0;\n    bpf_probe_read(&lport, sizeof(lport), &(inet_sk(sk)->inet_num));\n    perf_submit_agent_internal__udp_new_socket(ctx, now, tgid, (__u64)sk, (uint8_t *)(&addr), lport);\n  }\n  return ret;\n}\n\n//\n// HACK: Add tcp sockets that should exist to the table but don't\n//\n#if UDP_EXISTING_HACK\nstatic __always_inline struct udp_open_socket_t *udp_existing_hack(struct pt_regs *ctx, struct sock *sk)\n{\n  // Can only do this hack if we are in a userland process context\n  PID_TGID _pid_tgid = bpf_get_current_pid_tgid();\n  TGID _tgid = TGID_FROM_PID_TGID(_pid_tgid);\n  PID _pid = PID_FROM_PID_TGID(_pid_tgid);\n  if (_tgid == 0 || _pid == 0) {\n    return NULL;\n  }\n\n  // Ensure the udp socket exists\n  int ret = ensure_udp_existing(ctx, sk, _tgid);\n#if DEBUG_UDP_SOCKET_ERRORS\n  if (ret == 1) {\n    bpf_trace_printk(\n        \"udp_existing_hack: add_udp_open_socket of missing socket: %llx (tgid=%u, cpu=%u)\\n\",\n        sk,\n        _tgid,\n        bpf_get_smp_processor_id());\n    bpf_log(ctx, BPF_LOG_EXISTING_HACK, BPF_TABLE_UDP_OPEN_SOCKETS, (u64)sk, 0);\n  } else if (ret == 0) {\n    // already existed, okay\n  } else if (ret < 0) {\n    bpf_trace_printk(\"udp_existing_hack: add_udp_open_socket failed: %llx (tgid=%u)\\n\", sk, _tgid);\n  }\n#endif\n  struct udp_open_socket_t *sk_info_p = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n  return sk_info_p;\n}\n#endif\n\nstatic __always_inline void\nudp_send_stats_if_nonempty(struct pt_regs *ctx, u64 now, struct sock *sk, struct udp_stats_t *stats, u8 is_rx)\n{\n  /* is no data to send, return */\n  u32 sk_drops_counter = BPF_CORE_READ(sk, sk_drops.counter);\n  if (!stats->addr_changed && stats->packets == 0 && sk_drops_counter == stats->drops)\n    return;\n\n  // bpf_trace_printk(\"is_rx: %d   sk->sk_drops.counter: %d   stats->drops:\n  // %d\\n\", is_rx, sk->sk_drops.counter, stats->drops);\n\n  /* there is data, send a report */\n  /* only send drops on receive side, since udp sends never drop packets */\n  perf_submit_agent_internal__udp_stats(\n      ctx,\n      now,\n      (__u64)sk,\n      (uint8_t *)(stats->raddr6),\n      stats->packets,\n      stats->bytes,\n      stats->addr_changed,\n      stats->rport,\n      is_rx,\n      (uint8_t *)(stats->laddr6),\n      stats->lport,\n      is_rx ? (sk_drops_counter - stats->drops) : 0);\n}\n\nstatic void remove_udp_open_socket(struct pt_regs *ctx, struct sock *sk)\n{\n  struct udp_open_socket_t *sk_info_p;\n  sk_info_p = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n  if (!sk_info_p) {\n    return;\n  }\n\n#if TRACE_UDP_SOCKETS\n  bpf_trace_printk(\"remove_udp_open_socket: %llx (cpu=%u)\\n\", sk, bpf_get_smp_processor_id());\n#endif\n\n  // The lookup was successful.  Make a copy before deleting the entry, and only send related telemetry if the delete is\n  // successful.\n  struct udp_open_socket_t sk_info = *sk_info_p;\n\n  int ret = bpf_map_delete_elem(&udp_open_sockets, &sk);\n  if (ret != 0) {\n    // Another thread must have already deleted the entry for this sk.\n    return;\n  }\n\n  u64 now = get_timestamp();\n  udp_send_stats_if_nonempty(ctx, now, sk, &sk_info.stats[0], 0);\n  udp_send_stats_if_nonempty(ctx, now, sk, &sk_info.stats[1], 1);\n\n  perf_submit_agent_internal__udp_destroy_socket(ctx, now, (__u64)sk);\n}\n\n#if UDP_LIFETIME_HACK\n//\n// HACK: Remove open socket from table if it exists\n//\nstatic void udp_lifetime_hack(struct pt_regs *ctx, struct sock *sk)\n{\n  struct udp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n  if (sk_info) {\n#if DEBUG_UDP_SOCKET_ERRORS\n    bpf_trace_printk(\"udp_lifetime_hack: udp_lifetime_hack sk=%llx cpu=%u\\n\", sk, bpf_get_smp_processor_id());\n    bpf_log(ctx, BPF_LOG_LIFETIME_HACK, BPF_TABLE_UDP_OPEN_SOCKETS, (u64)sk, 0);\n#endif\n    remove_udp_open_socket(ctx, sk);\n  }\n}\n#endif\n\n// EXISTING\nstatic int udp46_seq_show_impl(struct pt_regs *ctx, struct seq_file *seq, void *v)\n{\n  struct sock *sk = v;\n\n  u32 ino = BPF_CORE_READ(sk, sk_socket, file, f_inode, i_ino);\n  u32 *lookup_tgid = bpf_map_lookup_elem(&seen_inodes, &ino);\n  if (!lookup_tgid) {\n#if DEBUG_UDP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_udp46_seq_show: ignoring socket %llx because of missing inode %u\\n\", sk, ino);\n#endif\n    return 0;\n  }\n  u32 tgid = *lookup_tgid;\n\n  /* we don't want to explore this inode again */\n  bpf_map_delete_elem(&seen_inodes, &ino);\n\n  int ret = ensure_udp_existing(ctx, sk, tgid);\n  if (ret == 0) {\n#if DEBUG_UDP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_udp46_seq_show: add_udp_open_socket of existing socket: %llx (tgid=%u)\\n\", sk, tgid);\n    bpf_log(ctx, BPF_LOG_TABLE_DUPLICATE_INSERT, BPF_TABLE_UDP_OPEN_SOCKETS, tgid, (u64)sk);\n#endif\n  } else if (ret < 0) {\n#if DEBUG_UDP_SOCKET_ERRORS\n    bpf_trace_printk(\"on_udp46_seq_show: add_udp_open_socket failed: %llx (tgid=%u)\\n\", sk, tgid);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_UDP_OPEN_SOCKETS, tgid, abs_val(ret));\n  }\n\n  return 0;\n}\n\nSEC(\"kprobe/udp4_seq_show\")\nint on_udp4_seq_show(struct pt_regs *ctx)\n{\n  struct seq_file *seq = (struct seq_file *)PT_REGS_PARM1(ctx);\n  void *v = (void *)PT_REGS_PARM2(ctx);\n  return udp46_seq_show_impl(ctx, seq, v);\n}\n\nSEC(\"kprobe/udp6_seq_show\")\nint on_udp6_seq_show(struct pt_regs *ctx)\n{\n  struct seq_file *seq = (struct seq_file *)PT_REGS_PARM1(ctx);\n  void *v = (void *)PT_REGS_PARM2(ctx);\n  return udp46_seq_show_impl(ctx, seq, v);\n}\n\n// NEW\nstatic int udp_v46_get_port_impl(struct pt_regs *ctx, struct sock *sk)\n{\n  GET_PID_TGID;\n\n  int ret = bpf_map_update_elem(&udp_get_port_hash, &_cpu, &sk, BPF_NOEXIST);\n  if (ret != 0) {\n    // Check if key already exists to distinguish duplicate vs table full\n    void *existing = bpf_map_lookup_elem(&udp_get_port_hash, &_cpu);\n    if (existing) {\n#if DEBUG_OTHER_MAP_ERRORS\n      bpf_trace_printk(\"on_udp_v46_get_port: should not see existing hash entry (tgid=%u, cpu=%u)\\n\", _tgid, _cpu);\n#endif\n      return 0;\n    } else {\n#if DEBUG_OTHER_MAP_ERRORS\n      bpf_trace_printk(\"on_udp_v46_get_port: udp_get_port_hash table is full, dropping call (tgid=%u, cpu=%u)\\n\", _tgid, _cpu);\n#endif\n      return 0;\n    }\n  }\n  return 0;\n}\n\nSEC(\"kprobe/udp_v4_get_port\")\nint on_udp_v4_get_port(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  return udp_v46_get_port_impl(ctx, sk);\n}\n\nSEC(\"kprobe/udp_v6_get_port\")\nint on_udp_v6_get_port(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  return udp_v46_get_port_impl(ctx, sk);\n}\n\nstatic int onret_udp_get_port_impl(struct pt_regs *ctx)\n{\n  struct sock **found = NULL;\n  struct sock *sk = NULL;\n  GET_PID_TGID;\n  int retval = (int)PT_REGS_RC(ctx);\n\n  found = bpf_map_lookup_elem(&udp_get_port_hash, &_cpu);\n  if (!found)\n    return 0;\n  sk = *found;\n\n  /**\n   * on success, udp_lib_get_port sets inet_sk(sk)->inet_num.\n   *\n   * get_port is called from:\n   *   - inet_autobind; does not set inet->inet_rcv_saddr\n   *   - inet_bind, which sets inet->inet_rcv_saddr and inet->inet_saddr\n   *       just before calling get_port\n   *\n   */\n\n  if (retval == 0) {\n\n#if UDP_LIFETIME_HACK\n    // remove the entry from our hash map if it's there\n    udp_lifetime_hack(ctx, sk);\n#endif\n\n    int ret = ensure_udp_existing(ctx, sk, _tgid);\n#if DEBUG_UDP_SOCKET_ERRORS\n    if (ret == 0) {\n      bpf_trace_printk(\n          \"common_ret_udp_v46_get_port: add_udp_open_socket of existing socket: %llx (tgid=%u, cpu=%u)\\n\", sk, _tgid, _cpu);\n    } else if (ret < 0) {\n      bpf_trace_printk(\"common_ret_udp_v46_get_port: add_udp_open_socket failed: %llx (tgid=%u, cpu=%u)\\n\", sk, _tgid, _cpu);\n    }\n#endif\n  }\n\n  bpf_map_delete_elem(&udp_get_port_hash, &_cpu);\n\n  return 0;\n}\n\nSEC(\"kretprobe/udp_v4_get_port\")\nint onret_udp_v4_get_port(struct pt_regs *ctx)\n{\n  return onret_udp_get_port_impl(ctx);\n}\n\nSEC(\"kretprobe/udp_v6_get_port\")\nint onret_udp_v6_get_port(struct pt_regs *ctx)\n{\n  return onret_udp_get_port_impl(ctx);\n}\n\n/*\n * socket lifetime end\n * Note:  This function is called from on_security_sk_free() and on_inet_release().  Historically, only the security_sk_free()\n * kprobe was used, but there were some unexplained missing socket close events that seemed to be due to security_sk_free()\n * probes not being triggered from certain kernel thread states/contexts.  The inet_release() kprobe was added to make sure all\n * socket close events were captured.  inet_release() calls security_sk_free(), synchronously in some cases and asynchronously\n * via call_rcu() in other cases. Asynchronous calls can result in multiple threads executing this and subsequent functions\n * concurrently, so they need to deal with tcp/udp_open_sockets map lookup and delete failures appropriately to make sure only\n * one of the calls sends the final socket telemetry. (i.e. If a lookup or delete fails, assume that the other thread already\n * successfully deleted the entry, and only send the final telemetry if the delete is successful.)\n */\nstatic inline void remove_open_socket(struct pt_regs *ctx, struct sock *sk)\n{\n  {\n    // TCP(v4/v6) Socket?\n    struct tcp_open_socket_t *tcp_sk_info;\n    tcp_sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n    if (tcp_sk_info) {\n      remove_tcp_open_socket(ctx, sk);\n      return;\n    }\n  }\n  {\n    // UDP(v4/v6) Socket?\n    struct udp_open_socket_t *udp_sk_info;\n    udp_sk_info = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n    if (udp_sk_info) {\n      remove_udp_open_socket(ctx, sk);\n      return;\n    }\n  }\n  // Must be some other kind of socket we don't yet care about\n}\n\n// --- security_sk_free ----------------------------------------------------\n// This is where final socket destruction happens for all socket types\nSEC(\"kprobe/security_sk_free\")\nint on_security_sk_free(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  remove_open_socket(ctx, sk);\n  return 0;\n}\n\nBEGIN_DECLARE_SAVED_ARGS(inet_release)\nstruct sock *sk;\nEND_DECLARE_SAVED_ARGS(inet_release)\n\nSEC(\"kprobe/inet_release\")\nint on_inet_release(struct pt_regs *ctx)\n{\n  struct socket *sock = (struct socket *)PT_REGS_PARM1(ctx);\n  GET_PID_TGID;\n\n  struct sock *sk = NULL;\n  bpf_probe_read(&sk, sizeof(sk), &sock->sk);\n\n  if (!sk) {\n    return 0;\n  }\n\n  BEGIN_SAVE_ARGS(inet_release)\n  SAVE_ARG(sk)\n  END_SAVE_ARGS(inet_release)\n\n  return 0;\n}\n\nSEC(\"kretprobe/inet_release\")\nint onret_inet_release(struct pt_regs *ctx)\n{\n  GET_PID_TGID;\n\n  GET_ARGS_MISSING_OK(inet_release, args);\n  if (args == NULL) {\n    return 0;\n  }\n\n  struct sock *sk = args->sk;\n\n  DELETE_ARGS(inet_release);\n\n  if (sk) {\n    remove_open_socket(ctx, sk);\n  }\n\n  return 0;\n}\n\n// TCP reset\nstatic void handle_tcp_reset(struct pt_regs *ctx, struct sock *sk, u8 is_rx)\n{\n  if (!bpf_map_lookup_elem(&tcp_open_sockets, &sk)) {\n    // don't send an event on sockets we never notified userspace about\n    // bpf_trace_printk(\"handle_tcp_reset: no socket\\n\");\n    return;\n  }\n\n  // bpf_trace_printk(\"handle_tcp_reset: perf_submit_agent_internal__tcp_reset\\n\");\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__tcp_reset(ctx, now, (__u64)sk, is_rx);\n}\n\n// receive TCP RST\nSEC(\"kprobe/tcp_reset\")\nint on_tcp_reset(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  // bpf_trace_printk(\"on_tcp_reset\\n\");\n  // receive RST (is_rx = 1)\n  handle_tcp_reset(ctx, sk, 1);\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* LIVE UDP */\n\n// laddr, raddr is in big endian format, lport and rport are in little endian\n// format\nstatic __always_inline void udp_update_stats(\n    struct pt_regs *ctx,\n    struct sock *sk,\n    struct sk_buff *skb,\n    struct in6_addr *laddr,\n    u16 lport,\n    struct in6_addr *raddr,\n    u16 rport,\n    u8 is_rx)\n{\n  u16 family = BPF_CORE_READ(sk, sk_family);\n  struct udp_open_socket_t *sk_info_p;\n  struct udp_stats_t *stats;\n\n  sk_info_p = bpf_map_lookup_elem(&udp_open_sockets, &sk);\n  if (sk_info_p == NULL) {\n#if UDP_EXISTING_HACK\n\n    sk_info_p = udp_existing_hack(ctx, sk);\n    if (sk_info_p == NULL) {\n      return;\n    }\n\n#else\n\n    bpf_log(ctx, BPF_LOG_TABLE_MISSING_KEY, BPF_TABLE_UDP_OPEN_SOCKETS, (u64)sk, 0);\n    return;\n\n#endif\n  }\n\n  stats = &sk_info_p->stats[is_rx];\n\n  /* compare sk_info_p's address and the one in fl4 */\n  u32 lchanged = (stats->laddr6[0] ^ laddr->s6_addr32[0]) | (stats->laddr6[1] ^ laddr->s6_addr32[1]) |\n                 (stats->laddr6[2] ^ laddr->s6_addr32[2]) | (stats->laddr6[3] ^ laddr->s6_addr32[3]) | (stats->lport ^ lport);\n  u32 rchanged = (stats->raddr6[0] ^ raddr->s6_addr32[0]) | (stats->raddr6[1] ^ raddr->s6_addr32[1]) |\n                 (stats->raddr6[2] ^ raddr->s6_addr32[2]) | (stats->raddr6[3] ^ raddr->s6_addr32[3]) | (stats->rport ^ rport);\n  u32 sk_drops_counter = BPF_CORE_READ(sk, sk_drops.counter);\n  u32 dchanged = is_rx ? (sk_drops_counter != stats->drops) : 0;\n  u32 changed = lchanged | rchanged | dchanged;\n\n  u64 now = get_timestamp();\n\n  if (changed || ((now - stats->last_output) >= filter_ns)) {\n\n    /* set the address */\n    if (lchanged) {\n      stats->laddr6[0] = laddr->s6_addr32[0];\n      stats->laddr6[1] = laddr->s6_addr32[1];\n      stats->laddr6[2] = laddr->s6_addr32[2];\n      stats->laddr6[3] = laddr->s6_addr32[3];\n      stats->lport = lport;\n    }\n    if (rchanged) {\n      stats->raddr6[0] = raddr->s6_addr32[0];\n      stats->raddr6[1] = raddr->s6_addr32[1];\n      stats->raddr6[2] = raddr->s6_addr32[2];\n      stats->raddr6[3] = raddr->s6_addr32[3];\n      stats->rport = rport;\n    }\n    stats->addr_changed = (lchanged || rchanged) ? (u8)family : 0;\n\n    /* send the update if anything has changed, or if we have statistics */\n    udp_send_stats_if_nonempty(ctx, now, sk, stats, is_rx);\n\n    /* reset statistics */\n    stats->packets = 1;\n    stats->drops = is_rx ? sk_drops_counter : 0;\n    stats->bytes = BPF_CORE_READ(skb, len);\n\n    /* schedule next update */\n    stats->last_output = now;\n\n    return;\n  }\n\n  /* address is the same and too early to send a notification, just update */\n  stats->packets++;\n  stats->bytes += BPF_CORE_READ(skb, len);\n\n  /** don't update 'drops' here, because it's not cumulative,\n   * it's a total since last reset\n   */\n\n  return;\n}\n\n/* Tail calls used by kprobes below, so we can have enough stack space */\nstruct {\n  __uint(type, BPF_MAP_TYPE_PROG_ARRAY);\n  __uint(max_entries, NUM_TAIL_CALLS);\n  __uint(key_size, sizeof(__u32));\n  __uint(value_size, sizeof(__u32));\n} tail_calls SEC(\".maps\");\n\nSEC(\"kprobe\")\nint on_udp_send_skb__2(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  struct flowi4 *fl4 = (struct flowi4 *)PT_REGS_PARM2(ctx);\n\n  if (!skb || !fl4)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n\n  perf_check_and_submit_dns(ctx, sk, skb, IPPROTO_UDP, BPF_CORE_READ(fl4, fl4_sport), BPF_CORE_READ(fl4, fl4_dport), 0);\n  return 0;\n}\n\nSEC(\"kprobe\")\nint on_udp_v6_send_skb__2(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  struct flowi6 *fl6 = (struct flowi6 *)PT_REGS_PARM2(ctx);\n\n  if (!skb || !fl6)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n  u16 sport = BPF_CORE_READ(fl6, fl6_sport);\n  u16 dport = BPF_CORE_READ(fl6, fl6_dport);\n\n  perf_check_and_submit_dns(ctx, sk, skb, IPPROTO_UDP, sport, dport, 0);\n  return 0;\n}\n\nSEC(\"kprobe\")\nint on_ip_send_skb__2(struct pt_regs *ctx)\n{\n  struct net *net = (struct net *)PT_REGS_PARM1(ctx);\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);\n\n  if (!skb)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n  u16 network_header = BPF_CORE_READ(skb, network_header);\n  u16 transport_header = BPF_CORE_READ(skb, transport_header);\n  unsigned char *skb_head = BPF_CORE_READ(skb, head);\n\n  struct iphdr *ip_hdr = (struct iphdr *)(skb_head + network_header);\n  u8 protocol = BPF_CORE_READ(ip_hdr, protocol);\n\n  if (protocol == IPPROTO_UDP) {\n    struct udphdr *udp_hdr = (struct udphdr *)(skb_head + transport_header);\n    u16 source = BPF_CORE_READ(udp_hdr, source);\n    u16 dest = BPF_CORE_READ(udp_hdr, dest);\n\n    perf_check_and_submit_dns(ctx, sk, skb, IPPROTO_UDP, source, dest, 0);\n  }\n\n  return 0;\n}\n\nSEC(\"kprobe\")\nint on_ip6_send_skb__2(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n\n  if (!skb)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n  unsigned char *skb_head = BPF_CORE_READ(skb, head);\n\n  struct ipv6hdr *ipv6_hdr = (struct ipv6hdr *)(skb_head + BPF_CORE_READ(skb, network_header));\n\n  if (BPF_CORE_READ(ipv6_hdr, nexthdr) == IPPROTO_UDP) {\n    struct udphdr *udp_hdr = (struct udphdr *)(skb_head + BPF_CORE_READ(skb, transport_header));\n\n    perf_check_and_submit_dns(ctx, sk, skb, IPPROTO_UDP, BPF_CORE_READ(udp_hdr, source), BPF_CORE_READ(udp_hdr, dest), 0);\n  }\n\n  return 0;\n}\n\n/* udp kprobes, reference the tail calls above */\n\n// may be optimized out on some kernels, if hook fails, we will use\n// on_ip_send_skb\nSEC(\"kprobe/udp_send_skb\")\nint on_udp_send_skb(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  struct flowi4 *fl4 = (struct flowi4 *)PT_REGS_PARM2(ctx);\n\n  if (!skb || !fl4)\n    return 0;\n\n  struct in6_addr laddr = make_ipv6_address(BPF_CORE_READ(fl4, saddr));\n  struct in6_addr raddr = make_ipv6_address(BPF_CORE_READ(fl4, daddr));\n\n  struct sock *sk_ptr = BPF_CORE_READ(skb, sk);\n  if (!sk_ptr)\n    return 0;\n\n  udp_update_stats(ctx, sk_ptr, skb, &laddr, BPF_CORE_READ(fl4, fl4_sport), &raddr, BPF_CORE_READ(fl4, fl4_dport), 0);\n\n  // Call on_udp_send_skb__2\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_ON_UDP_SEND_SKB__2);\n\n  return 0;\n}\n\n// may be optimized out on some kernels, if hook fails, we will use\n// on_ip6_send_skb\nSEC(\"kprobe/udp_v6_send_skb\")\nint on_udp_v6_send_skb(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  struct flowi6 *fl6 = (struct flowi6 *)PT_REGS_PARM2(ctx);\n\n  if (!skb || !fl6)\n    return 0;\n\n  GET_PID_TGID;\n\n  struct in6_addr laddr = BPF_CORE_READ(fl6, saddr);\n  struct in6_addr raddr = BPF_CORE_READ(fl6, daddr);\n  __be16 sport = BPF_CORE_READ(fl6, fl6_sport);\n  __be16 dport = BPF_CORE_READ(fl6, fl6_dport);\n\n#if DEBUG_UDP_SOCKET_ERRORS\n  __check_broken_in6_addr(&laddr, __LINE__);\n  __check_broken_in6_addr(&raddr, __LINE__);\n#endif\n\n  struct sock *sk_ptr = BPF_CORE_READ(skb, sk);\n  if (!sk_ptr)\n    return 0;\n\n  udp_update_stats(ctx, sk_ptr, skb, &laddr, sport, &raddr, dport, 0);\n\n  // Call on_udp_v6_send_skb__2\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_ON_UDP_V6_SEND_SKB__2);\n\n  return 0;\n}\n\n// send TCP RST\nSEC(\"kprobe/tcp_send_active_reset\")\nint on_tcp_send_active_reset(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  // gfp_t priority = (gfp_t)PT_REGS_PARM2(ctx);   -- unused\n\n  // bpf_trace_printk(\"on_tcp_send_active_reset\\n\");\n  // send RST (is_rx = 0)\n  handle_tcp_reset(ctx, sk, 0);\n  return 0;\n}\n\nSEC(\"kprobe/ip_send_skb\")\nint on_ip_send_skb(struct pt_regs *ctx)\n{\n  // struct net *net = (struct net *)PT_REGS_PARM1(ctx);   -- unused\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);\n\n  if (!skb)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n  unsigned char *head = BPF_CORE_READ(skb, head);\n\n  struct iphdr *ip_hdr = (struct iphdr *)(head + BPF_CORE_READ(skb, network_header));\n  __u8 protocol = BPF_CORE_READ(ip_hdr, protocol);\n\n  if (protocol == IPPROTO_UDP) {\n    struct udphdr *udp_hdr = (struct udphdr *)(head + BPF_CORE_READ(skb, transport_header));\n\n    struct in6_addr laddr = make_ipv6_address(BPF_CORE_READ(ip_hdr, saddr));\n    struct in6_addr raddr = make_ipv6_address(BPF_CORE_READ(ip_hdr, daddr));\n\n    udp_update_stats(ctx, sk, skb, &laddr, BPF_CORE_READ(udp_hdr, source), &raddr, BPF_CORE_READ(udp_hdr, dest), 0);\n  }\n\n  if (protocol == IPPROTO_TCP) {\n    struct tcphdr *tcp_hdr = (struct tcphdr *)(head + BPF_CORE_READ(skb, transport_header));\n\n    u16 flags = 0;\n    bpf_probe_read(&flags, 2, ((u8 *)tcp_hdr) + 12);\n\n    if (flags & TCP_FLAG_RST) {\n      // bpf_trace_printk(\"on_ip_send_skb: tcp rst is set\\n\");\n      // send RST (is_rx = 0)\n      handle_tcp_reset(ctx, sk, 0);\n    }\n  }\n\n  // Call on_ip_send_skb__2\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_ON_IP_SEND_SKB__2);\n\n  return 0;\n}\n\nSEC(\"kprobe/ip6_send_skb\")\nint on_ip6_send_skb(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n\n  if (!skb)\n    return 0;\n\n  struct sock *sk = BPF_CORE_READ(skb, sk);\n  unsigned char *head = BPF_CORE_READ(skb, head);\n  __u16 network_header = BPF_CORE_READ(skb, network_header);\n  __u16 transport_header = BPF_CORE_READ(skb, transport_header);\n\n  struct ipv6hdr *ipv6_hdr = (struct ipv6hdr *)(head + network_header);\n  __u8 nexthdr = BPF_CORE_READ(ipv6_hdr, nexthdr);\n\n  GET_PID_TGID;\n\n  if (nexthdr == IPPROTO_UDP) {\n    struct udphdr *udp_hdr = (struct udphdr *)(head + transport_header);\n\n    struct in6_addr laddr = BPF_CORE_READ(ipv6_hdr, saddr);\n    struct in6_addr raddr = BPF_CORE_READ(ipv6_hdr, daddr);\n    __be16 source = BPF_CORE_READ(udp_hdr, source);\n    __be16 dest = BPF_CORE_READ(udp_hdr, dest);\n\n#if DEBUG_UDP_SOCKET_ERRORS\n    __check_broken_in6_addr(&laddr, __LINE__);\n    __check_broken_in6_addr(&raddr, __LINE__);\n#endif\n\n    udp_update_stats(ctx, sk, skb, &laddr, source, &raddr, dest, 0);\n  }\n\n  if (nexthdr == IPPROTO_TCP) {\n    struct tcphdr *tcp_hdr = (struct tcphdr *)(head + transport_header);\n\n    u16 flags = 0;\n    bpf_probe_read(&flags, 2, ((u8 *)tcp_hdr) + 12);\n\n    if (flags & TCP_FLAG_RST) {\n      // bpf_trace_printk(\"on_ip6_send_skb: tcp rst is set\\n\");\n      // send RST (is_rx = 0)\n      handle_tcp_reset(ctx, sk, 0);\n    }\n  }\n\n  // Call on_ip6_send_skb__2\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_ON_IP6_SEND_SKB__2);\n\n  return 0;\n}\n\n// Common handler -tail call- for receiving udp skb's\n// step one, update stats, make sure udp socket exists\nSEC(\"kprobe\")\nint handle_receive_udp_skb(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);\n\n  if (!sk || !skb)\n    return 0;\n\n  // find offsets for ip and udp headers\n  unsigned char *skb_head = BPF_CORE_READ(skb, head);\n  __u16 transport_header = BPF_CORE_READ(skb, transport_header);\n  __u16 network_header = BPF_CORE_READ(skb, network_header);\n\n  // get the ip header\n  struct iphdr *ip_hdr = (struct iphdr *)(skb_head + network_header);\n  struct udphdr *udp_hdr = (struct udphdr *)(skb_head + transport_header);\n\n  // get the version from the ip packet (common location for ipv4 and ipv6)\n  u8 version;\n  bpf_probe_read(&version, 1, (const u8 *)ip_hdr);\n  version &= 0xF0;\n\n  // Parse the addresses out of the header\n  struct in6_addr laddr, raddr;\n  if (version == 0x40) {\n    // IPv4\n    __be32 daddr = BPF_CORE_READ(ip_hdr, daddr);\n    __be32 saddr = BPF_CORE_READ(ip_hdr, saddr);\n    laddr = make_ipv6_address(daddr);\n    raddr = make_ipv6_address(saddr);\n  } else if (version & 0x60) {\n    // IPV6\n    laddr = BPF_CORE_READ((struct ipv6hdr *)ip_hdr, daddr);\n    raddr = BPF_CORE_READ((struct ipv6hdr *)ip_hdr, saddr);\n  } else {\n    // Unknown IP Protocol version, possibly malformed packet received?\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, (u64)version, 0, 0);\n    return 0;\n  }\n\n#if DEBUG_UDP_SOCKET_ERRORS\n  if (__check_broken_in6_addr(&laddr, __LINE__) || __check_broken_in6_addr(&raddr, __LINE__)) {\n    u16 family = BPF_CORE_READ(sk, sk_family);\n    bpf_trace_printk(\"sk_family = %d, version = %u\\n\", family, (unsigned int)version);\n    stack_trace(ctx);\n  }\n#endif\n\n  __be16 dest = BPF_CORE_READ(udp_hdr, dest);\n  __be16 source = BPF_CORE_READ(udp_hdr, source);\n  udp_update_stats(ctx, sk, skb, &laddr, dest, &raddr, source, 1);\n\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_HANDLE_RECEIVE_UDP_SKB__2);\n  return 0;\n}\n\n// Common handler -tail call- for receiving udp skb's\n// step two, check for receiving dns packets\nSEC(\"kprobe\")\nint handle_receive_udp_skb__2(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);\n\n  if (!sk || !skb)\n    return 0;\n\n  GET_PID_TGID;\n  unsigned char *skb_head = BPF_CORE_READ(skb, head);\n  __u16 transport_header = BPF_CORE_READ(skb, transport_header);\n  const struct udphdr *hdr = (const struct udphdr *)(skb_head + transport_header);\n  __be16 source = BPF_CORE_READ(hdr, source);\n  __be16 dest = BPF_CORE_READ(hdr, dest);\n  perf_check_and_submit_dns(ctx, sk, skb, IPPROTO_UDP, source, dest, 1);\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* LIVE TCP */\nstatic void report_rtt_estimator_if_time(struct pt_regs *ctx, struct sock *sk, struct tcp_open_socket_t *sk_info)\n{\n  u64 now = get_timestamp();\n\n  if ((now - sk_info->last_output) < filter_ns)\n    return; // too little time passed\n\n  report_rtt_estimator(ctx, sk, sk_info, now, false);\n}\n\nSEC(\"kprobe/tcp_rtt_estimator\")\nint on_tcp_rtt_estimator(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct tcp_open_socket_t *sk_info; /* for filtering */\n\n  if (BPF_CORE_READ(sk, sk_state) != TCP_ESTABLISHED) {\n    return 0;\n  }\n\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n    // Okay if socket is not yet tracked because it could be in kernel accept\n    // queue but not returned by inet_csk_accept yet.\n    return 0;\n  }\n\n  report_rtt_estimator_if_time(ctx, sk, sk_info);\n\n  return 0;\n}\n\nSEC(\"kprobe/tcp_rcv_established\")\nint on_tcp_rcv_established(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n    // Okay if socket is not yet tracked because it could be in kernel accept\n    // queue but not returned by inet_csk_accept yet.\n    return 0;\n  }\n\n  int ret;\n  u64 bytes_received = 0;\n  ret = bpf_probe_read(&bytes_received, sizeof(bytes_received), &tcp_sk(sk)->bytes_received);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  if (bytes_received != sk_info->bytes_received) {\n    /* update statistic for next report */\n    sk_info->rcv_holes++;\n    sk_info->rcv_delivered++;\n\n    /* update the bytes_received so we won't report this occurrence again */\n    sk_info->bytes_received = bytes_received;\n\n    report_rtt_estimator_if_time(ctx, sk, sk_info);\n  }\n\n  return 0;\n}\n\nSEC(\"kprobe/tcp_event_data_recv\")\nint on_tcp_event_data_recv(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct tcp_open_socket_t *sk_info;\n  sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n  if (!sk_info) {\n    // Okay if socket is not yet tracked because it could be in kernel accept\n    // queue but not returned by inet_csk_accept yet.\n    return 0;\n  }\n\n  int ret;\n  u64 bytes_received = 0;\n  ret = bpf_probe_read(&bytes_received, sizeof(bytes_received), &tcp_sk(sk)->bytes_received);\n  if (ret != 0) {\n    bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, abs_val(ret), 0, 0);\n    return 0;\n  }\n\n  sk_info->rcv_delivered++;\n  sk_info->bytes_received = bytes_received;\n\n  report_rtt_estimator_if_time(ctx, sk, sk_info);\n\n  return 0;\n}\n\n// SYN timeouts.\nstatic void handle_syn_timeout(struct pt_regs *ctx, struct sock *sk)\n{\n  u8 state = BPF_CORE_READ(sk, sk_state);\n  // is this a SYN timeout?\n  if (((1 << state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) == 0) {\n    // no, return.\n    return;\n  }\n\n  if (!bpf_map_lookup_elem(&tcp_open_sockets, &sk)) {\n    // don't send a retransmit event on sockets we never notified userspace about\n    return;\n  }\n\n  // okay, can send an event\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__tcp_syn_timeout(ctx, now, (__u64)sk);\n}\n\n// tcp_retransmit_timer is an ancient function which retained basically the\n// same structure for a long time (since the initial git repo at 1da177e4c3f4,\n// \"Linux-2.6.12-rc2\"). The static qualifier was removed in f1ecd5d9e7366\n// (v2.6.32-rc1~703^2~172)\nSEC(\"kprobe/tcp_retransmit_timer\")\nint on_tcp_retransmit_timer(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  handle_syn_timeout(ctx, sk);\n  return 0;\n}\n\n// NOTE: tcp_syn_ack_timeout's signature was different pre v4.1 kernels.\n// See kernel commit 42cb80a2353f4, (v4.1-rc1~128^2~175^2~6).\n// the function seems to have been around since 72659ecce6858\n// (v2.6.34-rc1~233^2~563).\nSEC(\"kprobe/tcp_syn_ack_timeout\")\nint on_tcp_syn_ack_timeout(struct pt_regs *ctx)\n{\n  // the request_sock parameter is the first parameter since 4.1 kernels\n  const struct request_sock *req = (const struct request_sock *)PT_REGS_PARM1(ctx);\n\n  if (!req) {\n    return 0;\n  }\n\n#if TCP_STATS_ON_PARENT\n\n  if (LINUX_KERNEL_VERSION < KERNEL_VERSION(4, 4, 0)) {\n    /* Linux<4.4 does not have req->rsk_listener */\n    struct sock *sk = BPF_CORE_READ(req, sk);\n\n    struct tcp_open_socket_t *sk_info;\n    sk_info = bpf_map_lookup_elem(&tcp_open_sockets, &sk);\n    if (!sk_info) {\n      //  don't send a retransmit event on sockets we never notified userspace about\n      return 0;\n    }\n\n    handle_syn_timeout(ctx, sk_info->parent);\n  } else {\n    struct sock *listener = BPF_CORE_READ(req, rsk_listener);\n    handle_syn_timeout(ctx, listener);\n  }\n\n#else\n\n  struct sock *sk = BPF_CORE_READ(req, sk);\n  if (sk == NULL) {\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n    return 0;\n  }\n  handle_syn_timeout(ctx, sk);\n\n#endif\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* DNS */\n\n#define BACKWARDS_COMPATIBLE_DNS_BUFFER_SIZE 280\n#define DNS_MAX_PACKET_LEN 512\n\n// For newer kernels, define the structure and per-CPU array\nstruct dns_message_data {\n  char data[DNS_MAX_PACKET_LEN + sizeof(struct bpf_agent_internal__dns_packet) + 16];\n};\n// use per-CPU array to overcome eBPF stack size limit\nstruct {\n  __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);\n  __uint(max_entries, 1);\n  __type(key, __u32);\n  __type(value, struct dns_message_data);\n} dns_message_array SEC(\".maps\");\n#pragma passthrough off\n\n// Depending on when the skb is inspected, the header may or may not be filled\n// in yet so we are passing in protocol and port components as parameters here\n// sport and dport are in -network byte order-\nstatic __always_inline void\nperf_check_and_submit_dns(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb, u8 proto, u16 sport, u16 dport, int is_rx)\n{\n  unsigned int len = BPF_CORE_READ(skb, len);\n\n  // Filter for DNS requests and responses\n  if (!((proto == IPPROTO_UDP) && ((sport == bpf_htons(53)) || (dport == bpf_htons(53))) && (len > 0))) {\n    return;\n  }\n\n  // Read skb fields directly to avoid bpf verifier errors\n  unsigned int skb_data_len = BPF_CORE_READ(skb, data_len);\n\n  unsigned char *from = BPF_CORE_READ(skb, data);\n  if (from == NULL) {\n    return;\n  }\n\n  unsigned char *skb_head = BPF_CORE_READ(skb, head);\n  if (skb_head == NULL) {\n    return;\n  }\n\n  u16 skb_transport_header = BPF_CORE_READ(skb, transport_header);\n\n  u16 skb_network_header = BPF_CORE_READ(skb, network_header);\n\n  /* we only take the linear part of the skb, not the paged part\n   * 'valid_len' here is the amount of -non paged- data in the skb,\n   * since 'data_len' refers to just the amount of -paged- data, and 'len'\n   * is the total amount of skb data\n   */\n  unsigned int valid_len = len - skb_data_len;\n\n  /* in e6afc8ace6dd5 (i.e., v4.7-rc1~154^2~349^2~1), udp_recvmsg started\n     pulling the udp header before our kprobe. before then, skb->data pointed to\n     the udp header. make sure we don't copy the udp header on pre-4.7 kernels\n   */\n  /* if data points to transport header (udp) then advance to the packet body */\n  if (from == (skb_head + skb_transport_header)) {\n    if (valid_len < sizeof(struct udphdr))\n      return;\n    from += sizeof(struct udphdr);\n    valid_len -= sizeof(struct udphdr);\n  }\n  /* if data points to network header (ip) then advance to the packet body */\n  else if (from == skb_head + skb_network_header) {\n    from = skb_head + skb_transport_header + sizeof(struct udphdr);\n    unsigned int diff = (from - (skb_head + skb_network_header));\n    if (valid_len < diff)\n      return;\n    valid_len -= diff;\n  }\n\n  /* only enable this if we need to detect\n  if (valid_len < len) {\n    // we are facing a paged or fragmented skb. only copy headlen bytes\n    bpf_log(ctx, BPF_LOG_DATA_TRUNCATED, (u64)len, (u64)valid_len, 0);\n  }\n  */\n\n  /* allocate buffer for event */\n  char *buf;\n  char stack_buf[BACKWARDS_COMPATIBLE_DNS_BUFFER_SIZE + sizeof(struct bpf_agent_internal__dns_packet) + 16] = {};\n\n  if (LINUX_KERNEL_VERSION < KERNEL_VERSION(4, 15, 0)) {\n    // Use stack-based approach on kernels below 4.15 (chosen using empirical testing).\n    // This is limited by max eBPF stack size (512 bytes). On newer kernels we can\n    // use per-CPU arrays for copying data which allows processing larger packets.\n    buf = stack_buf;\n    if (valid_len > BACKWARDS_COMPATIBLE_DNS_BUFFER_SIZE) {\n      bpf_log(ctx, BPF_LOG_DATA_TRUNCATED, (u64)valid_len, (u64)BACKWARDS_COMPATIBLE_DNS_BUFFER_SIZE, 0);\n      valid_len = BACKWARDS_COMPATIBLE_DNS_BUFFER_SIZE;\n    }\n\n    /* the actual offset into buf has to start from buf's start */\n    char *to = buf + bpf_agent_internal__dns_packet__data_size;\n    bpf_probe_read_kernel(to, valid_len, from);\n\n  } else {\n    // use bigger per-CPU array based buffer on newer kernels\n    __u32 zero = 0;\n    struct dns_message_data *pkt = bpf_map_lookup_elem(&dns_message_array, &zero);\n    if (pkt == NULL) {\n      bpf_log(ctx, BPF_LOG_BPF_CALL_FAILED, 0, 0, 0);\n      return;\n    }\n    buf = pkt->data;\n\n    // truncate dns packets at our maximum packet length right now\n    valid_len &= (DNS_MAX_PACKET_LEN - 1);\n\n    /* the actual offset into buf has to start from buf's start */\n    char *to = buf + bpf_agent_internal__dns_packet__data_size;\n    bpf_probe_read_kernel(to, valid_len, from);\n  }\n\n  char *to = buf + bpf_agent_internal__dns_packet__data_size;\n\n  struct bpf_agent_internal__dns_packet *const msg = (struct bpf_agent_internal__dns_packet *)&buf[0];\n  struct jb_blob blob = {to, valid_len};\n  bpf_fill_agent_internal__dns_packet(msg, get_timestamp(), (u64)sk, blob, len, is_rx);\n\n  bpf_perf_event_output(\n      ctx,\n      &events,\n      BPF_F_CURRENT_CPU,\n      &msg->unpadded_size,\n      ((DNS_MAX_PACKET_LEN + sizeof(struct jb_agent_internal__dns_packet) + 8 + 7) / 8) * 8 + 4);\n}\n\n// - Receive UDP packets ---------------------------------------\nSEC(\"kprobe/skb_consume_udp\")\nint on_skb_consume_udp(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb, int len)\n{\n  // Call handle_receive_udp_skb\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_HANDLE_RECEIVE_UDP_SKB);\n\n  return 0;\n}\n\nSEC(\"kprobe/__skb_free_datagram_locked\")\nint on___skb_free_datagram_locked(struct pt_regs *ctx)\n{\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_HANDLE_RECEIVE_UDP_SKB);\n  return 0;\n}\n\nSEC(\"kprobe/skb_free_datagram_locked\")\nint on_skb_free_datagram_locked(struct pt_regs *ctx)\n{\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_HANDLE_RECEIVE_UDP_SKB);\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* CGROUPS */\n\nstatic __always_inline u32 get_css_id(struct cgroup_subsys_state *css)\n{\n  return (u32)BPF_CORE_READ(css, ss, id);\n}\n\nstatic __always_inline struct cgroup *get_css_parent_cgroup(struct cgroup_subsys_state *css)\n{\n  if (!css) {\n    return NULL;\n  }\n\n  return BPF_CORE_READ(css, parent, cgroup);\n}\n\nstatic const char *get_cgroup_name(struct cgroup *cg)\n{\n  if (!cg) {\n    return NULL;\n  }\n\n  if (bpf_core_field_exists(cg->kn->name)) {\n    return BPF_CORE_READ(cg, kn, name);\n  } else {\n    // < 3.15\n    struct cgroup___3_11 *cg = cg;\n    struct cgroup_name___3_11 *name = BPF_CORE_READ(cg, name);\n    if (!name) {\n      return NULL;\n    }\n    return (const char *)&(name->name[0]);\n  }\n}\n\nstatic struct cgroup *get_cgroup_parent(struct cgroup *cgrp)\n{\n  if (bpf_core_field_exists(cgrp->self)) {\n    // introduced in kernel 3.16\n    return get_css_parent_cgroup(&cgrp->self);\n  } else {\n    struct cgroup___3_11 *cg = cg;\n    return cg->parent;\n  }\n}\n\n// close\n\n// For Kernel >= 3.12\nSEC(\"kprobe/kill_css\")\nint on_kill_css(struct pt_regs *ctx)\n{\n  struct cgroup_subsys_state *css = (struct cgroup_subsys_state *)PT_REGS_PARM1(ctx);\n\n  u32 ssid = get_css_id(css);\n  if (ssid != FLOW_CGROUP_SUBSYS)\n    return 0;\n\n  struct cgroup *parent_cgroup = get_css_parent_cgroup(css);\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__kill_css(\n      ctx, now, (__u64)BPF_CORE_READ(css, cgroup), (__u64)parent_cgroup, (void *)get_cgroup_name(BPF_CORE_READ(css, cgroup)));\n  return 0;\n}\n\n// For Kernel < 3.12\nSEC(\"kprobe/cgroup_destroy_locked\")\nint on_cgroup_destroy_locked(struct pt_regs *ctx)\n{\n  struct cgroup *cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);\n\n  struct cgroup_subsys_state *css = NULL;\n  bpf_probe_read(&css, sizeof(css), &(cgrp->subsys[FLOW_CGROUP_SUBSYS]));\n  if (css == NULL)\n    return 0;\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__kill_css(ctx, now, (__u64)cgrp, (__u64)get_cgroup_parent(cgrp), (void *)get_cgroup_name(cgrp));\n\n  return 0;\n}\n\n// start\n\n// For Kernel >= 4.4\nSEC(\"kprobe/css_populate_dir\")\nint on_css_populate_dir(struct pt_regs *ctx)\n{\n  struct cgroup_subsys_state *css = (struct cgroup_subsys_state *)PT_REGS_PARM1(ctx);\n\n  u32 ssid = get_css_id(css);\n  if (ssid != FLOW_CGROUP_SUBSYS)\n    return 0;\n\n  struct cgroup *parent_cgroup = get_css_parent_cgroup(css);\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__css_populate_dir(\n      ctx, now, (__u64)BPF_CORE_READ(css, cgroup), (__u64)parent_cgroup, (void *)get_cgroup_name(BPF_CORE_READ(css, cgroup)));\n  return 0;\n}\n\n// For Kernel < 4.4\nSEC(\"kprobe/cgroup_populate_dir\")\nint on_cgroup_populate_dir(struct pt_regs *ctx)\n{\n  struct cgroup *cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);\n  unsigned long subsys_mask = (unsigned long)PT_REGS_PARM2(ctx);\n\n  struct cgroup_subsys_state *css = NULL;\n  bpf_probe_read(&css, sizeof(css), &(cgrp->subsys[FLOW_CGROUP_SUBSYS]));\n  if (css == NULL)\n    return 0;\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__css_populate_dir(\n      ctx, now, (__u64)cgrp, (__u64)get_cgroup_parent(cgrp), (void *)get_cgroup_name(cgrp));\n  return 0;\n}\n\n// existing cgroups v2\nBEGIN_DECLARE_SAVED_ARGS(cgroup_control)\nstruct cgroup *cgrp;\nEND_DECLARE_SAVED_ARGS(cgroup_control)\n\n// Only available for kernel >= 4.6.0\nSEC(\"kprobe/cgroup_control\")\nint on_cgroup_control(struct pt_regs *ctx)\n{\n  struct cgroup *cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);\n\n  GET_PID_TGID;\n\n  BEGIN_SAVE_ARGS(cgroup_control)\n  SAVE_ARG(cgrp)\n  END_SAVE_ARGS(cgroup_control)\n\n  return 0;\n}\n\n// Common function to handle cgroup processing\nstatic inline int handle_existing_cgroup(struct pt_regs *ctx, struct cgroup *cgrp, u16 subsys_mask)\n{\n  if (!(subsys_mask & 1 << FLOW_CGROUP_SUBSYS))\n    return 0;\n\n  u64 now = get_timestamp();\n  struct cgroup *parent_cgroup = get_css_parent_cgroup(&cgrp->self);\n\n  perf_submit_agent_internal__existing_cgroup_probe(ctx, now, (__u64)cgrp, (__u64)parent_cgroup, (void *)get_cgroup_name(cgrp));\n\n  return 0;\n}\n\n// For kernels >= 4.6.0\nSEC(\"kretprobe/cgroup_control\")\nint onret_cgroup_control(struct pt_regs *ctx)\n{\n  GET_PID_TGID;\n\n  GET_ARGS_MISSING_OK(cgroup_control, args)\n  if (args == NULL) {\n    return 0;\n  }\n\n  struct cgroup *cgrp = args->cgrp;\n\n  DELETE_ARGS(cgroup_control);\n\n  u16 subsys_mask = (u16)PT_REGS_RC(ctx);\n  return handle_existing_cgroup(ctx, cgrp, subsys_mask);\n}\n\nSEC(\"kretprobe/cgroup_get_from_fd\")\nint onret_cgroup_get_from_fd(struct pt_regs *ctx)\n{\n  // cgroup_get_from_fd returns the cgroup pointer or an error\n  struct cgroup *cgrp = (struct cgroup *)PT_REGS_RC(ctx);\n  if (cgrp == NULL || IS_ERR_VALUE((unsigned long)cgrp)) {\n    return 0;\n  }\n\n  // For cgroup_get_from_fd, we assume all subsystems are relevant\n  // since this is called when accessing cgroup via fd\n  u16 subsys_mask = 1 << FLOW_CGROUP_SUBSYS;\n  return handle_existing_cgroup(ctx, cgrp, subsys_mask);\n}\n\n// existing cgroups v1\n\n// Function that handles both kernel versions for clone children read\nint on_cgroup_clone_children_read_css(struct pt_regs *ctx, struct cgroup_subsys_state *css, struct cftype *cft)\n{\n  // For Kernel >= 3.12.0\n  if (LINUX_KERNEL_VERSION < KERNEL_VERSION(3, 12, 0)) {\n    return 0; // Should use the cgroup version instead\n  }\n\n  u32 subsys_mask = (u32)BPF_CORE_READ(css, cgroup, root, subsys_mask);\n  if (subsys_mask != 1 << FLOW_CGROUP_SUBSYS)\n    return 0;\n\n  u64 now = get_timestamp();\n  struct cgroup *parent_cgroup = get_css_parent_cgroup(css);\n\n  perf_submit_agent_internal__existing_cgroup_probe(\n      ctx, now, (__u64)BPF_CORE_READ(css, cgroup), (__u64)parent_cgroup, (void *)get_cgroup_name(BPF_CORE_READ(css, cgroup)));\n\n  return 0;\n}\n\n// For Kernel < 3.12.0\nSEC(\"kprobe/cgroup_clone_children_read\")\nint on_cgroup_clone_children_read(struct pt_regs *ctx)\n{\n  struct cgroup *cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);\n  u32 subsys_mask = BPF_CORE_READ(cgrp, root, subsys_mask);\n  if (subsys_mask != 1 << FLOW_CGROUP_SUBSYS)\n    return 0;\n\n  u64 now = get_timestamp();\n  struct cgroup *parent_cgroup = get_cgroup_parent(cgrp);\n\n  perf_submit_agent_internal__existing_cgroup_probe(ctx, now, (__u64)cgrp, (__u64)parent_cgroup, (void *)get_cgroup_name(cgrp));\n\n  return 0;\n}\n\n// modify\nSEC(\"kprobe/cgroup_attach_task\")\nint on_cgroup_attach_task(struct pt_regs *ctx)\n{\n  struct cgroup *dst_cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);\n  struct task_struct *leader = (struct task_struct *)PT_REGS_PARM2(ctx);\n  u32 subsys_mask = BPF_CORE_READ(dst_cgrp, root, subsys_mask);\n  if (subsys_mask != 1 << FLOW_CGROUP_SUBSYS)\n    return 0;\n\n  pid_t pid = BPF_CORE_READ(leader, pid);\n  u8 comm[TASK_COMM_LEN];\n  if (bpf_probe_read_kernel_str(comm, sizeof(comm), leader->comm) <= 0) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__cgroup_attach_task(ctx, now, (__u64)dst_cgrp, pid, comm);\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n/* NAT */\n/* end */\n\n// Cleanup: nf_ct_delete is invoked for all teardown paths\nSEC(\"kprobe/nf_ct_delete\")\nint on_nf_ct_delete(struct pt_regs *ctx)\n{\n  struct nf_conn *ct = (struct nf_conn *)PT_REGS_PARM1(ctx);\n\n  if (!ct) {\n    return 0;\n  }\n\n  // Filter to IPv4 TCP/UDP only\n  if ((u16)BPF_CORE_READ(ct, tuplehash[0].tuple.src.l3num) != AF_INET) {\n    return 0;\n  }\n  u8 proto = (u8)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.protonum);\n  if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {\n    return 0;\n  }\n\n  // Only report for conntracks we've seen during confirm path (NAT-ed)\n  struct nf_conn **seen_conntrack = bpf_map_lookup_elem(&seen_conntracks, &ct);\n  if (!seen_conntrack) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__nf_nat_cleanup_conntrack(\n      ctx,\n      now,\n      (u64)ct,\n      (u32)BPF_CORE_READ(ct, tuplehash[0].tuple.src.u3.ip),\n      (u16)BPF_CORE_READ(ct, tuplehash[0].tuple.src.u.all),\n      (u32)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.u3.ip),\n      (u16)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.u.all),\n      (u8)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.protonum));\n\n  // Remove from seen table\n  bpf_map_delete_elem(&seen_conntracks, &ct);\n\n  return 0;\n}\n\n/* start */\n// Create: confirmation path via __nf_conntrack_confirm (entry + ret)\nSEC(\"kprobe/__nf_conntrack_confirm\")\nint on___nf_conntrack_confirm(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  if (!skb) {\n    return 0;\n  }\n  __u32 zero = 0;\n  struct sk_buff **slot = bpf_map_lookup_elem(&nfct_confirm_skb, &zero);\n  if (slot)\n    *slot = skb;\n  return 0;\n}\n\nSEC(\"kretprobe/__nf_conntrack_confirm\")\nint onret___nf_conntrack_confirm(struct pt_regs *ctx)\n{\n  int ret = (int)PT_REGS_RC(ctx);\n  // Only proceed on successful confirm (NF_ACCEPT == 1)\n  if (ret != 1) {\n    __u32 zero = 0;\n    struct sk_buff **slot = bpf_map_lookup_elem(&nfct_confirm_skb, &zero);\n    if (slot)\n      *slot = NULL;\n    return 0;\n  }\n\n  __u32 zero = 0;\n  struct sk_buff **skb_pp = bpf_map_lookup_elem(&nfct_confirm_skb, &zero);\n  if (!skb_pp) {\n    return 0;\n  }\n  struct sk_buff *skb = *skb_pp;\n  *skb_pp = NULL;\n\n  if (!skb) {\n    return 0;\n  }\n\n  // Extract ct from skb->_nfct with NFCT_PTRMASK (~1UL). Use CO-RE flavor cast.\n  if (!bpf_core_field_exists(((struct sk_buff___with_nfct *)skb)->_nfct)) {\n    return 0;\n  }\n  unsigned long nfct = BPF_CORE_READ((struct sk_buff___with_nfct *)skb, _nfct);\n  struct nf_conn *ct = (struct nf_conn *)(nfct & ~7UL);\n  if (!ct) {\n    return 0;\n  }\n\n  // IPv4 only\n  if ((u16)BPF_CORE_READ(ct, tuplehash[0].tuple.src.l3num) != AF_INET) {\n    return 0;\n  }\n\n  // TCP/UDP only\n  u8 proto = (u8)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.protonum);\n  if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {\n    return 0;\n  }\n\n  // Build ORIGINAL tuple (pre-NAT, original direction)\n  u32 orig_src_ip = (u32)BPF_CORE_READ(ct, tuplehash[0].tuple.src.u3.ip);\n  u16 orig_src_port = (u16)BPF_CORE_READ(ct, tuplehash[0].tuple.src.u.all);\n  u32 orig_dst_ip = (u32)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.u3.ip);\n  u16 orig_dst_port = (u16)BPF_CORE_READ(ct, tuplehash[0].tuple.dst.u.all);\n\n  // Build POST-NAT tuple in ORIGINAL direction from REPLY by flipping src/dst\n  u32 nat_src_ip = (u32)BPF_CORE_READ(ct, tuplehash[1].tuple.dst.u3.ip);\n  u16 nat_src_port = (u16)BPF_CORE_READ(ct, tuplehash[1].tuple.dst.u.all);\n  u32 nat_dst_ip = (u32)BPF_CORE_READ(ct, tuplehash[1].tuple.src.u3.ip);\n  u16 nat_dst_port = (u16)BPF_CORE_READ(ct, tuplehash[1].tuple.src.u.all);\n\n  // Filter out non-NAT flows by comparing tuples\n  if (orig_src_ip == nat_src_ip && orig_src_port == nat_src_port && orig_dst_ip == nat_dst_ip &&\n      orig_dst_port == nat_dst_port) {\n    return 0;\n  }\n\n  // Record in seen_conntracks (permit duplicates; BPF_ANY)\n  int up_ret = bpf_map_update_elem(&seen_conntracks, &ct, &ct, BPF_ANY);\n  if (up_ret != 0) {\n#if DEBUG_OTHER_MAP_ERRORS\n    bpf_trace_printk(\"onret___nf_conntrack_confirm: seen_conntracks update failed ret=%d\\n\", up_ret);\n#endif\n  }\n\n  u64 now = get_timestamp();\n  perf_submit_agent_internal__nf_conntrack_alter_reply(\n      ctx,\n      now,\n      (u64)ct,\n      orig_src_ip,\n      orig_src_port,\n      orig_dst_ip,\n      orig_dst_port,\n      proto,\n      nat_src_ip,\n      nat_src_port,\n      nat_dst_ip,\n      nat_dst_port,\n      proto);\n\n  return 0;\n}\n\nSEC(\"kprobe/ctnetlink_dump_tuples\")\nint on_ctnetlink_dump_tuples(struct pt_regs *ctx)\n{\n  struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);\n  const struct nf_conntrack_tuple *ct = (const struct nf_conntrack_tuple *)PT_REGS_PARM2(ctx);\n\n  if (!ct) {\n    return 0;\n  }\n\n  u64 now = get_timestamp();\n\n  // \"struct nf_conn\" contains two \"struct nf_conntrack_tuple_hash\". On the\n  // dir=1 case, we want to report the addr of the dir=0, which we expected to\n  // see first. this is addr is sizeof(struct nf_conntrack_tuple_hash) ahead of\n  // the start of our current \"ct\"\n  u64 ct_addr = (u64)ct;\n\n  u8 dir = BPF_CORE_READ(ct, dst.dir);\n  if (dir == 0) {\n    perf_submit_agent_internal__existing_conntrack_tuple(\n        ctx,\n        now,\n        (u64)ct_addr,\n        (u32)BPF_CORE_READ(ct, src.u3.ip),\n        (u16)BPF_CORE_READ(ct, src.u.all),\n        (u32)BPF_CORE_READ(ct, dst.u3.ip),\n        (u16)BPF_CORE_READ(ct, dst.u.all),\n        (u8)BPF_CORE_READ(ct, dst.protonum),\n        (u8)dir);\n  } else {\n    ct_addr = ct_addr - sizeof(struct nf_conntrack_tuple_hash);\n    // NOTE: in the dir=1 case, we flip src/dst to preserve four-tuple order.\n    perf_submit_agent_internal__existing_conntrack_tuple(\n        ctx,\n        now,\n        (u64)ct_addr,\n        (u32)BPF_CORE_READ(ct, dst.u3.ip),\n        (u16)BPF_CORE_READ(ct, dst.u.all),\n        (u32)BPF_CORE_READ(ct, src.u3.ip),\n        (u16)BPF_CORE_READ(ct, src.u.all),\n        (u8)BPF_CORE_READ(ct, dst.protonum),\n        (u8)dir);\n  }\n  return 0;\n}\n\n//\n// Include other modules\n//\n\n#include \"tcp-processor/bpf_tcp_processor.c\"\n\nchar _license[] SEC(\"license\") = \"Dual MIT/GPL\";\n"
  },
  {
    "path": "collector/kernel/bpf_src/render_bpf.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n// This file is included both in BPF (render_bpf and tcp-processor) as well as by userland agent.\n\n////////////////////////////////////////////////////////////////////////////\n// Config\n\n///////// render_bpf.c config\n\n#define BPF_MAX_CPUS 1024           // Maximum number of CPUs to support\n#define TABLE_SIZE__TGID_INFO 65536 // Task (TGID) information - compile-time constant needed for map definition\n#define TABLE_SIZE__SEEN_INODES                                                                                                \\\n  70000 // XXX: Is this even necessary? could this tracking be done in userland with non-limited tables?\n#define TABLE_SIZE__TCP_OPEN_SOCKETS (256 * 1024) // Was 4096, but should be larger to accommodate high traffic systems\n#define TABLE_SIZE__UDP_OPEN_SOCKETS (256 * 1024) // Was 4096, but should be larger to accommodate high traffic systems\n#define TABLE_SIZE__UDP_GET_PORT_HASH 512         // Should be no more than the number of cores, in theory\n#define TABLE_SIZE__SEEN_CONNTRACKS                                                                                            \\\n  (TABLE_SIZE__TCP_OPEN_SOCKETS +                                                                                              \\\n   TABLE_SIZE__UDP_OPEN_SOCKETS)         // Worst case scenario, we have a conntrack for every open socket\n#define TABLE_SIZE__DEAD_GROUP_TASKS 512 // Should be no more than the number of cores, in theory\n#define TABLE_SIZE__STACK_TRACES 16384   // Number of stack traces to keep in the table\n#define TABLE_SIZE__NIC_INFO_TABLE 128   // Info per network interface\n\n#define WATERMARK_STACK_TRACES                                                                                                 \\\n  (TABLE_SIZE__STACK_TRACES - 256) // When to clear the table (unfortunately non-atomic, but that's a lot of stack traces...)\n\n// Maximum number of bytes in a tcp_data message block\n#define DATA_CHANNEL_CHUNK_MAX 16383\n\n// Shouldn't be necessary, be we aren't getting the lifetime of tcp\n// sockets right in some edge case\n\n// Forces socket lifetimes to not overlap due missing or out-of-order destroy events\n#define TCP_LIFETIME_HACK 1\n#define UDP_LIFETIME_HACK 1\n\n// Adds sockets that were missed during our initial 'existing' walk\n#define TCP_EXISTING_HACK 1\n#define UDP_EXISTING_HACK 1\n\n// When sockets are accepted, do we report the statistics on the child or parent socket?\n#define TCP_STATS_ON_PARENT 1\n\n///////// bpf_tcp_processor.c config\n\n#define TCP_CONNECTION_HASH_SIZE TABLE_SIZE__TCP_OPEN_SOCKETS // Same for now to ensure we can always handle all http requests\n\n////////////////////////////////////////////////////////////////////////////\n// Debugging flags\n\n///////// render_bpf.c debugging switches\n\n// #define TRACE_TCP_SOCKETS 1\n#define TRACE_UDP_SOCKETS 0\n\n// #define DEBUG_TCP_SOCKET_ERRORS 1\n#define DEBUG_UDP_SOCKET_ERRORS 0\n// #define DEBUG_OTHER_MAP_ERRORS 1\n\n// #define DEBUG_ENABLE_STACKTRACE 1\n\n///////// bpf_tcp_processor.c debugging switches\n\n// #define TRACE_TCP_CONNECTION 1\n// #define DEBUG_TCP_CONNECTION 1\n// #define DEBUG_MEMORY 1\n// #define TRACE_SOCKET_ACCEPT 1\n// #define DEBUG_TCP_RECEIVE 1\n// #define TRACE_TCP_RECEIVE 1\n// #define DEBUG_TCP_SEND 1\n// #define TRACE_TCP_SEND 1\n// #define TRACE_HTTP_PROTOCOL 1\n// #define DEBUG_TCP_DATA 1\n// #define DEBUG_DATA_CHANNEL 1\n\n////////////////////////////////////////////////////////////////////////////\n// BPF error report codes\n\nenum BPF_LOG_CODE {\n  BPF_LOG_UNKNOWN = 0,\n  BPF_LOG_TABLE_FULL = 1,             // arg0 = table id, arg1 = tgid, arg2 = item\n  BPF_LOG_TABLE_BAD_INSERT = 2,       // arg0 = table id, arg1 = tgid, arg2 = return code\n  BPF_LOG_TABLE_BAD_REMOVE = 3,       // arg0 = table id, arg1 = item being removed, arg2 = return code\n  BPF_LOG_TABLE_MISSING_KEY = 4,      // arg0 = table id, arg1 = item looked up\n  BPF_LOG_LIFETIME_HACK = 5,          // arg0 = table id, arg1 = item\n  BPF_LOG_TABLE_DUPLICATE_INSERT = 6, // arg0 = table id, arg1 = tgid, arg2 = item\n  BPF_LOG_UNEXPECTED_TYPE = 7,        // arg0 = key, arg1 = type\n  BPF_LOG_DATA_TRUNCATED = 8,         // arg0 = total len, arg1 = truncated len\n  BPF_LOG_INVALID_POINTER = 9,        // none\n  BPF_LOG_UNSUPPORTED_IO = 10,        // arg0 = ST_SEND/ST_RECV, arg1 = sk, arg2 = type\n  BPF_LOG_MISSING_ARGUMENTS = 11,     // none\n  BPF_LOG_UNREACHABLE = 12,           // none\n  BPF_LOG_THROTTLED = 13,             // arg0 = cpu number\n  BPF_LOG_UNEXPECTED_VALUE = 14,      // arg0 = value\n  BPF_LOG_INVALID_PID_TGID = 15,      // arg0 = pid_tgid\n  BPF_LOG_EXISTING_HACK = 16,         // arg0 = table id, arg1 = item\n  BPF_LOG_BPF_CALL_FAILED = 17,       // arg0 = ret val\n};\n\nenum BPF_TABLE_ID {\n  BPF_TABLE_TGID_INFO = 0,\n  BPF_TABLE_SEEN_INODES = 1,\n  BPF_TABLE_TCP_OPEN_SOCKETS = 2,\n  BPF_TABLE_TCP_OPENREQ_CHILD_HASH = 3,\n  BPF_TABLE_UDP_OPEN_SOCKETS = 4,\n  BPF_TABLE_UDP_GET_PORT_HASH = 5,\n  BPF_TABLE_SEEN_CONNTRACKS = 6,\n  BPF_TABLE_TCP_CONNECTIONS = 7,\n  BPF_TABLE_DEAD_GROUP_TASKS = 8,\n  BPF_TABLE_PID_INFO = 9,\n};\n\n// Log throttling, currently set to a max of 20 messages per second\n#define BPF_LOG_THROTTLE_MAX_PER_PERIOD 20     // maximum number of log messages from bpf per period\n#define BPF_LOG_THROTTLE_PERIOD_LENGTH_MS 1000 // length of the log throttle period in milliseconds\n\n// Tail Calls\n#define TAIL_CALL_ON_UDP_SEND_SKB__2 0\n#define TAIL_CALL_ON_UDP_V6_SEND_SKB__2 1\n#define TAIL_CALL_ON_IP_SEND_SKB__2 2\n#define TAIL_CALL_ON_IP6_SEND_SKB__2 3\n#define TAIL_CALL_HANDLE_RECEIVE_UDP_SKB 4\n#define TAIL_CALL_HANDLE_RECEIVE_UDP_SKB__2 5\n#define TAIL_CALL_CONTINUE_TCP_SENDMSG 6\n#define TAIL_CALL_CONTINUE_TCP_RECVMSG 7\n#define NUM_TAIL_CALLS 8\n\n#if _PROCESSING_BPF\n// Include this until we merge the tcp-processor code into render_bpf more closely\n// Note, this is included by bpf and userland c++, so it -must- be an include with \"\" not <>\n// as this distinction determines which includes are processed during BPF compilation\n#include \"tcp-processor/tcp_processor.h\"\n#else\n#include <collector/kernel/bpf_src/tcp-processor/tcp_processor.h>\n#endif\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_data_channel.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// TCP Data sent to userland - perf event array map for libbpf\nstruct {\n  __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);\n  __uint(key_size, sizeof(__u32));\n  __uint(value_size, sizeof(__u32));\n} data_channel SEC(\".maps\");\n\n// Slightly more compact fore than COPY_BIT(2) + COPY_BIT(1)\n#define COPY_LAST_BITS                                                                                                         \\\n  const int last_bits = len & 3;                                                                                               \\\n  msg.hdr.length = last_bits;                                                                                                  \\\n  switch (last_bits) {                                                                                                         \\\n  default:                                                                                                                     \\\n    break;                                                                                                                     \\\n  case 1:                                                                                                                      \\\n    bpf_probe_read(&msg.data, 1, in);                                                                                          \\\n    bpf_perf_event_output(ctx, &data_channel, BPF_F_CURRENT_CPU, &msg, sizeof(struct data_channel_header_t) + 1);              \\\n    break;                                                                                                                     \\\n  case 2:                                                                                                                      \\\n    bpf_probe_read(&msg.data, 2, in);                                                                                          \\\n    bpf_perf_event_output(ctx, &data_channel, BPF_F_CURRENT_CPU, &msg, sizeof(struct data_channel_header_t) + 2);              \\\n    break;                                                                                                                     \\\n  case 3:                                                                                                                      \\\n    bpf_probe_read(&msg.data, 3, in);                                                                                          \\\n    bpf_perf_event_output(ctx, &data_channel, BPF_F_CURRENT_CPU, &msg, sizeof(struct data_channel_header_t) + 3);              \\\n    break;                                                                                                                     \\\n  }\n\n#define COPY_BIT(B)                                                                                                            \\\n  if (len & B) {                                                                                                               \\\n    msg.hdr.length = B;                                                                                                        \\\n    bpf_probe_read(&msg.data, B, in);                                                                                          \\\n    bpf_perf_event_output(ctx, &data_channel, BPF_F_CURRENT_CPU, &msg, sizeof(struct data_channel_header_t) + B);              \\\n    in += B;                                                                                                                   \\\n  }\n\n#define COPY_CHUNK_256                                                                                                         \\\n  bpf_probe_read(&msg.data, 256, in);                                                                                          \\\n  bpf_perf_event_output(ctx, &data_channel, BPF_F_CURRENT_CPU, &msg, sizeof(struct data_channel_header_t) + 256);              \\\n  in += 256;\n\n#define COPY_BIT_256                                                                                                           \\\n  if (len & 256) {                                                                                                             \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n#define COPY_BIT_512                                                                                                           \\\n  if (len & 512) {                                                                                                             \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n#define COPY_BIT_1024                                                                                                          \\\n  if (len & 1024) {                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n#define COPY_BIT_2048                                                                                                          \\\n  if (len & 2048) {                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n#define COPY_BIT_4096                                                                                                          \\\n  if (len & 4096) {                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n#define COPY_BIT_8192                                                                                                          \\\n  if (len & 8192) {                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n    COPY_CHUNK_256;                                                                                                            \\\n  }\n\n// Submit contents of TCP data stream to the perf ring to userland\nstatic __always_inline void\ndata_channel_submit(struct pt_regs *ctx, struct tcp_connection_t *pconn, const void *data, size_t data_len, size_t *actual_len)\n{\n  // Clip the length to the most we can copy\n  unsigned int len = data_len;\n  if (len > DATA_CHANNEL_CHUNK_MAX) {\n#if DEBUG_DATA_CHANNEL\n    bpf_log(ctx, BPF_LOG_DATA_TRUNCATED, (u64)len, (u64)DATA_CHANNEL_CHUNK_MAX, 0);\n#endif\n    len = DATA_CHANNEL_CHUNK_MAX;\n  }\n  *actual_len = len;\n\n#if DEBUG_DATA_CHANNEL\n  bpf_trace_printk(\"tcp_events_submit_tcp_data: sk=%llx, len=%u, data_len=%u\\n\", pconn->sk, len, data_len);\n#endif\n\n  // Copy the data to the data stream\n  const u8 *in = (const u8 *)data;\n  struct {\n    struct data_channel_header_t hdr;\n    char data[256];\n  } msg = {.hdr.length = 256, .data = {}};\n\n  // Copy 256 byte chunks, because we can't have a very large on-stack buffer\n  // and early ebpf can't bpf_probe_read to anywhere but the stack, and also\n  // early ebpf can not perf_submit from anything other than the stack\n#if DATA_CHANNEL_CHUNK_MAX >= 8192\n  COPY_BIT_8192;\n#endif\n#if DATA_CHANNEL_CHUNK_MAX >= 4096\n  COPY_BIT_4096;\n#endif\n#if DATA_CHANNEL_CHUNK_MAX >= 2048\n  COPY_BIT_2048;\n#endif\n#if DATA_CHANNEL_CHUNK_MAX >= 1024\n  COPY_BIT_1024;\n#endif\n#if DATA_CHANNEL_CHUNK_MAX >= 512\n  COPY_BIT_512;\n#endif\n#if DATA_CHANNEL_CHUNK_MAX >= 256\n  COPY_BIT_256;\n#endif\n\n#if DATA_CHANNEL_CHUNK_MAX < 256\n#error \"Invalid data channel chunk size\"\n#endif\n\n  // Copy  < 256 byte variable length chunks\n  COPY_BIT(128);\n  COPY_BIT(64);\n  COPY_BIT(32);\n  COPY_BIT(16);\n  COPY_BIT(8);\n  COPY_BIT(4);\n\n  // Copy last bits using a slightly more compact form so this fits in the 4k instruction limit\n  COPY_LAST_BITS;\n\n  // COPY_BIT(2);\n  // COPY_BIT(1);\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_debug.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n//\n// bpf_debug.h - BPF Debugging Tools\n//\n\n#pragma once\n\n#include \"tcp-processor/bpf_types.h\"\n\n#if DEBUG_LOG\n#define DEBUG_PRINTK bpf_trace_printk\n#else\n#define DEBUG_PRINTK(...)\n#endif\n\nstatic void s_print_bpf_assert(int cond, const char *condstr)\n{\n  DEBUG_PRINTK(\"BPF_ASSERT failed: %s\\n\", condstr);\n}\n\n#define BPF_ASSERT_RET(COND, RET)                                                                                              \\\n  if (!(COND)) {                                                                                                               \\\n    char __condstr[] = #COND;                                                                                                  \\\n    s_print_bpf_assert((COND), __condstr);                                                                                     \\\n    return (RET);                                                                                                              \\\n  }\n\n#if DEBUG_ENABLE_STACKTRACE\n\n#include <linux/sched.h>\n#include <uapi/linux/ptrace.h>\n\nBPF_STACK_TRACE(stack_traces, TABLE_SIZE__STACK_TRACES);\n\nstatic __always_inline void stack_trace(struct pt_regs *ctx)\n{\n  char comm[16];\n  u64 now = get_timestamp();\n  s32 kernel_stack_id = stack_traces.get_stackid(ctx, 0);\n  s32 user_stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);\n  u32 tgid = bpf_get_current_pid_tgid() >> 32;\n  bpf_get_current_comm(comm, sizeof(comm));\n\n  perf_submit_agent_internal__stack_trace(ctx, now, kernel_stack_id, user_stack_id, tgid, comm);\n}\n\n#endif\n\nstatic __always_inline int __check_broken_in6_addr(struct in6_addr *addr, int line)\n{\n  if (addr->in6_u.u6_addr16[0] == 0x1140 && addr->in6_u.u6_addr16[2] == 0x007f) {\n    bpf_trace_printk(\"broken in6_addr on line %d\\n\", line);\n    return 1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_http_protocol.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n//\n// bpf_http_request.h - BPF HTTP Request Parsing\n//\n\n#pragma once\n\nstruct http_protocol_state_data_t {\n  u64 request_timestamp;\n  u64 __unused; // Keep this to TCP_SOCKET_PROTOCOL_STATE_SIZE\n};\n\nstatic __always_inline enum TCP_PROTOCOL_DETECT_RESULT http_detect(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const char *data,\n    size_t data_len)\n{\n#if TRACE_HTTP_PROTOCOL\n  DEBUG_PRINTK(\n      \"http_detect: start=%llu, total=%llu\\n\",\n      (u32)pctrl->streams[(int)streamtype].start,\n      pconn->streams[(int)streamtype].total);\n  DEBUG_PRINTK(\"             data=%s, data_len=%d\\n\", data, (int)data_len);\n#endif\n\n  enum TCP_PROTOCOL_DETECT_RESULT res = TPD_UNKNOWN;\n\n  // Does the buffer start with an HTTP verb?\n  if (data_len >= 3) {\n    char c = 0;\n    bpf_probe_read(&c, 1, data);\n    switch (c) {\n    case 'G':\n      res = string_starts_with(data + 1, (size_t)(data_len - 1), \"ET\", (sizeof(\"ET\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      break;\n    case 'H':\n      if (data_len >= 4) {\n        res = string_starts_with(data + 1, (size_t)(data_len - 1), \"EAD\", (sizeof(\"EAD\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      }\n      break;\n    case 'D':\n      if (data_len >= 6) {\n        res = string_starts_with(data + 1, (size_t)(data_len - 1), \"ELETE\", (sizeof(\"ELETE\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      }\n      break;\n    case 'C':\n      if (data_len >= 7) {\n        res = string_starts_with(data + 1, (size_t)(data_len - 1), \"ONNECT\", (sizeof(\"ONNECT\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      }\n      break;\n    case 'O':\n      if (data_len >= 7) {\n        res = string_starts_with(data + 1, (size_t)(data_len - 1), \"PTIONS\", (sizeof(\"PTIONS\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      }\n      break;\n    case 'T':\n      if (data_len >= 5) {\n        res = string_starts_with(data + 1, (size_t)(data_len - 1), \"RACE\", (sizeof(\"RACE\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n      }\n      break;\n\n    case 'P':\n      bpf_probe_read(&c, 1, data + 1);\n      switch (c) {\n      case 'U':\n        res = string_starts_with(data + 2, (size_t)(data_len - 2), \"T\", (sizeof(\"T\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n        break;\n      case 'O':\n        res = string_starts_with(data + 2, (size_t)(data_len - 2), \"ST\", (sizeof(\"ST\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n        break;\n      case 'A':\n        if (data_len >= 5) {\n          res = string_starts_with(data + 2, (size_t)(data_len - 2), \"TCH\", (sizeof(\"TCH\") - 1)) ? TPD_SUCCESS : TPD_FAILED;\n        }\n        break;\n      default:\n        res = TPD_FAILED;\n        break;\n      }\n      break;\n\n    default:\n      res = TPD_FAILED;\n      break;\n    }\n  }\n\n  return res;\n}\n\nstatic __always_inline void http_process_request(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const u8 *data,\n    size_t data_len)\n{\n#if TRACE_HTTP_PROTOCOL\n  DEBUG_PRINTK(\n      \"http_process_request: start=%llu, total=%llu\\n\",\n      (u32)pctrl->streams[(int)streamtype].start,\n      pconn->streams[(int)streamtype].total);\n  DEBUG_PRINTK(\"             data=%s, data_len=%d\\n\", data, (int)data_len);\n#endif\n\n  // Keep the request timestamp for latency calculation\n  struct http_protocol_state_data_t *state_data = (struct http_protocol_state_data_t *)(pconn->protocol_state.data);\n  state_data->request_timestamp = get_timestamp();\n\n  // Enable getting the response, and turn off the request side until we get it\n  if (streamtype == ST_RECV) {\n    enable_tcp_connection(pctrl, -1, 1);\n  } else {\n    enable_tcp_connection(pctrl, 1, -1);\n  }\n}\n\nstatic __always_inline void http_process_response(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const u8 *data,\n    size_t data_len)\n{\n#if TRACE_HTTP_PROTOCOL\n  DEBUG_PRINTK(\n      \"http_process_response: start=%llu, total=%llu\\n\",\n      (u32)pctrl->streams[(int)streamtype].start,\n      pconn->streams[(int)streamtype].total);\n  DEBUG_PRINTK(\"             data=%s, data_len=%d\\n\", data, (int)data_len);\n#endif\n\n  // HTTP response can't be less than 12 characters long, if we haven't gotten\n  // 12 characters yet, just wait until more bytes show up\n  if (data_len < 12) {\n    goto finished_response;\n  }\n\n  char localdata[12] = {};\n  bpf_probe_read(localdata, 12, data);\n\n  // Pattern match against HTTP/x.y. localdata is on BPF stack, so compare directly\n  // instead of using string_starts_with (which reads via helpers).\n  if (!(localdata[0] == 'H' && localdata[1] == 'T' && localdata[2] == 'T' && localdata[3] == 'P' && localdata[4] == '/') ||\n      localdata[6] != '.' || localdata[8] != ' ') {\n    goto finished_response;\n  }\n  int http_version_major = char_to_number(localdata[5]);\n  int http_version_minor = char_to_number(localdata[7]);\n\n#if TRACE_HTTP_PROTOCOL\n  DEBUG_PRINTK(\"HTTP version: %d.%d\\n\", http_version_major, http_version_minor);\n#endif\n  // Check for invalid HTTP version\n  if (http_version_major == -1 || http_version_minor == -1) {\n    goto finished_response;\n  }\n\n  // Convert HTTP response code to integer\n  int http_code_2 = char_to_number(localdata[9]);\n  int http_code_1 = char_to_number(localdata[10]);\n  int http_code_0 = char_to_number(localdata[11]);\n\n#if TRACE_HTTP_PROTOCOL\n  DEBUG_PRINTK(\"HTTP response code: %d%d%d\\n\", http_code_2, http_code_1, http_code_0);\n#endif\n\n  // Ensure each digit of HTTP response code is valid\n  if (http_code_2 == -1 || http_code_1 == -1 || http_code_0 == -1) {\n    goto finished_response;\n  }\n\n  u16 code = (u16)(http_code_2 * 100 + http_code_1 * 10 + http_code_0);\n\n  // Submit the http response code, and latency\n  struct http_protocol_state_data_t *state_data = (struct http_protocol_state_data_t *)(pconn->protocol_state.data);\n  u64 latency = get_timestamp() - state_data->request_timestamp;\n  u8 client_server = (streamtype == ST_SEND) ? SC_SERVER : SC_CLIENT;\n  tcp_events_submit_http_response(ctx, pconn->sk, code, latency, client_server);\n\n  // Enable getting the request, and turn off the response side until we get it\nfinished_response:\n\n  if (streamtype == ST_RECV) {\n    enable_tcp_connection(pctrl, -1, 1);\n  } else {\n    enable_tcp_connection(pctrl, 1, -1);\n  }\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_inet_csk_accept.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n//\n// bpf_inet_csk_accept.h - BPF handler for connection-based socket accepts\n//\n\n#pragma once\n\n#include \"bpf_debug.h\"\n#include \"bpf_memory.h\"\n#include \"bpf_tcp_socket.h\"\n#include \"bpf_types.h\"\n\nBEGIN_DECLARE_SAVED_ARGS(inet_csk_accept)\nstruct sock *sk;\nint flags;\nu32 _pad_0; // required alignment\nint *err;\nEND_DECLARE_SAVED_ARGS(inet_csk_accept)\n\n// --- inet_csk_accept --------------------------------------------------\n// Called when a listen socket accepts gets a connection\nSEC(\"kprobe/inet_csk_accept\")\nint handle_kprobe__inet_csk_accept(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  int flags = (int)PT_REGS_PARM2(ctx);\n  int *err = (int *)PT_REGS_PARM3(ctx);\n  // bool kern = (bool)PT_REGS_PARM4(ctx); // not used\n\n  GET_PID_TGID\n\n  // Ensure the parent socket has a tcp_connection\n  struct tcp_connection_t *psk_info;\n  psk_info = lookup_tcp_connection(sk);\n  if (!psk_info) {\n    psk_info = create_tcp_connection(ctx, sk);\n    if (!psk_info) {\n#if DEBUG_TCP_CONNECTION\n      DEBUG_PRINTK(\"inet_csk_accept: couldn't create tcp_connection\");\n#endif\n      bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_CONNECTIONS, _tgid, (u64)sk);\n      return 0;\n    }\n  }\n\n  // Link us to our kretprobe\n  BEGIN_SAVE_ARGS(inet_csk_accept)\n  SAVE_ARG(sk)\n  SAVE_ARG(flags)\n  SAVE_ARG(err)\n  END_SAVE_ARGS(inet_csk_accept)\n\n#if TRACE_SOCKET_ACCEPT\n  DEBUG_PRINTK(\"inet_csk_accept enter: pid %u accepting socket on %llx\\n\", psk_info->upid, sk);\n  DEBUG_PRINTK(\"                       pid_tgid %llu added\\n\", _pid_tgid);\n#endif\n\n  return 0;\n}\nSEC(\"kretprobe/inet_csk_accept\")\nint handle_kretprobe__inet_csk_accept(struct pt_regs *ctx)\n{\n  GET_PID_TGID\n\n  // Ensure the accept succeeded\n  struct sock *newsk = (struct sock *)PT_REGS_RC(ctx);\n  if (!newsk) {\n\n#if TRACE_SOCKET_ACCEPT\n    DEBUG_PRINTK(\"                       accept failed\\n\");\n    DEBUG_PRINTK(\"inet_csk_accept exit:  pid_tgid %llu removed\\n\", _pid_tgid);\n#endif\n    // Unlink the kprobe/kretprobe\n    DELETE_ARGS(inet_csk_accept);\n    return 0;\n  }\n\n  // Link us from our kprobe\n  GET_ARGS(inet_csk_accept, args);\n  if (args == NULL) {\n    // Race condition where we might have been inside an accept when the probe\n    // was inserted, ignore\n    return 0;\n  }\n\n  // Set up the child socket\n  struct tcp_connection_t *pconn = create_tcp_connection(ctx, newsk);\n  if (!pconn) {\n#if DEBUG_TCP_CONNECTION\n    DEBUG_PRINTK(\"inet_csk_accept(ret): couldn't create tcp_connection\");\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_CONNECTIONS, _tgid, (u64)newsk);\n    // Unlink the kprobe/kretprobe\n    DELETE_ARGS(inet_csk_accept);\n    return 0;\n  }\n\n  // Note that this is a listening socket\n  pconn->parent_sk = args->sk;\n\n#if TRACE_SOCKET_ACCEPT\n  DEBUG_PRINTK(\"                       accepted socket %llx\\n\", newsk);\n  DEBUG_PRINTK(\"inet_csk_accept exit:  pid_tgid %llu removed\\n\", _pid_tgid);\n#endif\n  // Unlink the kprobe/kretprobe\n  DELETE_ARGS(inet_csk_accept);\n\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_memory.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n//\n// bpf_memory.h - BPF memory related utility functions\n//\n\n#pragma once\n\n#include \"bpf_debug.h\"\n#include \"bpf_types.h\"\n\n/*\n * Safe prefix compare for potentially untrusted pointers (kernel or user).\n * Copies up to 16 bytes from s1 via helper before comparing to s2 literal.\n * Intended for 5.4-era verifier quirks and mixed user/kernel buffers.\n */\nstatic __always_inline int string_starts_with(const char *s1, const size_t s1_len, const char *s2, const size_t s2_len)\n{\n  if (s2_len == 0) {\n    return 1;\n  }\n  if (s2_len > 16) {\n    return 0;\n  }\n  if (s1_len < s2_len) {\n    return 0;\n  }\n\n  char local[16] = {};\n  if (bpf_probe_read(local, (u32)s2_len, s1) != 0) {\n    return 0;\n  }\n\n  int n = (int)s2_len; // s2_len <= 16 guaranteed\n  for (int i = 0; i < n; i++) {\n    if (local[i] != s2[i]) {\n      return 0;\n    }\n  }\n  return 1;\n}\n\nstatic __always_inline int char_to_number(char x)\n{\n  if (x < '0' || x > '9')\n    return -1;\n  return (int)(x - '0');\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_tcp_events.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#ifdef STANDALONE_TCP_PROCESSOR\n\n// Only used by standalone debugging (tcp-processor.py)\n///////////////////////////////////////////////////////\n\n// TCP Event Types\n#define TCP_EVENT_TYPE u32\n#define TCP_EVENT_TYPE_HTTP_RESPONSE ((TCP_EVENT_TYPE)0)\n#define TCP_EVENT_TYPE_TCP_DATA ((TCP_EVENT_TYPE)1)\n\n// Format of tcp_events perf buffer messages\nstruct tcp_events_t {\n  TCP_EVENT_TYPE type; // TCP_EVENT_TYPE\n  TGID pid;            // Userland process id related to this event\n  TIMESTAMP ts;        // Timestamp\n  u64 sk;              // Socket pointer related to this event\n  union {\n    struct {        // HTTP Reponse events data\n      u16 code;     // Response status code\n      u8 dir;       // client/server direction (0=client)\n      u8 __pad0[5]; // 64 bit align\n      u64 latency;  // request/response latency in ns\n    } http_response;\n    struct {         // TCP Data events data\n      u32 length;    // Length of chunk\n      u8 streamtype; // STREAM_TYPE\n      u8 is_server;  // CLIENT_SERVER_TYPE\n      u16 __pad0;    // 64 bit align\n      u64 offset;    // Position in the stream\n    } tcp_data;\n    struct {\n      u64 __pad0; // Padding to ensure length is multiple of 8 bytes\n      u64 __pad1; // Padding to ensure length is multiple of 8 bytes\n    } __align;\n  };\n};\n\n// The perf event array map for libbpf\nstruct {\n  __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);\n  __uint(key_size, sizeof(__u32));\n  __uint(value_size, sizeof(__u32));\n} tcp_events SEC(\".maps\");\n\n#endif\n\n// Utility functions\n\nstatic __always_inline void\ntcp_events_submit_http_response(struct pt_regs *ctx, struct sock *sk, u16 code, u64 latency, enum CLIENT_SERVER_TYPE dir)\n{\n  GET_PID_TGID\n  u64 now = get_timestamp();\n\n#ifdef STANDALONE_TCP_PROCESSOR\n\n  struct tcp_events_t event = {\n      .type = TCP_EVENT_TYPE_HTTP_RESPONSE, .pid = _tgid, .ts = now, .sk = (u64)sk, .__align.__pad0 = 0, .__align.__pad1 = 0};\n  event.http_response.code = code;\n  event.http_response.dir = (u8)dir;\n  event.http_response.latency = latency;\n\n  bpf_perf_event_output(ctx, &tcp_events, BPF_F_CURRENT_CPU, &event, sizeof(event));\n\n#else\n\n  perf_submit_agent_internal__http_response(ctx, now, (u64)sk, _tgid, code, latency, dir);\n#endif\n}\n\nstatic __always_inline void tcp_events_submit_tcp_data(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    enum STREAM_TYPE streamtype,\n    enum CLIENT_SERVER_TYPE is_server,\n    const void *data,\n    size_t data_len)\n{\n\n  // submit data to data channel\n  size_t actual_len;\n  data_channel_submit(ctx, pconn, data, data_len, &actual_len);\n\n  // now send render message announcing its existence\n  u64 now = get_timestamp();\n\n#if DEBUG_TCP_DATA\n  bpf_trace_printk(\"tcp_events_submit_tcp_data: tstamp=%u sk=%llx streamtype=%d\\n\", now, pconn->sk, streamtype);\n  bpf_trace_printk(\"                  is_server=%d, data_len=%u\\n\", is_server, data_len);\n#endif\n\n  GET_PID_TGID;\n\n#ifdef STANDALONE_TCP_PROCESSOR\n\n  struct tcp_events_t event = {\n      .type = TCP_EVENT_TYPE_TCP_DATA, .pid = _tgid, .ts = now, .sk = (u64)pconn->sk, .__align.__pad0 = 0, .__align.__pad1 = 0};\n  event.tcp_data.length = actual_len;\n  event.tcp_data.streamtype = (u8)streamtype;\n  event.tcp_data.is_server = (u8)is_server;\n  event.tcp_data.offset = pconn->streams[streamtype].total;\n\n  bpf_perf_event_output(ctx, &tcp_events, BPF_F_CURRENT_CPU, &event, sizeof(event));\n\n#else\n\n  // Submit the control perf ring tcp data message\n  perf_submit_agent_internal__tcp_data(\n      ctx, now, (u64)pconn->sk, _tgid, actual_len, pconn->streams[streamtype].total, streamtype, is_server);\n\n#endif\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_tcp_processor.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifdef STANDALONE_TCP_PROCESSOR\n\n#define KBUILD_MODNAME \"bpf_http\"\n#include \"../render_bpf.h\"\n#include <vmlinux.h>\n\n// Include this from render_bpf.c so we can use tail calls in tcp processor test code\nstruct {\n  __uint(type, BPF_MAP_TYPE_PROG_ARRAY);\n  __uint(max_entries, NUM_TAIL_CALLS);\n  __uint(key_size, sizeof(__u32));\n  __uint(value_size, sizeof(__u32));\n} tail_calls SEC(\".maps\");\n\n#endif\n\n////////////////////////////////////////////////////////////////////////////\n// Utilities\n\n#include \"bpf_debug.h\"\n#include \"bpf_memory.h\"\n#include \"bpf_types.h\"\n\n////////////////////////////////////////////////////////////////////////////\n// Subcomponents\n\n#include \"tcp_processor.h\"\n\n// TCP socket handling\n#include \"bpf_tcp_socket.h\"\n\n// Connection accept handling\n#include \"bpf_inet_csk_accept.h\"\n\n// TCP send/receive handling\n\nstatic __always_inline void tcp_client_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len);\nstatic __always_inline void tcp_server_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len);\n#define TCP_CLIENT_HANDLER(CTX, CONN, CTRL, ST, DATA, LEN) tcp_client_handler(CTX, CONN, CTRL, ST, DATA, LEN)\n#define TCP_SERVER_HANDLER(CTX, CONN, CTRL, ST, DATA, LEN) tcp_server_handler(CTX, CONN, CTRL, ST, DATA, LEN)\n\n#include \"bpf_tcp_send_recv.h\"\n\n// Data channel output\n#include \"bpf_data_channel.h\"\n\n// TCP events output\n#include \"bpf_tcp_events.h\"\n\n#ifndef ENABLE_TCP_DATA_STREAM\n// HTTP protocol handling\n#include \"bpf_http_protocol.h\"\n#endif\n\n//////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic __always_inline void tcp_client_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len)\n{\n\n#pragma passthrough on\n#if ENABLE_TCP_DATA_STREAM\n#pragma passthrough off\n\n  tcp_events_submit_tcp_data(ctx, pconn, streamtype, 0, data, data_len);\n\n#pragma passthrough on\n#else\n#pragma passthrough off\n\n  struct tcp_protocol_state_t *state = &(pconn->protocol_state);\n  struct tcp_stream_info_t *strm = pconn->streams + (int)streamtype;\n\n  // Perform protocol detection\n  if (state->protocol == TCPPROTO_UNKNOWN) {\n    // Detect HTTP\n    if (state->candidates & TCP_PROTOCOL_BIT(TCPPROTO_HTTP)) {\n\n      enum TCP_PROTOCOL_DETECT_RESULT res = http_detect(ctx, pconn, pctrl, streamtype, data, data_len);\n      if (res == TPD_FAILED) {\n        state->candidates &= ~TCP_PROTOCOL_BIT(TCPPROTO_HTTP);\n      } else if (res == TPD_SUCCESS) {\n        state->protocol = TCPPROTO_HTTP;\n      }\n    }\n\n    // If no candidates left, no need to process anything else\n    if (state->candidates == 0) {\n      enable_tcp_connection(pctrl, -1, -1);\n      return;\n    }\n  }\n\n  switch (state->protocol) {\n  case TCPPROTO_UNKNOWN:\n    // If we don't know the protocol, don't process this at all\n    return;\n  case TCPPROTO_HTTP:\n    http_process_request(ctx, pconn, pctrl, streamtype, data, data_len);\n    break;\n  default:\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n    break;\n  }\n\n#pragma passthrough on\n#endif\n#pragma passthrough off\n}\n\nstatic __always_inline void tcp_server_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len)\n{\n#pragma passthrough on\n#if ENABLE_TCP_DATA_STREAM\n#pragma passthrough off\n\n  tcp_events_submit_tcp_data(ctx, pconn, streamtype, 1, data, data_len);\n\n#pragma passthrough on\n#else\n#pragma passthrough off\n\n  struct tcp_protocol_state_t *state = &(pconn->protocol_state);\n\n  // Parse the protocol\n  switch (state->protocol) {\n  case TCPPROTO_UNKNOWN:\n    // If we don't know the protocol, don't process this at all\n    return;\n  case TCPPROTO_HTTP:\n    http_process_response(ctx, pconn, pctrl, streamtype, data, data_len);\n    break;\n  default:\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n    break;\n  }\n\n#pragma passthrough on\n#endif\n#pragma passthrough off\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_tcp_send_recv.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n//\n// bpf_tcp_send_recv.h - BPF TCP socket streaming receive/send buffering\n//\n// Requires:\n//   To declare a tcp client handler, #define TCP_CLIENT_HANDLER(...)\n//   To declare a tcp server handler, #define TCP_SERVER_HANDLER(...)\n\n#pragma once\n\n#include \"bpf_debug.h\"\n#include \"bpf_memory.h\"\n#include \"bpf_tcp_socket.h\"\n#include \"bpf_types.h\"\n\n#ifndef TCP_CLIENT_HANDLER\n#error \"Must define TCP_CLIENT_HANDLER\"\n#endif\n#ifndef TCP_SERVER_HANDLER\n#error \"Must define TCP_SERVER_HANDLER\"\n#endif\n\n////////////////////////////////////////////////////////////////////////////\n// TCP Receive\n\nBEGIN_DECLARE_SAVED_ARGS(tcp_recvmsg)\nstruct sock *sk;\nstruct msghdr *msg;\nsize_t len;\nint nonblock;\nint flags;\n// int *addr_len;\n// decoded msg\nvoid *iov_base;\nsize_t iov_len;\nint written;\nint depth;\nEND_DECLARE_SAVED_ARGS(tcp_recvmsg)\n\n////////////////////////////////////////////////////////////////////////////\n// TCP Send\n\nBEGIN_DECLARE_SAVED_ARGS(tcp_sendmsg)\nstruct sock *sk;\nstruct msghdr *msg;\nsize_t size;\n// decoded msg\nstruct iovec *iov;\nvoid *iov_ptr;\nsize_t iov_len;\nsize_t iov_offset;\nunsigned long nr_segs;\nint written;\nint depth;\nEND_DECLARE_SAVED_ARGS(tcp_sendmsg)\n\n////////////////////////////////////////////////////////////////////////////\n// Send / Receive Stream Handlers\n// Processes data that ends up in the send and receive streams\n// to determine protocols and deal with them.\n\n////////////////////////////////////////////////////////////////////////////\n// Helpers: CO-RE safe iov_iter checks and resolution\n\n/*\n * Returns true if msg->msg_iter designates ITER_IOVEC or ITER_KVEC across\n * kernel versions. Uses enum CO-RE relocation to avoid numeric constants.\n * Optionally returns the iter type value for logging (if type_out!=NULL).\n */\nstatic __always_inline bool msg_iter_is_iov_or_kvec(const struct msghdr *msg, unsigned int *type_out)\n{\n  // Default iter type value for logging\n  unsigned int iter_type = 0;\n\n  // Require enum values to exist; otherwise, return false (non-permissive)\n  bool has_iov = bpf_core_enum_value_exists(enum iter_type, ITER_IOVEC);\n  bool has_kvec = bpf_core_enum_value_exists(enum iter_type, ITER_KVEC);\n  if (!(has_iov && has_kvec)) {\n    if (type_out)\n      *type_out = iter_type;\n    return false;\n  }\n  __u64 ev_iov = bpf_core_enum_value(enum iter_type, ITER_IOVEC);\n  __u64 ev_kvec = bpf_core_enum_value(enum iter_type, ITER_KVEC);\n\n  // Prefer ordinal-era field (>= 5.14): iter_type is an enum-ordinal (u8)\n  if (bpf_core_field_exists(((struct msghdr *)0)->msg_iter.iter_type)) {\n    __u8 it = BPF_CORE_READ(msg, msg_iter.iter_type);\n    iter_type = it;\n\n    if (type_out)\n      *type_out = iter_type;\n    return it == ev_iov || it == ev_kvec;\n  }\n\n  // Bitmask-era (<= 5.13): use compat msghdr to read iov_iter.type\n  struct msghdr___5_13_19 *msg_compat = (void *)msg;\n  if (msg_compat) {\n    if (bpf_probe_read_kernel(&iter_type, sizeof(iter_type), &msg_compat->msg_iter.type) == 0) {\n      if (type_out)\n        *type_out = iter_type;\n      unsigned int mask = (unsigned int)(ev_iov | ev_kvec);\n      return (iter_type & mask) != 0;\n    }\n  }\n\n  // If all else fails, return false and pass through iter_type (0)\n  if (type_out)\n    *type_out = iter_type;\n  return false;\n}\n\n// Resolve iovec pointer across kernel versions: use __iov if present (6.4+),\n// else fall back to iov (<= 6.3). Layout matches kvec, so it's valid either way.\nstatic __always_inline struct iovec *msg_iter_get_iov(struct msghdr *msg)\n{\n  if (bpf_core_field_exists(((struct msghdr *)0)->msg_iter.__iov)) {\n    return (struct iovec *)BPF_CORE_READ(msg, msg_iter.__iov);\n  }\n  return (struct iovec *)BPF_CORE_READ((struct msghdr___5_13_19 *)msg, msg_iter.iov);\n}\n\n/*\n * Returns true if msg->msg_iter designates ITER_UBUF on kernels that support it.\n * Uses CO-RE field/enum relocation to avoid relying on numeric enum values.\n *\n * Behavior across eras:\n * - <=5.13: no iter_type field and no UBUF; returns false.\n * - 5.14–5.19: iter_type exists but UBUF not present; returns false.\n * - >=6.0: iter_type exists and ITER_UBUF may exist; compare ordinal safely.\n */\nstatic __always_inline bool iter_is_ubuf(const struct msghdr *msg)\n{\n  if (!bpf_core_field_exists(((struct msghdr *)0)->msg_iter.iter_type))\n    return false; // bitmask-era: no UBUF\n\n  if (!bpf_core_enum_value_exists(enum iter_type, ITER_UBUF))\n    return false; // target kernel lacks UBUF support\n\n  __u8 it = BPF_CORE_READ(msg, msg_iter.iter_type);\n  __u64 ev_ubuf = bpf_core_enum_value(enum iter_type, ITER_UBUF);\n  return it == ev_ubuf;\n}\n\n/*\n * Resolve UBUF as an iovec-like (base,len) pair across kernel versions.\n * - 6.4+: prefer __ubuf_iovec overlay (base/len). iov_offset is outside overlay.\n * - 6.0–6.3: use ubuf pointer and count fields. Caller may apply iov_offset.\n */\nstatic __always_inline void ubuf_as_iovec(const struct msghdr *msg, void **out_base, size_t *out_len)\n{\n  // 6.4+ overlay: exposes base/len as struct iovec\n  if (bpf_core_field_exists(((struct msghdr *)0)->msg_iter.__ubuf_iovec)) {\n    void *base = BPF_CORE_READ(msg, msg_iter.__ubuf_iovec.iov_base);\n    size_t len = BPF_CORE_READ(msg, msg_iter.__ubuf_iovec.iov_len);\n    *out_base = base;\n    *out_len = len;\n    return;\n  }\n\n  // 6.0–6.3: plain ubuf pointer + count\n  void *ubuf = BPF_CORE_READ(msg, msg_iter.ubuf);\n  size_t cnt = BPF_CORE_READ(msg, msg_iter.count);\n  *out_base = ubuf;\n  *out_len = cnt;\n}\n\nstatic __always_inline void tcp_send_stream_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len)\n{\n  // Are we an accepting socket, or an originating socket?\n  if (pconn->parent_sk) {\n    // Accepting sockets send from server\n    TCP_SERVER_HANDLER(ctx, pconn, pctrl, streamtype, data, data_len);\n  } else {\n    // Originating sockets send from clients\n    TCP_CLIENT_HANDLER(ctx, pconn, pctrl, streamtype, data, data_len);\n  }\n}\n\nstatic __always_inline void tcp_recv_stream_handler(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    struct tcp_control_value_t *pctrl,\n    enum STREAM_TYPE streamtype,\n    const void *data,\n    size_t data_len)\n{\n  // Are we an accepting socket, or an originating socket?\n  if (pconn->parent_sk) {\n    // Accepting sockets receive from clients\n    TCP_CLIENT_HANDLER(ctx, pconn, pctrl, streamtype, data, data_len);\n  } else {\n    // Originating sockets receive from servers\n    TCP_SERVER_HANDLER(ctx, pconn, pctrl, streamtype, data, data_len);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////\n\n// --- tcp_sendmsg ----------------------------------------------------\n// Called when data is to be send to a TCP socket\n\nSEC(\"kprobe/tcp_sendmsg\")\n__attribute__((noinline)) int handle_kprobe__tcp_sendmsg(struct pt_regs *ctx)\n{\n  // In post 4.1 kernels: struct sock *sk, struct msghdr *msg, size_t size\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx);\n  size_t size = (size_t)PT_REGS_PARM3(ctx);\n\n  GET_PID_TGID\n\n  if (!msg || !sk) {\n    return 0;\n  }\n\n  struct tcp_connection_t *pconn;\n  pconn = lookup_tcp_connection(sk);\n  if (!pconn) {\n    // For now ignore sends on sockets we haven't seen the tcp_init_sock for\n    // #if TRACE_TCP_SEND\n    //    DEBUG_PRINTK(\"tcp_sendmsg found no tcp connection in kprobe\\n\");\n    // #endif\n    return 0;\n  }\n  struct tcp_control_value_t *pctrl = get_tcp_control(pconn);\n  if (!pctrl) {\n    return 0;\n  }\n\n  // Quick ignore for sockets we deem uninteresting\n\n#ifndef ENABLE_TCP_DATA_STREAM\n  if (pconn->protocol_state.candidates == 0) {\n#if TRACE_TCP_SEND\n    DEBUG_PRINTK(\"no candidates left on tcp_sendmsg\\n\");\n#endif\n    return 0;\n  }\n  if (pconn->streams[ST_SEND].protocol_count == 0) {\n#if TRACE_TCP_SEND\n    DEBUG_PRINTK(\"no protocols interested in tcp_sendmsg\\n\");\n#endif\n    return 0;\n  }\n#endif\n\n  if (pctrl->streams[ST_SEND].enable == 0) {\n#if TRACE_TCP_SEND\n    DEBUG_PRINTK(\"send stream is disabled for sk=%llx\\n\", (u64)sk);\n#endif\n    return 0;\n  }\n\n  // decode msg\n  struct iovec *iov = NULL;\n  unsigned long nr_segs = 0;\n  size_t iov_offset = 0;\n  unsigned int iter_type = 0;\n  void *iov_ptr = NULL;\n  size_t iov_len = 0;\n  int written = 0;\n  int depth = 1;\n\n  if (iter_is_ubuf(msg)) {\n    // ITER_UBUF: treat as a single contiguous segment\n    ubuf_as_iovec(msg, &iov_ptr, &iov_len);\n  } else if (msg_iter_is_iov_or_kvec(msg, &iter_type)) {\n    // IOVEC/KVEC: can access through iov since both share layout\n    iov = (struct iovec *)msg_iter_get_iov(msg);\n    if (iov) {\n      iov_ptr = BPF_CORE_READ(iov, iov_base);\n      iov_len = BPF_CORE_READ(iov, iov_len);\n    }\n  } else {\n#if DEBUG_TCP_SEND\n    DEBUG_PRINTK(\"unsupported iov type: %u\\n\", iter_type);\n#endif\n    bpf_log(ctx, BPF_LOG_UNSUPPORTED_IO, (u64)ST_SEND, (u64)sk, (u64)iter_type);\n    return 0;\n  }\n  nr_segs = BPF_CORE_READ(msg, msg_iter.nr_segs);\n  iov_offset = BPF_CORE_READ(msg, msg_iter.iov_offset);\n\n  // Defer arguments to kretprobe\n  BEGIN_SAVE_ARGS(tcp_sendmsg)\n  SAVE_ARG(sk)\n  SAVE_ARG(msg)\n  SAVE_ARG(size)\n  // decoded msg structure\n  SAVE_ARG(iov)\n  SAVE_ARG(iov_ptr)\n  SAVE_ARG(iov_len)\n  SAVE_ARG(iov_offset)\n  SAVE_ARG(nr_segs)\n  SAVE_ARG(written)\n  SAVE_ARG(depth)\n  END_SAVE_ARGS(tcp_sendmsg)\n\n#if TRACE_TCP_SEND\n  DEBUG_PRINTK(\n      \"tcp_sendmsg enter: pid %u request to send up to %u bytes \"\n      \"on socket %llx\\n\",\n      pconn->upid,\n      (unsigned int)size,\n      sk);\n  DEBUG_PRINTK(\"                   nr_segs=%lu, iov_offset=%lu\\n\", nr_segs, iov_offset);\n#endif\n\n  return 0;\n}\n\nSEC(\"kretprobe/tcp_sendmsg\")\nint handle_kretprobe__tcp_sendmsg(struct pt_regs *ctx)\n{\n  // This call recurses up to TCP_TAIL_CALL_MAX_DEPTH times,\n  // writing up to DATA_CHANNEL_CHUNK_MAX each time\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_CONTINUE_TCP_SENDMSG);\n  return 0;\n}\n\nSEC(\"kprobe\")\nint continue_tcp_sendmsg(struct pt_regs *ctx)\n{\n  GET_PID_TGID\n\n  GET_ARGS_MISSING_OK(tcp_sendmsg, args)\n  if (args == NULL) {\n    return 0;\n  }\n\n  // Get copied byte count\n  int copied = (int)PT_REGS_RC(ctx);\n  if (copied <= 0) {\n    DELETE_ARGS(tcp_sendmsg);\n    return 0;\n  }\n\n  // Lookup our connection\n  struct tcp_connection_t *pconn;\n  pconn = lookup_tcp_connection(args->sk);\n  if (!pconn) {\n    // Socket was destroyed -during- tcp_sendmsg (happens on some control flow paths?)\n#if DEBUG_TCP_SEND\n    DEBUG_PRINTK(\"tcp_sendmsg found no tcp connection in kretprobe\\n\");\n    bpf_log(ctx, BPF_LOG_TABLE_MISSING_KEY, BPF_TABLE_TCP_CONNECTIONS, (u64)args->sk, _tgid);\n#endif\n    DELETE_ARGS(tcp_sendmsg);\n    return 0;\n  }\n\n  if (args->iov_len == 0) {\n    // Load next iovec\n    if (args->nr_segs > 0) {\n      void *iov_ptr = args->iov_ptr;\n      size_t iov_len = args->iov_len;\n#if TRACE_TCP_SEND\n      DEBUG_PRINTK(\"tcp_sendmsg continue: ptr=%llx, len=%d\\n\", iov_ptr, iov_len);\n#endif\n      args->iov_ptr = iov_ptr;\n      args->iov_len = iov_len;\n      args->iov++;\n      args->nr_segs--;\n      if (args->iov_offset) {\n        // Data in the first iovec can have an offset to the first byte\n        args->iov_ptr += args->iov_offset;\n        args->iov_offset = 0;\n      }\n    } else {\n      DELETE_ARGS(tcp_sendmsg);\n      return 0;\n    }\n  }\n\n  const u8 *data = (const u8 *)args->iov_ptr;\n  int remaining = copied - args->written;\n  int to_copy = remaining > DATA_CHANNEL_CHUNK_MAX ? DATA_CHANNEL_CHUNK_MAX : remaining;\n\n  if (to_copy > args->iov_len) {\n    to_copy = args->iov_len;\n  }\n\n  write_to_tcp_stream(ctx, pconn, ST_SEND, data, to_copy, tcp_send_stream_handler);\n\n  args->written += to_copy;\n  args->iov_ptr += to_copy;\n  args->iov_len -= to_copy;\n\n  if (to_copy == remaining || args->depth == TCP_TAIL_CALL_MAX_DEPTH) {\n    DELETE_ARGS(tcp_sendmsg);\n  } else {\n    args->depth++;\n    bpf_tail_call(ctx, &tail_calls, TAIL_CALL_CONTINUE_TCP_SENDMSG);\n  }\n\n  return 0;\n}\n\n// --- tcp_recvmsg ----------------------------------------------------\n// Called when data is to be received by a TCP socket\n\nSEC(\"kprobe/tcp_recvmsg\")\nint handle_kprobe__tcp_recvmsg(struct pt_regs *ctx)\n{\n  // In kernels 4.1 onwards: struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n  struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx);\n  size_t len = (size_t)PT_REGS_PARM3(ctx);\n  int nonblock = (int)PT_REGS_PARM4(ctx);\n  int flags = (int)PT_REGS_PARM5(ctx);\n  // int *addr_len = NULL;   -- unused\n\n  GET_PID_TGID\n\n  struct tcp_connection_t *pconn;\n  pconn = lookup_tcp_connection(sk);\n  if (!pconn) {\n    // Ignore receives on sockets we haven't seen the tcp_init_sock for\n    // #if TRACE_TCP_RECEIVE\n    //    DEBUG_PRINTK(\"tcp_recvmsg found no tcp connection in kprobe\\n\");\n    // #endif\n    return 0;\n  }\n  struct tcp_control_value_t *pctrl = get_tcp_control(pconn);\n  if (!pctrl) {\n    return 0;\n  }\n\n  // Quick ignore for sockets we deem uninteresting\n\n#ifndef ENABLE_TCP_DATA_STREAM\n  if (pconn->protocol_state.candidates == 0) {\n#if TRACE_TCP_RECEIVE\n    DEBUG_PRINTK(\"no candidates left on tcp_recvmsg\\n\");\n#endif\n    return 0;\n  }\n  if (pconn->streams[ST_RECV].protocol_count == 0) {\n#if TRACE_TCP_RECEIVE\n    DEBUG_PRINTK(\"no protocols interested in tcp_recvmsg\\n\");\n#endif\n    return 0;\n  }\n#endif\n\n  if (pctrl->streams[ST_RECV].enable == 0) {\n#if TRACE_TCP_RECEIVE\n    DEBUG_PRINTK(\"recv stream is disabled for sk=%llx\\n\", (u64)sk);\n#endif\n    return 0;\n  }\n\n  // decode msg\n  struct iovec *iov = NULL;\n  void *iov_base = NULL;\n  size_t iov_len = 0;\n  int written = 0;\n  int depth = 1;\n  unsigned int iter_type = 0;\n\n  if (iter_is_ubuf(msg)) {\n    // ITER_UBUF: read as iovec-like values\n    ubuf_as_iovec(msg, &iov_base, &iov_len);\n  } else if (msg_iter_is_iov_or_kvec(msg, &iter_type)) {\n    // IOVEC/KVEC: can access through iov since both share layout\n    iov = (struct iovec *)msg_iter_get_iov(msg);\n    iov_base = BPF_CORE_READ(iov, iov_base);\n    iov_len = BPF_CORE_READ(iov, iov_len);\n  } else {\n#if DEBUG_TCP_RECEIVE\n    DEBUG_PRINTK(\"unsupported iov type: %u\\n\", iter_type);\n#endif\n    bpf_log(ctx, BPF_LOG_UNSUPPORTED_IO, (u64)ST_RECV, (u64)sk, (u64)iter_type);\n    return 0;\n  }\n\n  // Add to receiver table\n  BEGIN_SAVE_ARGS(tcp_recvmsg)\n  SAVE_ARG(sk)\n  SAVE_ARG(msg)\n  SAVE_ARG(len)\n  SAVE_ARG(nonblock)\n  SAVE_ARG(flags)\n  // SAVE_ARG(addr_len)\n  // decoded msg structure\n  SAVE_ARG(iov_base)\n  SAVE_ARG(iov_len)\n  SAVE_ARG(written)\n  SAVE_ARG(depth)\n  END_SAVE_ARGS(tcp_recvmsg)\n\n#if TRACE_TCP_RECEIVE\n  DEBUG_PRINTK(\n      \"tcp_recvmsg enter: pid %u request to receive up to %u \"\n      \"bytes on socket %llx\\n\",\n      pconn->upid,\n      (unsigned int)len,\n      sk);\n  DEBUG_PRINTK(\"                   iov_base=%llx, iov_len=%d\\n\", iov_base, iov_len);\n\n#endif\n\n  return 0;\n}\n\nSEC(\"kretprobe/tcp_recvmsg\")\nint handle_kretprobe__tcp_recvmsg(struct pt_regs *ctx)\n{\n  // This call recurses up to TCP_TAIL_CALL_MAX_DEPTH times,\n  // writing up to DATA_CHANNEL_CHUNK_MAX each time\n  bpf_tail_call(ctx, &tail_calls, TAIL_CALL_CONTINUE_TCP_RECVMSG);\n  return 0;\n}\n\nSEC(\"kprobe\")\nint continue_tcp_recvmsg(struct pt_regs *ctx)\n{\n  GET_PID_TGID\n\n  GET_ARGS_MISSING_OK(tcp_recvmsg, args)\n  if (args == NULL) {\n    return 0;\n  }\n\n  // Get copied byte count\n  int copied = (int)PT_REGS_RC(ctx);\n  if (copied <= 0) {\n    DELETE_ARGS(tcp_recvmsg);\n    return 0;\n  }\n\n  // Lookup our connection\n  struct tcp_connection_t *pconn;\n  pconn = lookup_tcp_connection(args->sk);\n  if (!pconn) {\n    // Socket was destroyed -during- tcp_recvmsg (happens on some control flow paths?)\n#if DEBUG_TCP_RECEIVE\n    DEBUG_PRINTK(\"tcp_recvmsg found no tcp connection in kretprobe\\n\");\n    bpf_log(ctx, BPF_LOG_TABLE_MISSING_KEY, BPF_TABLE_TCP_CONNECTIONS, (u64)args->sk, _tgid);\n#endif\n    DELETE_ARGS(tcp_recvmsg);\n    return 0;\n  }\n\n  const u8 *data = ((const u8 *)args->iov_base) + args->written;\n  int remaining = copied - args->written;\n  int to_copy = remaining > DATA_CHANNEL_CHUNK_MAX ? DATA_CHANNEL_CHUNK_MAX : remaining;\n\n  write_to_tcp_stream(ctx, pconn, ST_RECV, data, to_copy, tcp_recv_stream_handler);\n\n  args->written += to_copy;\n\n  if (to_copy == remaining || args->depth == TCP_TAIL_CALL_MAX_DEPTH) {\n    DELETE_ARGS(tcp_recvmsg);\n  } else {\n    args->depth++;\n    bpf_tail_call(ctx, &tail_calls, TAIL_CALL_CONTINUE_TCP_RECVMSG);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_tcp_socket.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n//\n// bpf_tcp_connection.h - TCP socket tracking\n//\n// Requires:\n//   Define list of protocols prior to including this file as well and #define\n//   index type to TCP_PROTOCOL_TYPE and #define the number of protocols to\n//   TCP_PROTOCOL_COUNT, and TCP_PROTOCOL_MASK to (1<<TCP_PROTOCOL_COUNT)-1\n//   Set the TCP socket buffer size, #define TCP_SOCKET_BUFFER_SIZE, which\n//   must be a multiple of 8 bytes\n\n#include \"bpf_debug.h\"\n#include \"bpf_types.h\"\n\n#ifndef TCP_CONNECTION_HASH_SIZE\n#error \"Must define a hash size for tcp_connections table\"\n#endif\n\n// Must be the maximum size required for any protocol decoding\n#define TCP_SOCKET_PROTOCOL_STATE_SIZE 16\n\n// Verify there is a TCP_PROTOCOL_TYPE type\n#ifndef TCP_PROTOCOL_TYPE\n#define TCP_PROTOCOL_TYPE u32\n#endif\n\n#ifndef TCP_PROTOCOL_COUNT\n#error \"Must define a number of potential TCP protocols to detect\"\n#endif\n\nstruct tcp_stream_info_t {\n#ifndef ENABLE_TCP_DATA_STREAM\n  u16 protocol_count; // # of protocols that care about this data (0 means\n                      // we can stop parsing the data)\n  u16 _pad0;\n  u32 _pad1;\n#endif\n  u64 total; // # of bytes transmitted on this socket in this direction\n};\n\nstruct tcp_protocol_state_t {\n  TCP_PROTOCOL_TYPE protocol;              // detected protocol\n  u32 candidates;                          // bit mask of candidate protocols (0 bit = disqualified\n                                           // candidate, 1 bit=possible candidate)\n  u8 data[TCP_SOCKET_PROTOCOL_STATE_SIZE]; // internal data used by protocol\n};\n\n// TCP Connection Data\nstruct tcp_connection_t {\n  struct sock *sk;                     // the socket we're keyed to\n  struct sock *parent_sk;              // parent listen socket if this an accepted socket\n  TGID upid;                           // userland PID of this connection\n  struct tcp_stream_info_t streams[2]; // state tracking for send and receive streams\n#ifndef ENABLE_TCP_DATA_STREAM\n  struct tcp_protocol_state_t protocol_state; // protocol detection state\n#endif\n};\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct sock *);\n  __type(value, struct tcp_connection_t);\n  __uint(max_entries, TCP_CONNECTION_HASH_SIZE);\n} _tcp_connections SEC(\".maps\");\n\nstruct {\n  __uint(type, BPF_MAP_TYPE_HASH);\n  __type(key, struct tcp_control_key_t);\n  __type(value, struct tcp_control_value_t);\n  __uint(max_entries, TCP_CONNECTION_HASH_SIZE);\n} _tcp_control SEC(\".maps\");\n\n//\n// TCP Connection Lifecycle Management\n//\n\nstatic __always_inline struct tcp_connection_t *lookup_tcp_connection(struct sock *sk)\n{\n  // #if TRACE_TCP_CONNECTION\n  //   DEBUG_PRINTK(\"tcp_connections.lookup(%llx)\\n\", sk);\n  // #endif\n\n  struct tcp_connection_t *pconn = bpf_map_lookup_elem(&_tcp_connections, &sk);\n  return pconn;\n}\n\nstatic struct tcp_connection_t *create_tcp_connection(struct pt_regs *ctx, struct sock *sk)\n{\n  struct tcp_connection_t *pconn = bpf_map_lookup_elem(&_tcp_connections, &sk);\n  if (pconn) {\n#if TCP_LIFETIME_HACK\n#if DEBUG_TCP_CONNECTION\n    DEBUG_PRINTK(\"create_tcp_connection: tcp_lifetime_hack\\n\");\n#endif\n    // xxx: disable this for now because we know it happens all the time and it's too chatty\n    // bpf_log(ctx, BPF_LOG_LIFETIME_HACK, BPF_TABLE_TCP_CONNECTIONS, (u64)sk, 0);\n    bpf_map_delete_elem(&_tcp_connections, &sk);\n#else\n    DEBUG_PRINTK(\"create_tcp_connection: socket already exists sk=%llx\\n\", sk);\n    return pconn;\n#endif\n  }\n\n#if TRACE_TCP_CONNECTION\n  DEBUG_PRINTK(\"create_tcp_connection(%llx)\\n\", sk);\n#endif\n\n  GET_PID_TGID;\n\n  struct tcp_connection_t zero = {};\n  zero.sk = sk;\n  zero.parent_sk = NULL;\n  zero.upid = _tgid;\n#ifndef ENABLE_TCP_DATA_STREAM\n  zero.protocol_state.candidates = TCP_PROTOCOL_MASK;\n  zero.streams[0].protocol_count = TCP_PROTOCOL_COUNT;\n  zero.streams[1].protocol_count = TCP_PROTOCOL_COUNT;\n#endif\n\n  struct tcp_control_key_t key = {.sk = (u64)sk};\n  struct tcp_control_value_t value = {\n      .streams[ST_SEND].enable = 1, .streams[ST_SEND].start = 0, .streams[ST_RECV].enable = 1, .streams[ST_RECV].start = 0};\n\n  pconn = bpf_map_lookup_elem(&_tcp_connections, &sk);\n  if (!pconn) {\n    bpf_map_update_elem(&_tcp_connections, &sk, &zero, BPF_NOEXIST);\n    pconn = bpf_map_lookup_elem(&_tcp_connections, &sk);\n  }\n  bpf_map_update_elem(&_tcp_control, &key, &value, BPF_ANY);\n\n  return pconn;\n}\n\nstatic __always_inline struct tcp_control_value_t *get_tcp_control(struct tcp_connection_t *pconn)\n{\n  struct tcp_control_key_t key = {.sk = (u64)pconn->sk};\n  struct tcp_control_value_t *pvalue = bpf_map_lookup_elem(&_tcp_control, &key);\n  return pvalue;\n}\n\n// Call this when we don't want to bother processing a tcp\n// connection any longer to minimize overhead\n// recv/send = -1 (ignore), 0 (unchanged), 1 (don't ignore)\nstatic __always_inline void enable_tcp_connection(struct tcp_control_value_t *pctrl, int recv, int send)\n{\n  // DEBUG_PRINTK(\"enable_tcp_connection: recv=%d, send=%d\\n\", recv, send);\n  if (send != 0) {\n    pctrl->streams[ST_SEND].enable = send < 0 ? 0 : 1;\n  }\n  if (recv != 0) {\n    pctrl->streams[ST_RECV].enable = recv < 0 ? 0 : 1;\n  }\n}\n\n// Call this when we're completely done with a tcp connection and want to\n// release the socket map\nstatic void delete_tcp_connection(struct pt_regs *ctx, struct tcp_connection_t *pconn, struct sock *sk)\n{\n#if TRACE_TCP_CONNECTION\n  DEBUG_PRINTK(\"delete_tcp_connection(%llx)\\n\", sk);\n#endif\n\n  // remove from kernel data structures\n  struct tcp_control_key_t key = {.sk = (u64)pconn->sk};\n\n  bpf_map_delete_elem(&_tcp_control, &key);\n\n  int ret = bpf_map_delete_elem(&_tcp_connections, &sk);\n  if (ret != 0) {\n#if DEBUG_TCP_CONNECTION\n    DEBUG_PRINTK(\"delete_tcp_connection: delete on non-existent socket sk=%llx\\n\", sk);\n#endif\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_REMOVE, BPF_TABLE_TCP_CONNECTIONS, (u64)sk, 0);\n  }\n}\n\nstatic __always_inline void write_to_tcp_stream(\n    struct pt_regs *ctx,\n    struct tcp_connection_t *pconn,\n    enum STREAM_TYPE streamtype,\n    const void *src_data,\n    size_t src_bytes,\n    void (*tcp_stream_handler)(\n        struct pt_regs *ctx, struct tcp_connection_t *, struct tcp_control_value_t *, enum STREAM_TYPE, const void *, size_t))\n{\n  struct tcp_stream_info_t *strm = pconn->streams + (int)streamtype;\n  struct tcp_control_value_t *pctrl = get_tcp_control(pconn);\n  if (!pctrl) {\n    bpf_log(ctx, BPF_LOG_UNREACHABLE, 0, 0, 0);\n    return;\n  }\n  u64 start = pctrl->streams[streamtype].start;\n\n  // If the entirety of what we are going to write is\n  // before the window then we can skip this data\n  if ((strm->total + src_bytes) <= start) {\n    // just add the stream total without calling the callback\n    strm->total += src_bytes;\n    return;\n  }\n\n  u64 data_len = src_bytes;\n  u8 *data = (u8 *)src_data;\n\n  // If what we are about to write starts before the window, clip to the front\n  // end of the window\n  if (strm->total < start) {\n    size_t trim_len = (start - strm->total);\n    data_len -= trim_len;\n    data += trim_len;\n    strm->total = start;\n  }\n\n  // Inspect the buffer\n  tcp_stream_handler(ctx, pconn, pctrl, streamtype, data, data_len);\n\n  // Advance total\n  strm->total += data_len;\n}\n\n//\n// BPF Probes for intercepting the creation and destruction of TCP sockets\n//\n\n// --- tcp_init_sock ----------------------------------------------------\n// Where the start of TCP socket lifetimes is for IPv4 and IPv6\nSEC(\"kprobe/tcp_init_sock\")\nint handle_kprobe__tcp_init_sock(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n\n  struct tcp_connection_t *pconn;\n  pconn = create_tcp_connection(ctx, sk);\n  if (!pconn) {\n#if DEBUG_TCP_CONNECTION\n    DEBUG_PRINTK(\"tcp_init_sock: couldn't create tcp_connection\");\n#endif\n    GET_PID_TGID;\n\n    bpf_log(ctx, BPF_LOG_TABLE_BAD_INSERT, BPF_TABLE_TCP_CONNECTIONS, _tgid, (u64)sk);\n    return 0;\n  }\n  return 0;\n}\n\n// --- security_sk_free ----------------------------------------------\n// This is where final socket destruction happens for all socket types\nSEC(\"kprobe/security_sk_free\")\nint handle_kprobe__security_sk_free(struct pt_regs *ctx)\n{\n  struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);\n\n  struct tcp_connection_t *pconn = lookup_tcp_connection(sk);\n  if (pconn) {\n    delete_tcp_connection(ctx, pconn, sk);\n  }\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/bpf_types.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\ntypedef u64 PID_TGID;\ntypedef u32 TGID;\ntypedef u32 PID;\n\n// global variables for error counts\n// TODO: expose error counts to userspace\nvolatile long save_args_update_existing_error = 0;\nvolatile long save_args_update_full_error = 0;\n\n// tgid is upper 32 bits of pid_tgid, lower 32 bits is pid\n#define TGID_FROM_PID_TGID(X) ((TGID)((X) >> 32))\n#define PID_FROM_PID_TGID(X) ((PID)((X)))\n// common operation, use this macro to define _pid and _tgid for kprobes\n#define GET_PID_TGID                                                                                                           \\\n  PID_TGID _pid_tgid = bpf_get_current_pid_tgid();                                                                             \\\n  _pid_tgid &= 0xFFFFFFFFFFFFFFFF;                                                                                             \\\n  TGID _tgid = TGID_FROM_PID_TGID(_pid_tgid);                                                                                  \\\n  PID _pid = PID_FROM_PID_TGID(_pid_tgid);                                                                                     \\\n  if (_tgid == 0 || _pid == 0) {                                                                                               \\\n    bpf_log(ctx, BPF_LOG_INVALID_PID_TGID, _pid_tgid, 0, 0);                                                                   \\\n  }                                                                                                                            \\\n  u64 _cpu = bpf_get_smp_processor_id();\n\ntypedef u64 TIMESTAMP;\n\n//\n// probe->retprobe argument deferral\n// this stuff is a cut-and-paste-error reduction mechanism\n// because we often need to pass arguments received in a 'kprobe'\n// into its corresponding 'kretprobe', where they are no longer available.\n//\n\n#define SAVED_ARGS_TABLE_KEY _pid_tgid\n// #define SAVED_ARGS_TABLE_KEY _cpu\n\n#define SAVED_ARGS_TABLE(FUNC) FUNC##_active\n#define SAVED_ARGS_TYPE(FUNC) struct FUNC##_active_args_t\n\n#define BEGIN_DECLARE_SAVED_ARGS(FUNC)                                                                                         \\\n  SAVED_ARGS_TYPE(FUNC)                                                                                                        \\\n  {\n\n#define END_DECLARE_SAVED_ARGS(FUNC)                                                                                           \\\n  }                                                                                                                            \\\n  ;                                                                                                                            \\\n                                                                                                                               \\\n  struct {                                                                                                                     \\\n    __uint(type, BPF_MAP_TYPE_HASH);                                                                                           \\\n    __type(key, u64);                                                                                                          \\\n    __type(value, SAVED_ARGS_TYPE(FUNC));                                                                                      \\\n    __uint(max_entries, 1024);                                                                                                 \\\n  } SAVED_ARGS_TABLE(FUNC) SEC(\".maps\");\n\n#define BEGIN_SAVE_ARGS(FUNC)                                                                                                  \\\n  {                                                                                                                            \\\n    SAVED_ARGS_TYPE(FUNC) __saved_args = {\n\n#define SAVE_ARG(ARG) .ARG = ARG,\n\n// don't bpf_trace_printk if we have debugging turned off\n#if DEBUG_OTHER_MAP_ERRORS\n#define __DEBUG_OTHER_MAP_ERRORS_PRINTK false\n#else\n#define __DEBUG_OTHER_MAP_ERRORS_PRINTK true\n#endif\n\n#define END_SAVE_ARGS(FUNC)                                                                                                    \\\n  }                                                                                                                            \\\n  ;                                                                                                                            \\\n  int __ret = bpf_map_update_elem(&SAVED_ARGS_TABLE(FUNC), &SAVED_ARGS_TABLE_KEY, &__saved_args, BPF_NOEXIST);                 \\\n  if (__ret != 0 && __DEBUG_OTHER_MAP_ERRORS_PRINTK) {                                                                         \\\n    /* Check if key already exists to distinguish duplicate vs table full */                                                   \\\n    void *existing = bpf_map_lookup_elem(&SAVED_ARGS_TABLE(FUNC), &SAVED_ARGS_TABLE_KEY);                                      \\\n    if (existing) {                                                                                                            \\\n      save_args_update_existing_error++;                                                                                       \\\n    } else {                                                                                                                   \\\n      save_args_update_full_error++;                                                                                           \\\n    }                                                                                                                          \\\n  }                                                                                                                            \\\n  }\n\n#define GET_ARGS(FUNC, NAME)                                                                                                   \\\n  SAVED_ARGS_TYPE(FUNC) *NAME = bpf_map_lookup_elem(&SAVED_ARGS_TABLE(FUNC), &SAVED_ARGS_TABLE_KEY);                           \\\n  if (NAME == NULL) {                                                                                                          \\\n    __DEBUG_OTHER_MAP_ERRORS_PRINTK || bpf_trace_printk(#FUNC \": args table missing key. tgid=%u\\n\", _tgid);                   \\\n  }\n\n#define GET_ARGS_MISSING_OK(FUNC, NAME)                                                                                        \\\n  SAVED_ARGS_TYPE(FUNC) *NAME = bpf_map_lookup_elem(&SAVED_ARGS_TABLE(FUNC), &SAVED_ARGS_TABLE_KEY);\n\n#define DELETE_ARGS(FUNC) bpf_map_delete_elem(&SAVED_ARGS_TABLE(FUNC), &SAVED_ARGS_TABLE_KEY);\n\n// Timestamp abstraction\n// We do this so that eventually we could test the BPF outside\n// of the agent (in python or whatever). could replace this to simulate tight\n// timing tests eventually too\n\nstatic __always_inline TIMESTAMP get_timestamp(void)\n{\n#ifdef STANDALONE_TCP_PROCESSOR\n  return (TIMESTAMP)bpf_ktime_get_ns();\n#else\n  // Get time, adjust for boot\n  return bpf_ktime_get_ns() + boot_time_adjustment;\n#endif\n}\n\n// Error reporting abstraction\nstruct BPF_LOG_GLOBALS {\n  u32 period_start_ms;\n  u32 count;\n};\nstruct {\n  __uint(type, BPF_MAP_TYPE_ARRAY);\n  __type(key, u32);\n  __type(value, struct BPF_LOG_GLOBALS);\n  __uint(max_entries, BPF_MAX_CPUS);\n} bpf_log_globals_per_cpu SEC(\".maps\");\n\n#define bpf_log(ctx, _code, _arg0, _arg1, _arg2)                                                                               \\\n  do {                                                                                                                         \\\n    _bpf_log(__LINE__, ctx, _code, _arg0, _arg1, _arg2);                                                                       \\\n  } while (0)\n\nstatic void __always_inline _bpf_log(int filelineid, struct pt_regs *ctx, enum BPF_LOG_CODE code, u64 arg0, u64 arg1, u64 arg2)\n{\n\n  // Get timestamp in milliseconds\n  TIMESTAMP now = get_timestamp();\n  u32 now_ms = (u32)(now / 1000000ull);\n\n  // Get per-cpu globals\n  int cpu = bpf_get_smp_processor_id();\n  if (cpu < 0 || cpu >= BPF_MAX_CPUS) {\n    // what to do when 'log' itself fails?\n    return;\n  }\n  struct BPF_LOG_GLOBALS *globals = bpf_map_lookup_elem(&bpf_log_globals_per_cpu, &cpu);\n  if (globals == NULL) {\n    // what to do when 'log' itself fails?\n    return;\n  }\n\n  // Have we exceeded the previous period?\n  // if so, start a new period\n  if (now_ms >= globals->period_start_ms + BPF_LOG_THROTTLE_PERIOD_LENGTH_MS) {\n    globals->period_start_ms = now_ms;\n    globals->count = 0;\n  }\n\n  // Increment the count for this period\n  globals->count += 1;\n\n  // If the count has exceeded the max per period, log if it's the first one, else drop\n  if (globals->count == (BPF_LOG_THROTTLE_MAX_PER_PERIOD + 1)) {\n#ifdef STANDALONE_TCP_PROCESSOR\n    bpf_trace_printk(\"bpf_log: throttled on cpu %u\\n\", cpu);\n#else\n    perf_submit_agent_internal__bpf_log(ctx, now, (u64)filelineid, (u64)BPF_LOG_THROTTLED, cpu, 0, 0);\n#endif\n  } else if (globals->count <= BPF_LOG_THROTTLE_MAX_PER_PERIOD) {\n#ifdef STANDALONE_TCP_PROCESSOR\n    bpf_trace_printk(\"bpf_log: filelineid=%d, code=%u, now=%llu\\n\", filelineid, code, now);\n    bpf_trace_printk(\"         args=%llu, %llu, %llu\\n\", arg0, arg1, arg2);\n#else\n    perf_submit_agent_internal__bpf_log(ctx, now, (u64)filelineid, (u64)code, arg0, arg1, arg2);\n#endif\n  }\n}\n\n// Useful conversion from ip4 space to ip6 space for addresses\nstatic __always_inline struct in6_addr make_ipv6_address(__be32 addr)\n{\n  struct in6_addr addr6 = {.in6_u.u6_addr32 = {0, 0, 0xffff0000, addr}};\n  return addr6;\n}\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/tcp-processor.py",
    "content": "#!/usr/bin/env python3\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# DEPRECATED: This BCC-based debugging tool is no longer maintained\n# since the project has migrated to libbpf. The code is kept for\n# historical reference but may not work with current BPF code.\n\n#\n# Standalone debugging code for tcp-processor subtree of bpf_src\n# To use, sudo python tcp-processor.py\n#\n\nfrom bcc import BPF #, DEBUG_PREPROCESSOR\nimport io, os, sys\nfrom pcpp.preprocessor import Preprocessor, OutputDirective, Action\nimport ctypes as ct\n\nif \"--debug\" in sys.argv:\n    import ptvsd\n    print(\"Waiting for debugger attach\")\n    ptvsd.enable_attach(address=('localhost', 5678), redirect_output=True)\n    ptvsd.wait_for_attach()\n    breakpoint()\n\n#\n# Wrapper for BCC that prints useful diagnostics\n#\n\nclass BPFWrapper:\n\n    def __init__(self, bpf):\n        self._bpf = bpf\n\n    def attach_kprobe(self, event=b\"\", event_off=0, fn_name=b\"\", event_re=b\"\"):\n        print(\"attach_kprobe: event={} fn_name={}\".format(event, fn_name))\n        try:\n            self._bpf.attach_kprobe(event,event_off,fn_name,event_re)\n        except:\n            print(\"   failed for {}\".format(event))\n            return\n        print(\"   succeeded for {}\".format(event))\n        \n    def attach_kprobe_all(self, events, event_off=0, fn_name=b\"\", event_re=b\"\"):\n        print(\"attach_kprobe_all: events={} fn_name={}\".format(str(events), fn_name))\n        for event in events:\n            try:\n                self._bpf.attach_kprobe(event,event_off,fn_name,event_re)\n            except:\n                print(\"   failed for {}\".format(event))\n                continue\n            print(\"   succeeded for {}\".format(event))\n                \n    def attach_kretprobe(self, event=b\"\", fn_name=b\"\", event_re=b\"\", maxactive=0):\n        print(\"attach_kretprobe: event={} fn_name={}\".format(event, fn_name))\n        try:\n            self._bpf.attach_kretprobe(event,fn_name,event_re,maxactive)\n        except:\n            print(\"   failed for {}\".format(event))\n            return\n        print(\"   succeeded for {}\".format(event))\n        \n    def attach_kretprobe_all(self, events, fn_name=b\"\", event_re=b\"\", maxactive=0):\n        print(\"attach_kretprobe_all: events={} fn_name={}\".format(str(events), fn_name))\n        for event in events:\n            try:\n                self._bpf.attach_kretprobe(event,fn_name,event_re, maxactive)\n            except:\n                print(\"   failed for {}\".format(event))\n                continue\n            print(\"   succeeded for {}\".format(event))                    \n\n\n    def __getattr__(self, name):\n        if name in self.__dict__:\n            return self.__dict__[name]\n        if name in self.__class__.__dict__:\n            return self.__class__.__dict__[name]\n        return getattr(self._bpf, name)\n    \n    def __getitem__(self, key):\n        return self._bpf[key]\n\n#\n# Pass-through preprocessor to improve BCC's parsing\n#\n\nclass PassThruPreprocessor(Preprocessor):\n    def __init__(self,lexer=None):\n        super(PassThruPreprocessor, self).__init__(lexer)\n        self.passthrough = False\n        self.current_file_line_id = 0\n        self.file_line_table = []\n\n    def write_debug_info(self, debugfile):\n        lineid = 0\n        debugfile.write(\"int g_bpf_debug_line_info[] = {\\n\")\n        for x in self.file_line_table:   \n            debugfile.write(\"  {0},\\n\".format(x[\"line\"]))\n            lineid += 1\n        debugfile.write(\"};\\n\")\n        \n        lineid = 0\n        debugfile.write(\"const char *g_bpf_debug_file_info[] = {\\n\")\n        for x in self.file_line_table:            \n            debugfile.write(\"  {0},\\n\".format(x[\"file\"]))\n            lineid += 1\n        debugfile.write(\"};\\n\")\n    \n    def token(self):\n        \"\"\"Method to return individual tokens, overriding custom macros\"\"\"\n        tok = super(PassThruPreprocessor, self).token()\n        if tok and tok.value==\"__FILELINEID__\":\n            tok.value=str(self.current_file_line_id)\n            self.file_line_table.append({ \"file\": self.macros['__FILE__'].value[0].value, \"line\": tok.lineno })\n            self.current_file_line_id += 1\n        return tok\n\n    def on_include_not_found(self, is_system_include, curdir, includepath):\n        # If includes are not found, complain\n        sys.stderr.write(\"Unable to find include file: \"+str(includepath)+\"\\n\")\n        sys.exit(1)\n    def on_unknown_macro_in_defined_expr(self, tok):\n        # Pass through as expanded as possible, unexpanded without complaining if not possible\n        return None\n    def on_unknown_macro_in_expr(self, tok):\n        # Pass through as expanded as possible, unexpanded without complaining if not possible\n        return None\n    def on_directive_handle(self, directive, toks, ifpassthru, precedingtoks):\n        if directive.value==\"pragma\":\n            if len(toks)>=1 and toks[0].type==\"CPP_ID\" and toks[0].value==\"passthrough\":\n                if len(toks)==3 and toks[1].type==\"CPP_WS\" and toks[2].type==\"CPP_ID\" and toks[2].value==\"on\":\n                    # Turn on passthrough\n                    self.passthrough = True\n                    raise OutputDirective(Action.IgnoreAndRemove)\n                elif len(toks)==3 and toks[1].type==\"CPP_WS\" and toks[2].type==\"CPP_ID\" and toks[2].value==\"off\":\n                    # Turn on passthrough\n                    self.passthrough = False\n                    raise OutputDirective(Action.IgnoreAndRemove)\n                sys.stderr.write(\"Invalid passthrough pragma\\n\")\n                sys.exit(1)\n\n        if self.passthrough:\n            # Pass through without execution EVERYTHING if we have used pragma passthrough\n            raise OutputDirective(Action.IgnoreAndPassThrough)\n\n        if directive.value==\"define\":\n            # Process and ALSO pass through as well\n            return None\n        if directive.value==\"include\":\n            if toks[0].type==\"CPP_STRING\":\n                # Process #include \"\" normally\n                return True\n            else:\n                # Always pass through #include<>\n                raise OutputDirective(Action.IgnoreAndPassThrough)\n\n        # Attempt to process all other directives\n        return True\n    def on_directive_unknown(self, directive, toks, ifpassthru, precedingtoks):\n        raise OutputDirective(Action.IgnoreAndPassThrough)   \n\n\n\n#\n# Parse the BPF\n#\n\npp = PassThruPreprocessor()\ninstr = \"#define DEBUG_LOG 1\\n#define ENABLE_TCP_DATA_STREAM 1\\n#define _PROCESSING_BPF 1\\n#define STANDALONE_TCP_PROCESSOR 1\\n\" + open(\"./bpf_tcp_processor.c\",\"rt\").read()\noutfile = io.StringIO()\npp.add_path(\"../../../../src/\")\npp.add_path(\"../../../../\")\npp.parse(instr, source = \"./bpf_tcp_processor.c\")\npp.write(outfile)\npreprocessed_bpf_text = outfile.getvalue()\n# print(\"preproc: \"+preprocessed_bpf_text)\n\nprint(\"debug info:\")\npp.write_debug_info(sys.stdout)\n\nb = BPFWrapper(BPF(text=preprocessed_bpf_text))\n\n# Set up tail calls (mirroring bpf_handler.cc)\n\ntail_calls = b.get_table(\"tail_calls\")\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_ON_UDP_SEND_SKB__2\"].value[0].value))] = b.load_func(\"on_udp_send_skb__2\", BPF.KPROBE)\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_ON_UDP_V6_SEND_SKB__2\"].value[0].value))] = b.load_func(\"on_udp_v6_send_skb__2\", BPF.KPROBE)\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_ON_IP_SEND_SKB__2\"].value[0].value))] = b.load_func(\"on_ip_send_skb__2\", BPF.KPROBE)\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_ON_IP6_SEND_SKB__2\"].value[0].value))] = b.load_func(\"on_ip6_send_skb__2\", BPF.KPROBE)\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_HANDLE_RECEIVE_UDP_SKB\"].value[0].value))] = b.load_func(\"handle_receive_udp_skb\", BPF.KPROBE)\n#tail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_HANDLE_RECEIVE_UDP_SKB__2\"].value[0].value))] = b.load_func(\"handle_receive_udp_skb__2\", BPF.KPROBE)\ntail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_CONTINUE_TCP_SENDMSG\"].value[0].value))] = b.load_func(\"continue_tcp_sendmsg\", BPF.KPROBE)\ntail_calls[ct.c_int(int(pp.macros[\"TAIL_CALL_CONTINUE_TCP_RECVMSG\"].value[0].value))] = b.load_func(\"continue_tcp_recvmsg\", BPF.KPROBE)\n\n#\n# Attach probes\n#\n\n# Create/destroy\nb.attach_kprobe(event=\"tcp_init_sock\", fn_name=\"handle_kprobe__tcp_init_sock\")\nb.attach_kprobe(event=\"security_sk_free\", fn_name=\"handle_kprobe__security_sk_free\")\n\n# Accept\nb.attach_kprobe(event=\"inet_csk_accept\", fn_name=\"handle_kprobe__inet_csk_accept\")\nb.attach_kretprobe(event=\"inet_csk_accept\", fn_name=\"handle_kretprobe__inet_csk_accept\")\n\n# Send\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"handle_kprobe__tcp_sendmsg\")\nb.attach_kretprobe(event=\"tcp_sendmsg\", fn_name=\"handle_kretprobe__tcp_sendmsg\")\n\n# Receive\nb.attach_kprobe(event=\"tcp_recvmsg\", fn_name=\"handle_kprobe__tcp_recvmsg\")\nb.attach_kretprobe(event=\"tcp_recvmsg\", fn_name=\"handle_kretprobe__tcp_recvmsg\")\n\n#\n# Print trace output\n#\n\nclass TCPEventHTTPResponse(ct.Structure):\n    _fields_ = [\n        (\"code\", ct.c_ushort),\n        (\"__pad0\", ct.c_uint8*6),\n        (\"latency\", ct.c_ulonglong)\n    ]\n\nclass TCPEventTCPData(ct.Structure):\n    _fields_ = [\n        (\"length\", ct.c_uint),\n        (\"streamtype\", ct.c_uint8),\n        (\"is_server\", ct.c_uint8),\n        (\"__pad0\", ct.c_uint16),\n        (\"offset\", ct.c_ulonglong)\n    ]\n\nclass TCPEventData(ct.Union):\n    _fields_ = [ \n        (\"http_response\", TCPEventHTTPResponse),\n        (\"tcp_data\", TCPEventTCPData),\n        (\"__pad0\", ct.c_ulonglong),\n        (\"__pad1\", ct.c_ulonglong)\n        ]\n\nclass TCPEvent(ct.Structure):\n    _anonymous_ = (\"u\",)\n    _fields_ = [\n        (\"type\", ct.c_uint),\n        (\"pid\", ct.c_uint),\n        (\"ts\", ct.c_ulonglong),\n        (\"sk\", ct.c_ulonglong),\n        (\"u\", TCPEventData)\n        ]\n\nclass TCPDataHeader(ct.Structure):\n    _pack_ = 1\n    _fields_ = [\n        (\"length\", ct.c_ulonglong),\n    ]\n\nclass TCPDataMessage(ct.Structure):\n    _pack_ = 1\n    _fields_ = [\n        (\"hdr\", TCPDataHeader),              \n        (\"data\", ct.c_ubyte * 256)\n    ]\n\nprint(\"sizeof(TCPDataHeader) = {}\".format(ct.sizeof(TCPDataHeader)))\nprint(\"sizeof(TCPDataMessage) = {}\".format(ct.sizeof(TCPDataMessage)))\n\ndef print_tcp_event(cpu, data, size):\n    assert size >= ct.sizeof(TCPEvent)\n    event = ct.cast(data, ct.POINTER(TCPEvent)).contents\n\n    if event.type == 0:\n        print(\">>> TCP_EVENT_TYPE_HTTP_RESPONSE(pid=%u, ts=%u, sk=0x%X, code=%u, latency=%u)\" % (event.pid, event.ts, event.sk, event.http_response.code, event.http_response.latency))\n    elif event.type == 1:\n        print(\">>> TCP_EVENT_TYPE_TCP_DATA(pid=%u, ts=%u, sk=0x%X, length=%u, streamtype=%u, is_server=%u, offset=%u)\" % (event.pid, event.ts, event.sk, event.tcp_data.length, event.tcp_data.streamtype, event.tcp_data.is_server, event.tcp_data.offset))\n    else:\n        print(\">>> UNKNOWN TCP EVENT\")\n\n\ndef process_data_channel(cpu, data, size):\n    assert size >= ct.sizeof(TCPDataHeader)\n\n    header = ct.cast(data, ct.POINTER(TCPDataHeader)).contents\n    datalen = header.length\n\n    out = \"### DATA(size: {}, datalen: {}): \".format(size, datalen)\n    print(out)\n\n    assert size >= ct.sizeof(TCPDataHeader) + datalen\n\n    message = ct.cast(data, ct.POINTER(TCPDataMessage)).contents\n    out = ct.cast(message.data, ct.c_char_p).value[0:datalen]\n    print(out)\n        \nb[b\"tcp_events\"].open_perf_buffer(print_tcp_event)\nb[b\"data_channel\"].open_perf_buffer(process_data_channel)\n\nwhile 1:\n    try:\n        had_message = False\n        fields = b.trace_fields(True)\n        if fields:\n            (task, pid, cpu, flags, ts, msg) = fields\n            if msg:\n                had_message=True\n                print(msg.decode('latin-1'))\n        \n        if not had_message:\n            # Prefer to print messages instead of polling to speed through them\n            b.perf_buffer_poll(10)\n\n    except ValueError:\n        continue \n\n"
  },
  {
    "path": "collector/kernel/bpf_src/tcp-processor/tcp_processor.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// Protocols\n#define TCP_PROTOCOL_TYPE u32\n#define TCP_PROTOCOL_COUNT 1\n\n#define TCPPROTO_UNKNOWN 0\n#define TCPPROTO_HTTP 1\n\n#define TCP_PROTOCOL_BIT(X) (1 << ((X) - 1))\n#define TCP_PROTOCOL_MASK ((1 << TCP_PROTOCOL_COUNT) - 1)\n\n#define TCP_TAIL_CALL_MAX_DEPTH 8\n\n// Stream types\nenum STREAM_TYPE { ST_SEND = 0, ST_RECV = 1 };\n\n#ifndef _PROCESSING_BPF\n\ninline const char *stream_type_to_string(enum STREAM_TYPE stream_type)\n{\n  switch (stream_type) {\n  case ST_SEND:\n    return \"SEND\";\n  case ST_RECV:\n    return \"RECV\";\n  }\n  throw std::runtime_error(\"invalid STREAM_TYPE value\");\n}\n\n#endif\n\n// TCP Data sent to userland\n#define TCP_DATA_SIZE 256\n\n// TCP Control channel map\nstruct tcp_control_key_t {\n  u64 sk; // the socket to control\n};\n\nstruct tcp_control_stream_t {\n  u64 enable; // 0 = disable this side of the stream, 1 = enable\n  u64 start;  // start offset of stream to start watching\n};\n\nstruct tcp_control_value_t {\n  struct tcp_control_stream_t streams[2]; // control for send and receive streams\n};\n\n// this header could probably be removed if we are\n// ever allowed to perf_submit directly from kernel memory\nstruct data_channel_header_t {\n  u64 length;\n};\n\n// Protocol handling\nenum TCP_PROTOCOL_DETECT_RESULT { TPD_FAILED = -1, TPD_UNKNOWN = 0, TPD_SUCCESS = 1 };\n\n#include \"common/client_server_type.h\"\n"
  },
  {
    "path": "collector/kernel/bpf_src/vmlinux_compat.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// This file contains old struct definitions for compatibility\n\n#pragma once\n\n// required for btf relocations\n#pragma clang attribute push(__attribute__((preserve_access_index)), apply_to = record)\n\nstruct iov_iter___5_13_19 {\n  /*\n   * Bit 0 is the read/write bit, set if we're writing.\n   * Bit 1 is the BVEC_FLAG_NO_REF bit, set if type is a bvec and\n   * the caller isn't expecting to drop a page reference when done.\n   */\n  unsigned int type;\n  size_t iov_offset;\n  size_t count;\n  union {\n    const struct iovec *iov;\n    const struct kvec *kvec;\n    const struct bio_vec *bvec;\n    struct xarray *xarray;\n    struct pipe_inode_info *pipe;\n  };\n  union {\n    unsigned long nr_segs;\n    struct {\n      unsigned int head;\n      unsigned int start_head;\n    };\n    loff_t xarray_start;\n  };\n};\n\nstruct msghdr___5_13_19 {\n  void *msg_name;                     /* ptr to socket address structure */\n  int msg_namelen;                    /* size of socket address structure */\n  struct iov_iter___5_13_19 msg_iter; /* data */\n\n  /*\n   * Ancillary data. msg_control_user is the user buffer used for the\n   * recv* side when msg_control_is_user is set, msg_control is the kernel\n   * buffer used for all other cases.\n   */\n  union {\n    void *msg_control;\n    // void __user\t*msg_control_user;\n  };\n  bool msg_control_is_user : 1;\n  __kernel_size_t msg_controllen; /* ancillary data buffer length */\n  unsigned int msg_flags;         /* flags on received message */\n  struct kiocb *msg_iocb;         /* ptr to iocb for async requests */\n};\n\nstruct css_id___3_11 {\n  /*\n   * The css to which this ID points. This pointer is set to valid value\n   * after cgroup is populated. If cgroup is removed, this will be NULL.\n   * This pointer is expected to be RCU-safe because destroy()\n   * is called after synchronize_rcu(). But for safe use, css_tryget()\n   * should be used for avoiding race.\n   */\n  struct cgroup_subsys_state *css;\n  /*\n   * ID of this css.\n   */\n  unsigned short id;\n  /*\n   * Depth in hierarchy which this ID belongs to.\n   */\n  unsigned short depth;\n  /*\n   * ID is freed by RCU. (and lookup routine is RCU safe.)\n   */\n  struct rcu_head rcu_head;\n  /*\n   * Hierarchy of CSS ID belongs to.\n   */\n  unsigned short stack[0]; /* Array of Length (depth+1) */\n};\n\nstruct cgroup_name___3_11 {\n  struct rcu_head rcu_head;\n  char name[];\n};\n\nstruct cgroup___3_11 {\n  unsigned long flags; /* \"unsigned long\" so bitops work */\n\n  int id; /* ida allocated in-hierarchy ID */\n\n  /*\n   * We link our 'sibling' struct into our parent's 'children'.\n   * Our children link their 'sibling' into our 'children'.\n   */\n  struct list_head sibling;  /* my parent's children */\n  struct list_head children; /* my children */\n  struct list_head files;    /* my files */\n\n  struct cgroup *parent; /* my parent */\n  struct dentry *dentry; /* cgroup fs entry, RCU protected */\n\n  /*\n   * Monotonically increasing unique serial number which defines a\n   * uniform order among all cgroups.  It's guaranteed that all\n   * ->children lists are in the ascending order of ->serial_nr.\n   * It's used to allow interrupting and resuming iterations.\n   */\n  u64 serial_nr;\n\n  /*\n   * This is a copy of dentry->d_name, and it's needed because\n   * we can't use dentry->d_name in cgroup_path().\n   *\n   * You must acquire rcu_read_lock() to access cgrp->name, and\n   * the only place that can change it is rename(), which is\n   * protected by parent dir's i_mutex.\n   *\n   * Normally you should use cgroup_name() wrapper rather than\n   * access it directly.\n   */\n  struct cgroup_name___3_11 *name;\n\n  /* Private pointers for each registered subsystem */\n  // struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];\n\n  struct cgroupfs_root *root;\n\n  /*\n   * List of cgrp_cset_links pointing at css_sets with tasks in this\n   * cgroup.  Protected by css_set_lock.\n   */\n  struct list_head cset_links;\n\n  /*\n   * Linked list running through all cgroups that can\n   * potentially be reaped by the release agent. Protected by\n   * release_list_lock\n   */\n  struct list_head release_list;\n\n  /*\n   * list of pidlists, up to two for each namespace (one for procs, one\n   * for tasks); created on demand.\n   */\n  struct list_head pidlists;\n  struct mutex pidlist_mutex;\n\n  /* For css percpu_ref killing and RCU-protected deletion */\n  struct rcu_head rcu_head;\n  struct work_struct destroy_work;\n  atomic_t css_kill_cnt;\n\n  /* List of events which userspace want to receive */\n  struct list_head event_list;\n  spinlock_t event_list_lock;\n\n  /* directory xattrs */\n  struct simple_xattrs xattrs;\n};\n\nstruct cgroup_subsys_state___3_11 {\n  /*\n   * The cgroup that this subsystem is attached to. Useful\n   * for subsystems that want to know about the cgroup\n   * hierarchy structure\n   */\n  struct cgroup___3_11 *cgroup;\n\n  /* reference count - access via css_[try]get() and css_put() */\n  struct percpu_ref refcnt;\n\n  unsigned long flags;\n  /* ID for this css, if possible */\n  struct css_id___3_11 *id;\n\n  /* Used to put @cgroup->dentry on the last css_put() */\n  struct work_struct dput_work;\n};\n\nstruct tcp_sock___rcv_rtt_est_rtt {\n  struct {\n    u32 rtt;\n    u32 seq;\n    u32 time;\n  } rcv_rtt_est;\n};\n\n// remove the instruction to add preserve_access_index\n#pragma clang attribute pop\n"
  },
  {
    "path": "collector/kernel/bpf_src/vmlinux_extensions.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// This file contains kernel data structures that are not in libbpf's regular vmlinux.h repository\n// (https://github.com/libbpf/vmlinux.h).\n//\n// libbpf's vmlinux.h is prepared with their own config, not from distros:\n//  - https://github.com/libbpf/vmlinux.h/blob/main/.github/workflows/vmlinux.h.yml\n//  - https://github.com/libbpf/vmlinux.h/blob/main/scripts/gen-vmlinux-header.sh\n//  - https://github.com/libbpf/vmlinux.h/blob/main/kconfigs/config.x86_64\n// and so contains partial structures.\n//\n// This file adds more that is required by the project. These structs must have __attribute__((preserve_access_index));\n// See \"Defining own CO-RE-relocatable type definitions\" in https://nakryiko.com/posts/bpf-core-reference-guide/\n//\n// You can generate an h file from a live system using:\n//    bpftool btf dump file /sys/kernel/btf/vmlinux format c\n// (command extracted from https://github.com/aquasecurity/btfhub/blob/main/docs/btfgen-internals.md#btfhub)\n\n#pragma once\n\n// this is used to apply the required attribute to all structs\n#pragma clang attribute push(__attribute__((preserve_access_index)), apply_to = record)\n\n#define rcu_head callback_head\n\nunion nf_inet_addr {\n  __u32 all[4];\n  __be32 ip;\n  __be32 ip6[4];\n  struct in_addr in;\n  struct in6_addr in6;\n};\n\nunion nf_conntrack_man_proto {\n  __be16 all;\n};\n\nstruct nf_conntrack_man {\n  union nf_inet_addr u3;\n  union nf_conntrack_man_proto u;\n  uint16_t l3num;\n};\n\nstruct nf_conntrack_tuple {\n  struct nf_conntrack_man src;\n  struct {\n    union nf_inet_addr u3;\n    union {\n      __be16 all;\n    } u;\n\n    u_int8_t protonum;\n    u_int8_t dir;\n  } dst;\n};\n\nstruct nf_conntrack_tuple_hash {\n  struct hlist_nulls_node hnnode;\n  struct nf_conntrack_tuple tuple;\n};\n\nstruct nf_conn {\n  struct nf_conntrack_tuple_hash tuplehash[2];\n};\n\n// Optional sk_buff field flavor for kernels with NF_CONNTRACK configured.\nstruct sk_buff___with_nfct {\n  unsigned long _nfct;\n};\n\nstruct cgroup;\nstruct cgroup_subsys;\n\nstruct cftype {\n  char name[64];\n  long unsigned int private;\n  size_t max_write_len;\n  unsigned int flags;\n  unsigned int file_offset;\n  struct cgroup_subsys *ss;\n  struct list_head node;\n  struct kernfs_ops *kf_ops;\n};\n\nstruct cgroup_subsys {\n  struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *);\n  bool early_init : 1;\n  bool implicit_on_dfl : 1;\n  bool threaded : 1;\n  int id;\n  const char *name;\n  const char *legacy_name;\n  struct cgroup_root *root;\n  struct idr css_idr;\n  struct list_head cfts;\n  struct cftype *dfl_cftypes;\n  struct cftype *legacy_cftypes;\n  unsigned int depends_on;\n};\n\nstruct cgroup_subsys_state {\n  struct cgroup *cgroup;\n  struct cgroup_subsys *ss;\n  struct percpu_ref refcnt;\n  struct list_head sibling;\n  struct list_head children;\n  struct list_head rstat_css_node;\n  int id;\n  unsigned int flags;\n  u64 serial_nr;\n  atomic_t online_cnt;\n  struct work_struct destroy_work;\n  struct rcu_work destroy_rwork;\n  struct cgroup_subsys_state *parent;\n};\n\nstruct cgroup {\n  struct cgroup_subsys_state self;\n  long unsigned int flags;\n  int level;\n  int max_depth;\n  int nr_descendants;\n  int nr_dying_descendants;\n  int max_descendants;\n  int nr_populated_csets;\n  int nr_populated_domain_children;\n  int nr_populated_threaded_children;\n  int nr_threaded_children;\n  struct kernfs_node *kn;\n  // struct cgroup_file procs_file;\n  // struct cgroup_file events_file;\n  // struct cgroup_file psi_files[3];\n  u16 subtree_control;\n  u16 subtree_ss_mask;\n  u16 old_subtree_control;\n  u16 old_subtree_ss_mask;\n  struct cgroup_subsys_state *subsys[14];\n  struct cgroup_root *root;\n  struct list_head cset_links;\n  struct list_head e_csets[14];\n  struct cgroup *dom_cgrp;\n  struct cgroup *old_dom_cgrp;\n  // struct cgroup_rstat_cpu *rstat_cpu;\n  struct list_head rstat_css_list;\n  long : 64;\n  long : 64;\n  // struct cacheline_padding _pad_;\n  struct cgroup *rstat_flush_next;\n  // struct cgroup_base_stat last_bstat;\n  // struct cgroup_base_stat bstat;\n  struct prev_cputime prev_cputime;\n  struct list_head pidlists;\n  struct mutex pidlist_mutex;\n  wait_queue_head_t offline_waitq;\n  struct work_struct release_agent_work;\n  struct psi_group *psi;\n  // struct cgroup_bpf bpf;\n  atomic_t congestion_count;\n  // struct cgroup_freezer_state freezer;\n  struct bpf_local_storage *bpf_cgrp_storage;\n  struct cgroup *ancestors[0];\n};\n\nstruct cgroup_root {\n  struct kernfs_root *kf_root;\n  unsigned int subsys_mask;\n  int hierarchy_id;\n  struct list_head root_list;\n  struct callback_head rcu;\n  long : 64;\n  long : 64;\n  struct cgroup cgrp;\n  struct cgroup *cgrp_ancestor_storage;\n  atomic_t nr_cgrps;\n  unsigned int flags;\n  char release_agent_path[4096];\n  char name[64];\n  long : 64;\n  long : 64;\n  long : 64;\n  long : 64;\n  long : 64;\n  long : 64;\n};\n\nstruct css_set {\n  struct cgroup_subsys_state *subsys[14];\n  refcount_t refcount;\n  struct css_set *dom_cset;\n  struct cgroup *dfl_cgrp;\n  int nr_tasks;\n  struct list_head tasks;\n  struct list_head mg_tasks;\n  struct list_head dying_tasks;\n  struct list_head task_iters;\n  struct list_head e_cset_node[14];\n  struct list_head threaded_csets;\n  struct list_head threaded_csets_node;\n  struct hlist_node hlist;\n  struct list_head cgrp_links;\n  struct list_head mg_src_preload_node;\n  struct list_head mg_dst_preload_node;\n  struct list_head mg_node;\n  struct cgroup *mg_src_cgrp;\n  struct cgroup *mg_dst_cgrp;\n  struct css_set *mg_dst_cset;\n  bool dead;\n  struct callback_head callback_head;\n};\n\nstruct task_struct___with_css_set {\n  struct css_set *cgroups;\n};\n\n// dynamically generated by the kernel based on config variables, read with bpf_core_enum_value\nenum cgroup_subsys_id { mem_cgroup_subsys_id, memory_cgrp_id };\n\n// remove the instruction to add preserve_access_index\n#pragma clang attribute pop\n"
  },
  {
    "path": "collector/kernel/buffered_poller.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/agent_log.h>\n#include <collector/kernel/bpf_src/render_bpf.h>\n#include <collector/kernel/buffered_poller.h>\n#include <collector/kernel/dns/ares.h>\n#include <collector/kernel/dns/dns.h>\n#include <collector/kernel/kernel_collector_restarter.h>\n#include <collector/kernel/perf_reader.h>\n#include <collector/kernel/proc_cmdline.h>\n#include <common/client_server_type.h>\n#include <platform/platform.h>\n#include <util/ip_address.h>\n#include <util/log.h>\n#include <util/lookup3.h>\n\n#include <generated/ebpf_net/agent_internal/meta.h>\n#include <generated/ebpf_net/agent_internal/wire_message.h>\n#include <generated/ebpf_net/ingest/wire_message.h>\n\n#include <spdlog/common.h>\n#include <spdlog/fmt/bin_to_hex.h>\n\n#include <chrono>\n#include <cstring>\n#include <iostream>\n#include <stdexcept>\n#include <string_view>\n\nconstexpr u16 DNS_MAX_PACKET_LEN = 512;\n\n#ifdef DEBUG_PID\nstatic BufferedPoller *singleton_ = nullptr;\n#endif // DEBUG_PID\n\nstatic constexpr u64 DNS_TIMEOUT_TIME_NS = 10'000'000'000ull;\n\nnamespace {\n\nstd::string_view comm_to_string(std::uint8_t const (&comm)[16])\n{\n  auto const length = strnlen(reinterpret_cast<char const *>(comm), sizeof(comm));\n  return std::string_view(reinterpret_cast<char const *>(comm), length);\n}\n\n} // namespace\n\nBufferedPoller::BufferedPoller(\n    uv_loop_t &loop,\n    PerfContainer &container,\n    IBufferedWriter &writer,\n    u64 time_adjustment,\n    CurlEngine &curl_engine,\n    FileDescriptor &bpf_dump_file,\n    logging::Logger &log,\n    ProbeHandler &probe_handler,\n    struct render_bpf_bpf *skel,\n    u64 socket_stats_interval_sec,\n    CgroupHandler::CgroupSettings const &cgroup_settings,\n    ::ebpf_net::ingest::Encoder *encoder,\n    KernelCollectorRestarter &kernel_collector_restarter)\n    : PerfPoller(container),\n      loop_(loop),\n      time_adjustment_(time_adjustment),\n      bpf_dump_file_(bpf_dump_file),\n      log_(log),\n      buffered_writer_(writer),\n      probe_handler_(probe_handler),\n      skel_(skel),\n      writer_(buffered_writer_, monotonic, time_adjustment, encoder),\n      collector_index_({writer_}),\n      process_handler_(writer_, collector_index_, log_),\n      cgroup_handler_(writer_, curl_engine, std::move(cgroup_settings), log),\n      nat_handler_(writer_, log),\n      tcp_socket_table_ever_full_(false),\n      tslot_(socket_stats_interval_sec * 1e9, 16),\n      tcp_socket_stats_(tslot_),\n      udp_socket_table_ever_full_(false),\n      udp_socket_stats_{{{tslot_}, {tslot_}}},\n      all_probes_loaded_(false),\n      kernel_collector_restarter_(kernel_collector_restarter)\n{\n  if (buffered_writer_.buf_size() < MAX_ENCODED_DNS_MESSAGE) {\n    throw std::runtime_error(\"BufferedPoller: buf size too small for DNS\");\n  }\n\n  {\n    using namespace ebpf_net::agent_internal;\n\n    memset(handlers_, 0, sizeof(handlers_));\n    add_handler<dns_packet_message_metadata, &BufferedPoller::handle_dns_message, DNS_MAX_PACKET_LEN + 16>();\n    add_handler<new_sock_created_message_metadata, &BufferedPoller::handle_new_socket>();\n    add_handler<set_state_ipv4_message_metadata, &BufferedPoller::handle_set_state_ipv4>();\n    add_handler<set_state_ipv6_message_metadata, &BufferedPoller::handle_set_state_ipv6>();\n    add_handler<close_sock_info_message_metadata, &BufferedPoller::handle_close_socket>();\n    add_handler<rtt_estimator_message_metadata, &BufferedPoller::handle_rtt_estimator>();\n    add_handler<reset_tcp_counters_message_metadata, &BufferedPoller::handle_reset_tcp_counters>();\n    add_handler<tcp_syn_timeout_message_metadata, &BufferedPoller::handle_tcp_syn_timeout>();\n    add_handler<tcp_reset_message_metadata, &BufferedPoller::handle_tcp_reset>();\n    add_handler<http_response_message_metadata, &BufferedPoller::handle_http_response>();\n    add_handler<udp_new_socket_message_metadata, &BufferedPoller::handle_udp_new_socket>();\n    add_handler<udp_destroy_socket_message_metadata, &BufferedPoller::handle_udp_destroy_socket>();\n    add_handler<udp_stats_message_metadata, &BufferedPoller::handle_udp_stats>();\n    add_handler<pid_info_message_metadata, &BufferedPoller::handle_pid_info>();\n    add_handler<pid_close_message_metadata, &BufferedPoller::handle_pid_close>();\n    add_handler<pid_set_comm_message_metadata, &BufferedPoller::handle_pid_set_comm>();\n    add_handler<pid_exit_message_metadata, &BufferedPoller::handle_pid_exit>();\n    add_handler<kill_css_message_metadata, &BufferedPoller::handle_kill_css>();\n    add_handler<css_populate_dir_message_metadata, &BufferedPoller::handle_css_populate_dir>();\n    add_handler<existing_cgroup_probe_message_metadata, &BufferedPoller::handle_existing_cgroup_probe>();\n    add_handler<cgroup_attach_task_message_metadata, &BufferedPoller::handle_cgroup_attach_task>();\n    add_handler<nf_nat_cleanup_conntrack_message_metadata, &BufferedPoller::handle_nf_nat_cleanup_conntrack>();\n    add_handler<nf_conntrack_alter_reply_message_metadata, &BufferedPoller::handle_nf_conntrack_alter_reply>();\n    add_handler<existing_conntrack_tuple_message_metadata, &BufferedPoller::handle_existing_conntrack_tuple>();\n    add_handler<bpf_log_message_metadata, &BufferedPoller::handle_bpf_log>();\n    add_handler<stack_trace_message_metadata, &BufferedPoller::handle_stack_trace>();\n    add_handler<tcp_data_message_metadata, &BufferedPoller::handle_tcp_data>();\n  }\n\n  // Create a tcp data handler for the tcp_data message\n  tcp_data_handler_ = std::make_unique<TCPDataHandler>(loop_, probe_handler_, skel, writer_, container, log_);\n\n  // Set perf container callback for events\n  container.set_callback(loop, this, [](void *ctx) { ((BufferedPoller *)ctx)->handle_event(); });\n\n#ifdef DEBUG_PID\n  singleton_ = this;\n  signal(SIGUSR1, [](int) { singleton_->process_handler_.debug_pid_dump(); });\n#endif // DEBUG_PID\n}\n\nvoid BufferedPoller::handle_event()\n{\n  process_samples(true);\n}\n\nvoid BufferedPoller::poll(void)\n{\n  process_samples(false);\n}\n\nvoid BufferedPoller::process_samples(bool is_event)\n{\n  u64 t = monotonic() + time_adjustment_;\n  PerfReader reader(container_, t);\n\n  // in the case of event-driven poll, print debugging information to assist\n  // with understanding the profile of the perf buffers\n  if (is_event && is_log_whitelisted(AgentLogKind::PERF)) {\n    std::string cstr = container_.inspect();\n    LOG::debug_in(AgentLogKind::PERF, \"* Perf event triggered *\\ncontainer_ is:\\n{}\\n\\n\", cstr);\n  }\n\n  // read the top contents of our container into our buffer\n  while (!reader.empty()) {\n    auto peek_type = reader.peek_type();\n\n    auto handle_bpf_lost_samples = [this]() {\n      send_report_if_recent_loss();\n      log_.warn(\"Lost {} bpf samples - restarting kernel collector.\", lost_count_);\n      kernel_collector_restarter_.request_restart();\n    };\n\n#ifndef NDEBUG\n    if (debug_bpf_lost_samples_) {\n      lost_count_ += 1;\n      handle_bpf_lost_samples();\n      return;\n    }\n#endif\n\n    if (peek_type == PERF_RECORD_SAMPLE) {\n      if (bpf_dump_file_) {\n        auto const view = reader.peek_message();\n        bpf_dump_file_.write_all(view.first);\n        bpf_dump_file_.write_all(view.second);\n      }\n\n      u16 length = reader.peek_unpadded_length();\n\n      /* special handling for DNS packets */\n      if (length < 10)\n        throw std::runtime_error(\"got message < timestamp+rpc_id\");\n\n      u16 rpc_id = reader.peek_rpc_id();\n\n      /* if rpc_id is in handlers list, call it. */\n      u32 handlers_idx = agent_internal_hash(rpc_id);\n      if (handlers_[handlers_idx] != nullptr) {\n        (this->*handlers_[handlers_idx])(reader, length);\n        continue;\n      }\n\n      /* unknown message -- this is a bug */\n      throw std::runtime_error(\"unexpected bpf message\\n\");\n    } else if (peek_type == PERF_RECORD_LOST) {\n      lost_count_ += reader.peek_n_lost();\n      reader.pop();\n\n      handle_bpf_lost_samples();\n      return;\n    } else {\n      throw std::runtime_error(\"Unexpected record type\\n\");\n    }\n  }\n\n  /* do we need to process stats? */\n  s16 relative = tcp_socket_stats_.relative_timeslot(t);\n  if (relative != 0) {\n    send_stats_from_queue(t);\n    udp_send_stats_from_queue(t);\n    send_report_if_recent_loss();\n  }\n\n  // clear out buffer if there's still anything left\n  if (auto error = buffered_writer_.flush(); error && buffered_writer_.is_writable()) {\n    throw std::runtime_error(fmt::format(\"flush failed at end: {}\", error));\n  }\n}\n\nvoid BufferedPoller::send_report_if_recent_loss()\n{\n  if (lost_count_ == notified_lost_count_) {\n    return; // no need to report\n  }\n\n  writer_.bpf_lost_samples(lost_count_ - notified_lost_count_);\n  notified_lost_count_ = lost_count_;\n}\n\nu64 BufferedPoller::serv_lost_count()\n{\n  return lost_count_;\n}\n\ntemplate <\n    typename MessageMetadata,\n    BufferedPoller::message_handler_fn<MessageMetadata> Handler,\n    std::size_t MaxPadding,\n    typename Alignment>\nvoid BufferedPoller::message_handler_entrypoint(PerfReader &reader, u16 length)\n{\n  struct {\n    u64 timestamp;\n    typename MessageMetadata::wire_message msg;\n    Alignment padding[(MaxPadding + (sizeof(Alignment) - 1)) / sizeof(Alignment)];\n  } in;\n\n  if constexpr (MaxPadding) {\n    if (length < sizeof(u64) + MessageMetadata::wire_message_size) {\n      throw std::runtime_error(fmt::format(\n          \"message truncated (`{}`: {}/{})\", MessageMetadata::name, length, sizeof(u64) + MessageMetadata::wire_message_size));\n    } else if (length > sizeof(u64) + MessageMetadata::wire_message_size + MaxPadding) {\n      throw std::runtime_error(fmt::format(\n          \"message too long (`{}`: {}/{})\",\n          MessageMetadata::name,\n          length,\n          sizeof(u64) + MessageMetadata::wire_message_size + MaxPadding));\n    }\n  } else if (length != sizeof(u64) + MessageMetadata::wire_message_size) {\n    throw std::runtime_error(fmt::format(\n        \"invalid message length (`{}`: {}/{})\",\n        MessageMetadata::name,\n        length,\n        sizeof(u64) + MessageMetadata::wire_message_size));\n  }\n\n  // Get the cpu index we're on so we can read the right data\n  // do this before the pop, so we get the right index\n  std::size_t const cpu_index = reader.peek_index();\n\n  reader.pop_and_copy_to(reinterpret_cast<char *>(&in));\n\n  // at this point it's ok to skip the handler since the message has been\n  // consumed\n\n  // don't handle the message when disconnected\n  if (!buffered_writer_.is_writable()) {\n    return;\n  }\n\n  (this->*Handler)(\n      {\n          .timestamp = in.timestamp,\n          .cpu_index = cpu_index,\n          .payload = {reinterpret_cast<u8 const *>(&in), length},\n          .padding = {reinterpret_cast<u8 const *>(&in.msg) + MessageMetadata::wire_message_size, MaxPadding},\n      },\n      in.msg);\n}\n\ntemplate <\n    typename MessageMetadata,\n    BufferedPoller::message_handler_fn<MessageMetadata> Handler,\n    std::size_t MaxPadding,\n    typename Alignment>\nvoid BufferedPoller::add_handler()\n{\n  u32 idx = agent_internal_hash(MessageMetadata::rpc_id);\n\n  if (handlers_[idx] != nullptr) {\n    throw std::runtime_error(\"tried to add_handler to an occupied slot\");\n  }\n\n  handlers_[idx] = &BufferedPoller::message_handler_entrypoint<MessageMetadata, Handler, MaxPadding, Alignment>;\n}\n\nvoid BufferedPoller::handle_dns_message(message_metadata const &metadata, jb_agent_internal__dns_packet &msg)\n{\n  // we are assuming that struct id_addr is exactly 4 bytes and that struct\n  // in6_addr is exactly 16 bytes\n  //\n  static_assert(\n      sizeof(in_addr) == 4,\n      \"serialization protocol assumes IPv4 \"\n      \"address structure is 4 bytes exactly\");\n  static_assert(\n      sizeof(in6_addr) == 16,\n      \"serialization protocol assumes IPv6 address structure is 16 \"\n      \"bytes exactly\");\n\n  LOG::debug_in(AgentLogKind::DNS, \"handle_dns_message\");\n\n  u64 const sk = msg.sk;\n  auto const pkt_len = msg._len - jb_agent_internal__dns_packet__data_size;\n  auto const &dns_packet = metadata.padding;\n\n  /* sanity check the message */\n  if (dns_packet.data() + pkt_len > metadata.payload.data() + metadata.payload.size()) {\n    throw std::runtime_error(\"dns: garbled message length\");\n  }\n\n  // Look up the udp socket table entry\n  auto pos = udp_socket_table_.find(sk);\n  if (pos.index == udp_socket_table_.invalid) {\n    if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n      log_.error(\"ERROR: handle_dns_message - sk not found. sk={:x}\", sk);\n    }\n    return;\n  }\n  u32 sk_id = pos.index;\n\n  /* make variables to parse the DNS packet */\n  char hostname_out[DNS_NAME_MAX_LENGTH];\n  int hostname_len = 0;\n  int num_ipv4_addrs = MAX_ENCODED_IP_ADDRS;\n  struct in_addr ipv4_addrs[MAX_ENCODED_IP_ADDRS];\n  int num_ipv6_addrs = MAX_ENCODED_IP_ADDRS;\n  struct in6_addr ipv6_addrs[MAX_ENCODED_IP_ADDRS];\n\n  /* see if this is a request */\n  uint16_t type_out;\n  uint16_t qid_out;\n  int is_response;\n  int ret = dns_parse_query(dns_packet.data(), pkt_len, &is_response, &type_out, &qid_out, hostname_out, &hostname_len);\n  if (ret != ARES_SUCCESS) {\n    LOG::debug_in(\n        AgentLogKind::DNS,\n        \"dns_parse_query returned {}, total len {} valid len {} packet {:n}\",\n        ret,\n        msg.total_len,\n        pkt_len,\n        spdlog::to_hex(dns_packet.data(), dns_packet.data() + pkt_len));\n    return;\n  }\n  LOG::debug_in(\n      AgentLogKind::DNS,\n      \"dns_parse_query successful, total len {} valid len \"\n      \"{}\\nis_response {} type_out {} qid_out {} hostname {}\",\n      msg.total_len,\n      pkt_len,\n      is_response,\n      type_out,\n      qid_out,\n      std::string_view(hostname_out, hostname_len));\n\n  if (!is_response) {\n    DnsRequests::dns_request_key key{\n        .qid = qid_out, .type = type_out, .name = std::string(hostname_out, hostname_len), .is_rx = (bool)msg.is_rx};\n\n    // Add request to table, for later processing when response shows up\n    DnsRequests::dns_request_value value{.timestamp_ns = metadata.timestamp, .sk = sk};\n    dns_requests_.add(key, value);\n    return;\n  }\n\n  // looking for requests in the other direction\n  DnsRequests::dns_request_key key{\n      .qid = qid_out, .type = type_out, .name = std::string(hostname_out, hostname_len), .is_rx = !msg.is_rx};\n\n  // parse the reply\n  int send_a_aaaa_response = 0;\n  u16 sent_hostname_len = 0;\n  char *sent_hostname = NULL;\n\n  ret = dns_parse_a_aaaa_reply(\n      dns_packet.data(), pkt_len, hostname_out, &hostname_len, ipv4_addrs, &num_ipv4_addrs, ipv6_addrs, &num_ipv6_addrs);\n\n  if (hostname_len == 0 || (num_ipv4_addrs + num_ipv6_addrs) == 0) {\n    LOG::debug_in(\n        AgentLogKind::DNS,\n        \"dns_parse_a_aaaa_reply returned {}, total len {} valid len {} \"\n        \"packet {:n}\",\n        ret,\n        msg.total_len,\n        pkt_len,\n        spdlog::to_hex(dns_packet.data(), dns_packet.data() + pkt_len));\n    send_a_aaaa_response = 0;\n  } else {\n    /**\n     * we continue here even if packet was partial or corrupt, as long as we\n     * successfully parsed some IP addresses and the host name\n     */\n    LOG::debug_in(\n        AgentLogKind::DNS,\n        \"dns_parse_a_aaaa_reply successful, total len {} valid len \"\n        \"{}\\nnum_ipv4_addrs {} num_ipv6_addrs {}\",\n        msg.total_len,\n        pkt_len,\n        is_response,\n        type_out,\n        qid_out,\n        std::string_view(hostname_out, hostname_len));\n\n    send_a_aaaa_response = 1;\n    // truncate hostname */\n    sent_hostname_len = (hostname_len < DNS_NAME_MAX_LENGTH) ? (u16)hostname_len : DNS_NAME_MAX_LENGTH;\n    sent_hostname = hostname_out + hostname_len - sent_hostname_len;\n  }\n\n  // Only process DNS replies have have a matching request\n  // otherwise someone could be spoofing us\n  std::list<DnsRequests::Request> reqs;\n  dns_requests_.lookup(key, reqs);\n  if (reqs.size() > 0) {\n\n    /* see if this response matches requests we have seen */\n    for (auto &req : reqs) {\n\n      /* submit the dns response with latency information */\n      u64 request_timestamp = req->second.timestamp_ns;\n      u64 latency_ns = metadata.timestamp - request_timestamp;\n\n      if (send_a_aaaa_response) {\n        LOG::debug_in(\n            AgentLogKind::DNS,\n            \"sending DNS for hostname {} num_ipv4_addrs:{} \"\n            \"num_ipv6_addrs:{} latency_ns:{}\",\n            sent_hostname,\n            num_ipv4_addrs,\n            num_ipv6_addrs,\n            latency_ns);\n\n        // if the socket is exactly the same, then we match\n        bool matching = sk == req->second.sk;\n        if (!matching) {\n          // if it's not, but the port and address is exactly the same, then we\n          // also match\n          auto pos1 = udp_socket_table_.find(sk);\n          if (pos1.index == udp_socket_table_.invalid) {\n            if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n              log_.error(\"ERROR: handle_dns_message - sk not found. sk={:x}\", sk);\n            }\n          }\n          auto pos2 = udp_socket_table_.find(req->second.sk);\n          if (pos2.index == udp_socket_table_.invalid) {\n            if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n              log_.error(\"ERROR: handle_dns_message - sk2 not found. sk2={}\", req->second.sk);\n            }\n          }\n\n          // more strict would be this, thought i'm not sure port is necessarily\n          // the same in k8s environments either. matching =\n          // pos1.entry->pid==pos2.entry->pid &&\n          // pos1.entry->lport==pos2.entry->lport;\n          matching = pos1.entry->pid == pos2.entry->pid;\n\n          // why does the socket not match for request and response\n          if (!matching) {\n            LOG::debug_in(\n                AgentLogKind::DNS,\n                \"dns socket mismatch {}@{}:{}(pid={}) != {}@{}:{}(pid={})\\n\"\n                \"hostname: {}  num_ipv4_addrs:{}  num_ipv6_addrs:{}  latency: \"\n                \"{}\",\n                sk,\n                IPv6Address::from(pos1.entry->laddr),\n                pos1.entry->lport,\n                pos1.entry->pid,\n                req->second.sk,\n                IPv6Address::from(pos2.entry->laddr),\n                pos2.entry->lport,\n                pos2.entry->pid,\n                sent_hostname,\n                num_ipv4_addrs,\n                num_ipv6_addrs,\n                latency_ns);\n          }\n        }\n\n        // if receiving a dns response, this is a client and 'total time' is the\n        // appropriate metric if sending a dns response, this is a server and\n        // 'processing time' is the appropriate metric\n        writer_.dns_response_tstamp(\n            metadata.timestamp,\n            sk_id,\n            hostname_len,\n            /* domain_name */ jb_blob{sent_hostname, sent_hostname_len},\n            /* ipv4_addrs */\n            jb_blob{(char *)ipv4_addrs, (u16)(sizeof(u32) * num_ipv4_addrs)},\n            /* ipv6_addrs */\n            jb_blob{(char *)ipv6_addrs, (u16)(sizeof(struct in6_addr) * num_ipv6_addrs)},\n            latency_ns,\n            msg.is_rx ? SC_CLIENT : SC_SERVER);\n      }\n      // else {\n      //  // someday add other dns responses, or dns resolution errors\n      //}\n    }\n\n    /* remove request key */\n    dns_requests_.remove_all_with_key(key);\n  }\n}\n\nvoid BufferedPoller::timeout_dns_request(u64 timestamp_ns, const DnsRequests::Request &req)\n{\n  u64 t_req = req->second.timestamp_ns;\n  u64 sk = req->second.sk;\n\n  // Look up the udp socket table entry\n  auto pos = udp_socket_table_.find(sk);\n  if (pos.index == udp_socket_table_.invalid) {\n    if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n      // Just a debug message here, it's possible for a udp socket lifetime to\n      // race with the timeout\n      LOG::debug_in(AgentLogKind::DNS, \"ERROR: timeout_dns_request - sk not found. sk={:x}\", sk);\n    }\n  } else {\n\n    u32 sk_id = pos.index;\n    u64 duration_ns = (timestamp_ns - t_req);\n\n    /* truncate hostname */\n    const char *hostname_out = req->first.name.c_str();\n    size_t hostname_len = req->first.name.size();\n\n    u16 sent_hostname_len = (hostname_len < DNS_NAME_MAX_LENGTH) ? (u16)hostname_len : DNS_NAME_MAX_LENGTH;\n\n    const char *sent_hostname = hostname_out + hostname_len - sent_hostname_len;\n\n    LOG::debug_in(AgentLogKind::DNS, \"sending DNS timeout for hostname {} duration_ns {}\", sent_hostname, duration_ns);\n\n    writer_.dns_timeout_tstamp(\n        timestamp_ns,\n        sk_id,\n        hostname_len,\n        /* domain_name */ jb_blob{sent_hostname, sent_hostname_len},\n        duration_ns);\n  }\n\n  // drop this request from dns_requests_\n  dns_requests_.remove(req);\n}\n\nvoid BufferedPoller::slow_poll()\n{\n  u64 const t = monotonic() + time_adjustment_;\n  process_dns_timeouts(t);\n}\n\nvoid BufferedPoller::process_dns_timeouts(u64 t)\n{\n  std::list<DnsRequests::Request> old_reqs;\n  dns_requests_.lookup_older_than(t - DNS_TIMEOUT_TIME_NS, old_reqs);\n  for (auto &req : old_reqs) {\n    timeout_dns_request(t, req);\n  }\n}\n\nvoid BufferedPoller::handle_new_socket(message_metadata const &metadata, jb_agent_internal__new_sock_created &msg)\n{\n  /**\n   * NOTE: this is not the only handling of new sockets (contrary to name).\n   *   reset_tcp_counters also reports a new socket.\n   */\n\n  LOG::debug_in(AgentLogKind::TCP, \"handle_new_socket: sk={:x} pid={}\", msg.sk, msg.pid);\n\n  if (tcp_socket_table_.full()) {\n    log_.warn(\"handle_new_socket: tcp socket table is full! dropping socket\");\n    tcp_socket_table_ever_full_ = true;\n    return;\n  }\n\n  auto pos = tcp_socket_table_.insert(msg.sk);\n  if (pos.index != tcp_socket_table_.invalid) {\n    tcp_index_to_sk_[pos.index] = msg.sk;\n  } else {\n    log_.error(\"handle_new_socket: duplicate tcp socket sk={:x} pid={}\", msg.sk, msg.pid);\n    return;\n  }\n\n  writer_.new_sock_info_tstamp(metadata.timestamp, msg.pid, msg.sk);\n}\n\nvoid BufferedPoller::handle_set_state_ipv4(message_metadata const &metadata, jb_agent_internal__set_state_ipv4 &msg)\n{\n  LOG::trace_in(\n      AgentLogKind::TCP,\n      \"handle_set_state_ipv4: sk:{:x}, {}:{} -> {}:{} (tx_rx={})\",\n      msg.sk,\n      IPv4Address::from(msg.src),\n      msg.sport,\n      IPv4Address::from(msg.dest),\n      msg.dport,\n      msg.tx_rx);\n\n  // Ensure that if we get a state_set_ipv4 that the socket is something that\n  // exists in our table already\n  auto pos = tcp_socket_table_.find(msg.sk);\n  if (pos.index == tcp_socket_table_.invalid) {\n    if (!tcp_socket_table_ever_full_ && all_probes_loaded_) {\n      log_.error(\"handle_set_state_ipv4: socket not tracked sk={:x}\", msg.sk);\n    }\n    return;\n  }\n\n  writer_.set_state_ipv4_tstamp(metadata.timestamp, msg.dest, msg.src, msg.dport, msg.sport, msg.sk, msg.tx_rx);\n\n  nat_handler_.handle_set_state_ipv4(metadata.timestamp, &msg);\n}\n\nvoid BufferedPoller::handle_set_state_ipv6(message_metadata const &metadata, jb_agent_internal__set_state_ipv6 &msg)\n{\n  LOG::trace_in(\n      AgentLogKind::TCP,\n      \"handle_set_state_ipv6: sk:{}, {}:{} -> {}:{} (tx_rx={})\",\n      msg.sk,\n      IPv6Address::from(msg.src),\n      msg.sport,\n      IPv6Address::from(msg.dest),\n      msg.dport,\n      msg.tx_rx);\n\n  // Ensure that if we get a state_set_ipv4 that the socket is something that\n  // exists in our table already\n  auto pos = tcp_socket_table_.find(msg.sk);\n  if (pos.index == tcp_socket_table_.invalid) {\n    if (!tcp_socket_table_ever_full_ && all_probes_loaded_) {\n      log_.error(\"handle_set_state_ipv6: socket not tracked sk={:x}\", msg.sk);\n    }\n    return;\n  }\n\n  writer_.set_state_ipv6_tstamp(metadata.timestamp, msg.dest, msg.src, msg.dport, msg.sport, msg.sk, msg.tx_rx);\n  nat_handler_.handle_set_state_ipv6(metadata.timestamp, &msg);\n}\n\nvoid BufferedPoller::handle_close_socket(message_metadata const &metadata, jb_agent_internal__close_sock_info &msg)\n{\n  auto pos = tcp_socket_table_.find(msg.sk);\n  if (pos.index == tcp_socket_table_.invalid) {\n    // This should be prevented in BPF except when the socket table was full\n    if (!tcp_socket_table_ever_full_ && all_probes_loaded_) {\n      log_.error(\"handle_close_socket: socket not found sk={:x}\", msg.sk);\n    }\n    return;\n  }\n\n  LOG::debug_in(AgentLogKind::TCP, \"handle_close_socket: sk={:x}\", msg.sk);\n\n  // send out a statistics message if needed\n  for (u32 epoch = 0; epoch < n_epochs; epoch++) {\n    auto &stats = tcp_socket_stats_.lookup_relative(pos.index, epoch, false).second;\n    if (stats.valid == true) {\n      send_socket_stats(metadata.timestamp, msg.sk, stats);\n    }\n  }\n\n  bool success = tcp_socket_table_.erase(msg.sk);\n  if (!success) {\n    throw std::runtime_error(fmt::format(\"handle_close_socket: removing socket from table failed sk={:x}\", msg.sk));\n  }\n\n  writer_.close_sock_info_tstamp(metadata.timestamp, msg.sk);\n  nat_handler_.handle_close_socket(metadata.timestamp, &msg);\n\n  // Also clean up any tcp data protocol handlers this socket may have\n  // associated with it\n  tcp_data_handler_->handle_close_socket(msg.sk);\n}\n\nvoid BufferedPoller::handle_rtt_estimator(message_metadata const &metadata, jb_agent_internal__rtt_estimator &msg)\n{\n  auto pos = tcp_socket_table_.find(msg.sk);\n  if (pos.index == tcp_socket_table_.invalid) {\n    if (!tcp_socket_table_ever_full_ && all_probes_loaded_) {\n      LOG::debug_in(AgentLogKind::TCP, \"handle_rtt_estimator: rtt_estimator on unknown socket sk={:x}\", msg.sk);\n    }\n    return;\n  }\n  auto entry = pos.entry;\n\n  /* we have a valid position. will go on to compute statistics */\n  u64 diff_bytes_acked = msg.bytes_acked - entry->bytes_acked;\n  u32 diff_delivered = msg.packets_delivered - entry->packets_delivered;\n  u32 diff_retrans = msg.packets_retrans - entry->packets_retrans;\n\n  u64 diff_bytes_received = msg.bytes_received - entry->bytes_received;\n  u32 diff_rcv_holes = msg.rcv_holes - entry->rcv_holes;\n  u32 diff_rcv_delivered = msg.rcv_delivered - entry->rcv_delivered;\n\n  /* differences should be positive, if interpreted as signed numbers.\n     avoid accumulating these huge diffs in stats below. */\n  if (((s64)diff_bytes_acked) < ((s64)0)) {\n    diff_bytes_acked = 0;\n  }\n  if (((s32)diff_delivered) < ((s32)0)) {\n    // on kernels < 4.6, packets_delivered is an estimate and can under-count\n    // and even become negative. make sure we're reporting non-negative numbers\n    diff_delivered = 0;\n  }\n  if (((s32)diff_retrans) < ((s32)0)) {\n    diff_retrans = 0;\n  }\n  if (((s64)diff_bytes_received) < ((s64)0)) {\n    diff_bytes_received = 0;\n  }\n  if (((s32)diff_rcv_holes) < ((s32)0)) {\n    diff_rcv_holes = 0;\n  }\n  if (((s32)diff_rcv_delivered) < ((s32)0)) {\n    diff_rcv_delivered = 0;\n  }\n\n  /* update the entry for next time */\n  entry->bytes_acked = msg.bytes_acked;\n  entry->packets_delivered = msg.packets_delivered;\n  entry->packets_retrans = msg.packets_retrans;\n\n  entry->bytes_received = msg.bytes_received;\n  entry->rcv_holes = msg.rcv_holes;\n  entry->rcv_delivered = msg.rcv_delivered;\n\n  /* find the statistics, and ask it to enqueue */\n  auto &stats = tcp_socket_stats_.lookup(pos.index, metadata.timestamp, true).second;\n\n  /* if stats were invalid, reset the values */\n  if (!stats.valid) {\n    stats.diff_bytes_acked = diff_bytes_acked;\n    stats.diff_delivered = diff_delivered;\n    stats.diff_retrans = diff_retrans;\n    stats.max_srtt = msg.srtt;\n\n    stats.diff_bytes_received = diff_bytes_received;\n    stats.diff_rcv_holes = diff_rcv_holes;\n    stats.diff_rcv_delivered = diff_rcv_delivered;\n    stats.max_rcv_rtt = msg.rcv_rtt;\n    stats.valid = true;\n  } else {\n    stats.diff_bytes_acked += diff_bytes_acked;\n    stats.diff_delivered += diff_delivered;\n    stats.diff_retrans += diff_retrans;\n    stats.max_srtt = std::max(stats.max_srtt, msg.srtt);\n\n    stats.diff_bytes_received += diff_bytes_received;\n    stats.diff_rcv_holes += diff_rcv_holes;\n    stats.diff_rcv_delivered += diff_rcv_delivered;\n    stats.max_rcv_rtt = std::max(stats.max_rcv_rtt, msg.rcv_rtt);\n  }\n}\n\nvoid BufferedPoller::handle_reset_tcp_counters(message_metadata const &metadata, jb_agent_internal__reset_tcp_counters &msg)\n{\n\n  LOG::debug_in(AgentLogKind::TCP, \"handle_reset_tcp_counters: sk={:x} pid={}\", msg.sk, msg.pid);\n\n  // first, write telemetry like handle_new_socket\n  writer_.new_sock_info_tstamp(metadata.timestamp, msg.pid, msg.sk);\n\n  if (tcp_socket_table_.full()) {\n    log_.warn(\"handle_reset_tcp_counters: tcp socket table is full! dropping socket\");\n    tcp_socket_table_ever_full_ = true;\n    return;\n  }\n\n  auto pos = tcp_socket_table_.insert(msg.sk);\n  if (pos.index != tcp_socket_table_.invalid) {\n    tcp_index_to_sk_[pos.index] = msg.sk;\n  } else {\n    log_.error(\"handle_reset_tcp_counters: duplicate tcp socket sk={:x} pid={}\", msg.sk, msg.pid);\n    return;\n  }\n  auto entry = pos.entry;\n\n  entry->bytes_acked = msg.bytes_acked;\n  entry->packets_delivered = msg.packets_delivered;\n  entry->packets_retrans = msg.packets_retrans;\n  entry->bytes_received = msg.bytes_received;\n}\n\nvoid BufferedPoller::handle_tcp_syn_timeout(message_metadata const &metadata, jb_agent_internal__tcp_syn_timeout &msg)\n{\n  writer_.syn_timeout_tstamp(metadata.timestamp, msg.sk);\n}\n\nvoid BufferedPoller::handle_tcp_reset(message_metadata const &metadata, jb_agent_internal__tcp_reset &msg)\n{\n  writer_.tcp_reset_tstamp(metadata.timestamp, msg.sk, msg.is_rx);\n}\n\nvoid BufferedPoller::handle_http_response(message_metadata const &metadata, jb_agent_internal__http_response &msg)\n{\n  LOG::debug_in(\n      AgentLogKind::HTTP,\n      \"handle_http_response: timestamp={}, sk={:x}, pid={}, code={}, \"\n      \"latency_ns={}, client_server={}\",\n      metadata.timestamp,\n      msg.sk,\n      msg.pid,\n      msg.code,\n      msg.latency_ns,\n      client_server_type_to_string((enum CLIENT_SERVER_TYPE)msg.client_server));\n\n  writer_.http_response_tstamp(metadata.timestamp, msg.sk, msg.pid, msg.code, msg.latency_ns, msg.client_server);\n}\n\nvoid BufferedPoller::send_socket_stats(u64 t, u64 sk, tcp_statistics &stats)\n{\n  if ((stats.diff_bytes_acked > 0) || (stats.diff_retrans > 0)) {\n    writer_.socket_stats_tstamp(t, sk, stats.diff_bytes_acked, stats.diff_delivered, stats.diff_retrans, stats.max_srtt, 0);\n  }\n\n  if ((stats.diff_bytes_received > 0) || (stats.diff_rcv_holes > 0)) {\n    writer_.socket_stats_tstamp(\n        t, sk, stats.diff_bytes_received, stats.diff_rcv_delivered, stats.diff_rcv_holes, stats.max_rcv_rtt, 1);\n  }\n\n  stats.valid = false;\n}\n\nvoid BufferedPoller::send_stats_from_queue(u64 t)\n{\n  if (tcp_socket_stats_.relative_timeslot(t) == 0) {\n    /* not ready */\n    return;\n  }\n\n  auto &queue = tcp_socket_stats_.current_queue();\n  while (!queue.empty()) {\n    /* get the next index */\n    u32 index = queue.peek();\n\n    /* get the stats entry for that index */\n    auto &stats = tcp_socket_stats_.lookup_relative(index, 0, false).second;\n\n    if (stats.valid) {\n      /* write the message */\n      send_socket_stats(t, tcp_index_to_sk_[index], stats);\n\n      /* send_socket_stats sets stats.valid = false */\n    }\n    queue.pop();\n  }\n\n  /* done. advance the current timeslot */\n  tcp_socket_stats_.advance();\n}\n\nvoid BufferedPoller::handle_udp_new_socket(message_metadata const &metadata, jb_agent_internal__udp_new_socket &msg)\n{\n  LOG::debug_in(\n      AgentLogKind::UDP,\n      \"handle_udp_new_socket: sk={:x} pid={} laddr={} lport={}\",\n      msg.sk,\n      msg.pid,\n      IPv6Address::from(msg.laddr),\n      msg.lport);\n\n  /* first, insert into table */\n  if (udp_socket_table_.full()) {\n    log_.warn(\"handle_udp_new_socket: udp socket table full! dropping socket\");\n    udp_socket_table_ever_full_ = true;\n    return;\n  }\n  auto pos = udp_socket_table_.insert(msg.sk);\n  if (pos.index == udp_socket_table_.invalid) {\n    log_.error(\"handle_udp_new_socket: duplicate udp socket\");\n    return;\n  }\n  pos.entry->pid = msg.pid;\n  pos.entry->sk = msg.sk;\n  memcpy(&pos.entry->laddr, msg.laddr, sizeof(struct in6_addr));\n  pos.entry->lport = msg.lport;\n\n  udp_send_new_socket(metadata.timestamp, pos.entry, pos.index);\n\n  //\tchar ipaddr6_buf[INET6_ADDRSTRLEN];\n  //\tconst char *addr_s = inet_ntop(AF_INET6, &msg.laddr, ipaddr6_buf,\n  // INET6_ADDRSTRLEN);\n  //\n  //\tstd::cout << (reported ? \"MSG: \": \"\")\n  //\t\t\t<< \"UDP existing socket pid \" << msg.pid\n  //\t\t\t<< \" sk \" << msg.sk\n  //\t\t\t<< \" lport \" << msg.lport\n  //\t\t\t<< \" laddr \" << addr_s << std::endl;\n}\n\nvoid BufferedPoller::handle_udp_destroy_socket(message_metadata const &metadata, jb_agent_internal__udp_destroy_socket &msg)\n{\n  LOG::debug_in(AgentLogKind::UDP, \"handle_udp_destroy_socket: sk={:x}\", msg.sk);\n\n  auto pos = udp_socket_table_.find(msg.sk);\n  if (pos.index == udp_socket_table_.invalid) {\n    if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n      LOG::debug_in(AgentLogKind::UDP, \"handle_udp_destroy_socket: socket not found sk={:x}\", msg.sk);\n    }\n    return;\n  }\n\n  // Ensure dns queries on this socket are timed out\n  std::list<DnsRequests::Request> reqs;\n  dns_requests_.lookup_socket(msg.sk, reqs);\n  for (auto &req : reqs) {\n    timeout_dns_request(metadata.timestamp, req);\n  }\n\n  /* send out statistics message if available */\n  if (pos.entry->reported) {\n    for (int is_rx = 0; is_rx < 2; is_rx++) {\n      for (u32 epoch = 0; epoch < n_epochs; epoch++) {\n        auto &stats = udp_socket_stats_[is_rx].lookup_relative(pos.index, epoch, false).second;\n        if (stats.valid == true)\n          udp_send_stats(metadata.timestamp, pos.index, is_rx, *pos.entry, stats);\n      }\n    }\n    /* notify of the destruction */\n    writer_.udp_destroy_socket_tstamp(metadata.timestamp, pos.index);\n  }\n\n  bool success = udp_socket_table_.erase(msg.sk);\n  if (!success) {\n    log_.error(\"handle_udp_destroy_socket: removing socket from table failed sk={:x}\", msg.sk);\n  }\n}\n\nvoid BufferedPoller::handle_udp_stats(message_metadata const &metadata, jb_agent_internal__udp_stats &msg)\n{\n  auto pos = udp_socket_table_.find(msg.sk);\n  if (pos.index == udp_socket_table_.invalid) {\n    // bpf should prevent this unless the socket table was ever full\n    if (!udp_socket_table_ever_full_ && all_probes_loaded_) {\n      // kprobe is inserted only after steady state, so this should never happen\n      log_.error(\"handle_udp_stats: stats for missing socket sk={:x}\", msg.sk);\n    }\n    return;\n  }\n  auto &entry = *pos.entry;\n\n  /* find the statistics, and ask it to enqueue */\n  u8 is_rx = msg.is_rx;\n  auto &stats = udp_socket_stats_[is_rx].lookup(pos.index, metadata.timestamp, true).second;\n\n  /* if stats are valid and address changed, output the previous stat and update\n   * address */\n  if (msg.changed_af != 0) {\n    /* there might be statistics for a different address */\n    if (stats.valid) {\n      /* send the stats. will clear the stats */\n      udp_send_stats(metadata.timestamp, pos.index, is_rx, entry, stats);\n    }\n\n    // Lookup whether this is a NAT-ed connection if this is ipv4\n    hostport_tuple *ft = nullptr;\n    if (msg.changed_af == AF_INET) {\n      // NOTE: the bpf code always has a changed_af event before\n      // the first statistics are sent, since the remote_addr in the bpf\n      // table is initialized to 0:0.\n      u32 laddr = ((u32 *)msg.laddr)[3];\n      u32 raddr = ((u32 *)msg.raddr)[3];\n      ft = nat_handler_.get_nat_mapping(laddr, raddr, ntohs(msg.lport), ntohs(msg.rport), IPPROTO_UDP);\n    }\n\n    /* set the address */\n    auto &addr = entry.addrs[is_rx];\n    if (ft != nullptr) {\n      u32 laddr[4] = {0, 0, 0xffff0000, ft->src_ip};\n      memcpy(&entry.laddr, laddr, sizeof(struct in6_addr));\n      entry.lport = ntohs(ft->src_port);\n\n      u32 raddr[4] = {0, 0, 0xffff0000, ft->dst_ip};\n      memcpy(&addr.addr, raddr, sizeof(struct in6_addr));\n      addr.port = ntohs(ft->dst_port);\n    } else {\n      memcpy(&entry.laddr, msg.laddr, sizeof(struct in6_addr));\n      entry.lport = ntohs(msg.lport);\n\n      memcpy(&addr.addr, msg.raddr, sizeof(struct in6_addr));\n      addr.port = ntohs(msg.rport);\n    }\n    addr.changed_af = msg.changed_af;\n\n    // fast track stats for address changes. we can't delay pushing address\n    // changes to the server, because the next messages (dns\n    // responses/timeouts/etc for example) may require the address to be set\n    udp_send_stats(metadata.timestamp, pos.index, is_rx, entry, stats);\n  }\n\n  if (!stats.valid) {\n    /* if stats were invalid, reset the values */\n    stats.packets = msg.packets;\n    stats.bytes = msg.bytes;\n    stats.drops = msg.drops;\n    stats.valid = true;\n  } else {\n    /* stats were valid, aggregate */\n    stats.packets += msg.packets;\n    stats.bytes += msg.bytes;\n    stats.drops += msg.drops;\n  }\n\n  char lipaddr6_buf[INET6_ADDRSTRLEN];\n  const char *laddr_s = inet_ntop(AF_INET6, &msg.laddr, lipaddr6_buf, INET6_ADDRSTRLEN);\n  char ripaddr6_buf[INET6_ADDRSTRLEN];\n  const char *raddr_s = inet_ntop(AF_INET6, &msg.raddr, ripaddr6_buf, INET6_ADDRSTRLEN);\n\n  LOG::trace_in(\n      AgentLogKind::UDP,\n      \"UDP {} sk {:x} sk_id {} laddr {} lport {} raddr {} rport {} \"\n      \"packets {} bytes {} changed_af {} drops {}\",\n      (msg.is_rx ? \"RX\" : \"TX\"),\n      msg.sk,\n      pos.index,\n      laddr_s,\n      ntohs(msg.lport),\n      raddr_s,\n      ntohs(msg.rport),\n      msg.packets,\n      msg.bytes,\n      int(msg.changed_af),\n      int(msg.drops));\n}\n\nvoid BufferedPoller::handle_pid_info(message_metadata const &metadata, jb_agent_internal__pid_info &msg)\n{\n  pid_count_++;\n\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::debug_in(\n      AgentLogKind::PID,\n      \"{}: pid={} parent={} cgroup=0x{:x} comm='{}' pid_count_={}\",\n      __func__,\n      msg.pid,\n      msg.parent_pid,\n      msg.cgroup,\n      comm,\n      pid_count_);\n\n  cgroup_handler_.handle_pid_info(msg.pid, msg.cgroup, msg.comm);\n  process_handler_.on_new_process(std::chrono::nanoseconds{metadata.timestamp}, msg);\n\n  // Read the process command-line from /proc/PID/cmdline.\n  // By this time the process could have exited, so reading this entry can fail.\n  // We are using the `try_read_proc_cmdline` function which ignores the\n  // \"no such file or directory\" error and returns an empty string instead.\n  std::string cmdline;\n  if (auto r = try_read_proc_cmdline(msg.pid)) {\n    cmdline = *r;\n  } else {\n    log_.error(\"handle_pid_info: error reading cmdline for pid={}: {}\", msg.pid, r.error());\n  }\n\n  writer_.pid_info_create_tstamp(metadata.timestamp, msg.pid, msg.comm, msg.cgroup, msg.parent_pid, jb_blob(cmdline));\n}\n\nvoid BufferedPoller::handle_pid_close(message_metadata const &metadata, jb_agent_internal__pid_close &msg)\n{\n  pid_count_--;\n\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::debug_in(AgentLogKind::PID, \"{}: pid={} comm='{}' pid_count_={}\", __func__, msg.pid, comm, pid_count_);\n\n  process_handler_.on_process_end(std::chrono::nanoseconds{metadata.timestamp}, msg);\n  writer_.pid_close_info_tstamp(metadata.timestamp, msg.pid, msg.comm);\n}\n\nvoid BufferedPoller::handle_pid_set_comm(message_metadata const &metadata, jb_agent_internal__pid_set_comm &msg)\n{\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::debug_in(AgentLogKind::PID, \"{}: pid={} comm='{}'\", __func__, msg.pid, comm);\n\n  process_handler_.set_process_command(std::chrono::nanoseconds{metadata.timestamp}, msg);\n  writer_.pid_set_comm_tstamp(metadata.timestamp, msg.pid, msg.comm);\n}\n\nvoid BufferedPoller::handle_pid_exit(message_metadata const &metadata, jb_agent_internal__pid_exit &msg)\n{\n  LOG::debug_in(AgentLogKind::PID, \"{}: tgid={} pid={} exit_code={}\", __func__, msg.tgid, msg.pid, msg.exit_code);\n\n  process_handler_.pid_exit(std::chrono::nanoseconds{metadata.timestamp}, msg);\n}\n\nvoid trace_print_udp_socket_entry(const std::string_view &location, udp_socket_entry *entry)\n{\n  LOG::trace_in(\n      AgentLogKind::UDP,\n      \"{}: udp_socket_entry\\n\"\n      \"  laddr={}  lport={}\\n\"\n      \"  reported={}  pid={}  sk={:x}\\n\"\n      \"  addrs[TX]: addr={}  port={}  changed_af={}\\n\"\n      \"  addrs[RX]: addr={}  port={}  changed_af={}\",\n      location,\n      IPv6Address::from(entry->laddr),\n      entry->lport,\n      entry->reported,\n      entry->pid,\n      entry->sk,\n      IPv6Address::from(entry->addrs[0].addr),\n      entry->addrs[0].port,\n      entry->addrs[0].changed_af,\n      IPv6Address::from(entry->addrs[1].addr),\n      entry->addrs[1].port,\n      entry->addrs[1].changed_af);\n}\n\nvoid BufferedPoller::udp_send_new_socket(u64 ts, udp_socket_entry *entry, u64 index)\n{\n  trace_print_udp_socket_entry(\"udp_send_new_socket\", entry);\n\n  if (entry->lport != 0) {\n    writer_.udp_new_socket_tstamp(ts, entry->pid, index, (uint8_t *)(entry->laddr.data()), entry->lport);\n    entry->reported = true;\n  }\n}\n\nvoid BufferedPoller::udp_send_stats(u64 t, u32 sk_id, u8 is_rx, udp_socket_entry &entry, udp_statistics &stats)\n{\n  trace_print_udp_socket_entry(\"udp_send_stats\", &entry);\n\n  auto &addr = entry.addrs[is_rx];\n  if (entry.reported == false) {\n    LOG::trace(\"BufferedPoller::udp_send_stats - entry.reported == false\");\n    udp_send_new_socket(t, &entry, sk_id);\n  }\n\n  switch (addr.changed_af) {\n  case AF_INET:\n    writer_.udp_stats_addr_changed_v4_tstamp(t, sk_id, is_rx, stats.packets, stats.bytes, addr.addr[3], addr.port);\n    // TODO: Can uncomment this once we decide to support laddr info\n    // writer_.udp_stats_addr_changed_v4_tstamp(\n    //     t, sk_id, is_rx, stats.packets, stats.bytes, addr.addr[3], addr.port,\n    //     entry.laddr[3], entry.lport);\n    break;\n  case AF_INET6:\n    writer_.udp_stats_addr_changed_v6_tstamp(t, sk_id, is_rx, stats.packets, stats.bytes, (u8 *)&addr.addr, addr.port);\n    // TODO: Can uncomment this once we decide to support laddr info\n    // writer_.udp_stats_addr_changed_v6_tstamp(t, sk_id, is_rx, stats.packets,\n    //                                          stats.bytes, (u8 *)&addr.addr,\n    //                                          addr.port, (u8*)&entry.laddr,\n    //                                          entry.lport);\n    break;\n  default:\n    writer_.udp_stats_addr_unchanged_tstamp(t, sk_id, is_rx, stats.packets, stats.bytes);\n    break;\n  }\n\n  // Send drops for receive side only, if we have drops\n  if (is_rx && stats.drops > 0) {\n    writer_.udp_stats_drops_changed_tstamp(t, sk_id, stats.drops);\n  }\n\n  if (is_log_whitelisted(AgentLogKind::UDP)) {\n    LOG::trace_in(\n        AgentLogKind::UDP,\n        \"BufferedPoller::udp_send_stats - sk_id: {}, raddr: {}, rport: {}, \"\n        \"packets: {}, bytes: {}, changed_af: {}, drops: {}\",\n        sk_id,\n        IPv6Address::from(addr.addr),\n        addr.port,\n        stats.packets,\n        stats.bytes,\n        int(addr.changed_af),\n        stats.drops);\n  }\n\n  /* don't need to report the address next time if it doesn't change */\n  addr.changed_af = 0;\n\n  /* the current statistic is invalid, don't try to send */\n  stats.valid = false;\n}\n\nvoid BufferedPoller::udp_send_stats_from_queue(u64 t)\n{\n  for (int is_rx = 0; is_rx < 2; is_rx++) {\n    auto &store = udp_socket_stats_[is_rx];\n\n    if (store.relative_timeslot(t) == 0) {\n      /* not ready */\n      continue;\n    }\n\n    auto &queue = store.current_queue();\n    while (!queue.empty()) {\n      /* get the next index */\n      u32 index = queue.peek();\n\n      /* get the stats entry for that index */\n      auto &stats = store.lookup_relative(index, 0, false).second;\n\n      if (stats.valid) {\n        /* write the message */\n        udp_send_stats(t, index, is_rx, udp_socket_table_[index], stats);\n\n        /* udp_send_stats sets stats.valid = false */\n      }\n\n      queue.pop();\n    }\n\n    /* done. advance the current timeslot */\n    store.advance();\n  }\n}\n\nu32 BufferedPoller::u64_hasher::operator()(u64 const &s) const noexcept\n{\n  return lookup3_hashword((u32 *)&s, sizeof(u64) / 4, 0x7AFBAF00);\n}\n\nvoid BufferedPoller::handle_kill_css(message_metadata const &metadata, jb_agent_internal__kill_css &msg)\n{\n  cgroup_handler_.kill_css(metadata.timestamp, &msg);\n\n  writer_.cgroup_close_tstamp(metadata.timestamp, msg.cgroup);\n}\n\nvoid BufferedPoller::handle_css_populate_dir(message_metadata const &metadata, jb_agent_internal__css_populate_dir &msg)\n{\n  cgroup_handler_.css_populate_dir(metadata.timestamp, &msg);\n\n  writer_.cgroup_create_tstamp(metadata.timestamp, msg.cgroup, msg.cgroup_parent, msg.name);\n}\n\nvoid BufferedPoller::handle_existing_cgroup_probe(\n    message_metadata const &metadata, jb_agent_internal__existing_cgroup_probe &msg)\n{\n  cgroup_handler_.existing_cgroup_probe(metadata.timestamp, &msg);\n\n  writer_.cgroup_create_tstamp(metadata.timestamp, msg.cgroup, msg.cgroup_parent, msg.name);\n}\n\nvoid BufferedPoller::handle_cgroup_attach_task(message_metadata const &metadata, jb_agent_internal__cgroup_attach_task &msg)\n{\n  cgroup_handler_.cgroup_attach_task(metadata.timestamp, &msg);\n\n  process_handler_.on_cgroup_move(std::chrono::nanoseconds{metadata.timestamp}, msg);\n\n  writer_.pid_cgroup_move_tstamp(metadata.timestamp, msg.pid, msg.cgroup);\n}\n\nvoid BufferedPoller::handle_nf_nat_cleanup_conntrack(\n    message_metadata const &metadata, jb_agent_internal__nf_nat_cleanup_conntrack &msg)\n{\n  nat_handler_.handle_nf_nat_cleanup_conntrack(metadata.timestamp, &msg);\n}\n\nvoid BufferedPoller::handle_nf_conntrack_alter_reply(\n    message_metadata const &metadata, jb_agent_internal__nf_conntrack_alter_reply &msg)\n{\n  nat_handler_.handle_nf_conntrack_alter_reply(metadata.timestamp, &msg);\n}\n\nvoid BufferedPoller::handle_existing_conntrack_tuple(\n    message_metadata const &metadata, jb_agent_internal__existing_conntrack_tuple &msg)\n{\n  nat_handler_.handle_existing_conntrack_tuple(metadata.timestamp, &msg);\n}\n\nvoid BufferedPoller::handle_bpf_log(message_metadata const &metadata, jb_agent_internal__bpf_log &msg)\n{\n  // TODO: since removing the pre-processor, we have no line number information\n  auto const linenumber = 0;\n  std::string_view const filename = \"TODO\";\n\n  writer_.bpf_log(jb_blob{filename}, linenumber, msg.code, msg.arg0, msg.arg1, msg.arg2);\n}\n\nvoid BufferedPoller::handle_stack_trace(message_metadata const &metadata, jb_agent_internal__stack_trace &msg)\n{\n#if DEBUG_ENABLE_STACKTRACE\n  std::string stacktrace = probe_handler_.get_stack_trace(skel_, msg.kernel_stack_id, msg.user_stack_id, msg.tgid);\n  LOG::debug_in(\n      AgentLogKind::BPF,\n      \"stack_trace: timestamp={}, kernel_stack_id={}, \"\n      \"user_stack_id={}, tgid={}, comm={}\\n{}\\n\",\n      metadata.timestamp,\n      msg.kernel_stack_id,\n      msg.user_stack_id,\n      msg.tgid,\n      std::string_view((char *)msg.comm, strnlen((char *)msg.comm, sizeof(msg.comm))),\n      stacktrace);\n#endif\n}\n\nvoid BufferedPoller::handle_tcp_data(message_metadata const &metadata, jb_agent_internal__tcp_data &msg)\n{\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"tcp_data: idx={}, timestamp={}, sk={:x}, pid={}, length={}, offset={}, \"\n      \"stream_type={}({}), client_server={}({})\\n\",\n      metadata.cpu_index,\n      metadata.timestamp,\n      msg.sk,\n      msg.pid,\n      msg.length,\n      msg.offset,\n      msg.stream_type,\n      stream_type_to_string((enum STREAM_TYPE)msg.stream_type),\n      msg.client_server,\n      client_server_type_to_string((enum CLIENT_SERVER_TYPE)msg.client_server));\n\n  tcp_data_handler_->process(\n      metadata.cpu_index,\n      metadata.timestamp,\n      msg.sk,\n      msg.pid,\n      msg.length,\n      msg.offset,\n      (STREAM_TYPE)msg.stream_type,\n      (CLIENT_SERVER_TYPE)msg.client_server);\n}\n\nvoid BufferedPoller::set_all_probes_loaded()\n{\n  all_probes_loaded_ = true;\n}\n\n#ifndef NDEBUG\nvoid BufferedPoller::debug_bpf_lost_samples()\n{\n  debug_bpf_lost_samples_ = true;\n}\n#endif\n"
  },
  {
    "path": "collector/kernel/buffered_poller.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n\n#include <util/curl_engine.h>\n#include <util/fast_div.h>\n#include <util/file_ops.h>\n#include <util/logger.h>\n#include <util/metric_store.h>\n\n#include <channel/buffered_writer.h>\n#include <collector/kernel/bpf_src/render_bpf.h>\n#include <collector/kernel/cgroup_handler.h>\n#include <collector/kernel/dns_requests.h>\n#include <collector/kernel/nat_handler.h>\n#include <collector/kernel/perf_poller.h>\n#include <collector/kernel/probe_handler.h>\n#include <collector/kernel/process_handler.h>\n#include <collector/kernel/socket_table.h>\n#include <collector/kernel/tcp_data_handler.h>\n#include <generated/ebpf_net/agent_internal/hash.h>\n#include <generated/ebpf_net/ingest/encoder.h>\n#include <generated/ebpf_net/ingest/writer.h>\n#include <generated/ebpf_net/kernel_collector/index.h>\n\n#include <memory>\n\n// Forward declaration for the skeleton\nstruct render_bpf_bpf;\n\nclass KernelCollectorRestarter;\n\n/**\n * A BufferedPoller that empties the perf ring every poll\n * and sends the info to a specified channel\n */\nclass BufferedPoller : public PerfPoller {\npublic:\n  // The hash map needs to be some 20% larger than the max number of elements.\n  // Otherwise you'd start getting hash failures before all the elements are\n  // exhausted\n  static constexpr u32 tcp_socket_table_max_sockets = TABLE_SIZE__TCP_OPEN_SOCKETS;\n  static constexpr u32 udp_socket_table_max_sockets = TABLE_SIZE__UDP_OPEN_SOCKETS;\n\n  static constexpr u32 n_epochs = 2;\n\n  /**\n   * c'tor\n   * throws if buff_ can't be malloc-ed\n   * @param loop: the libuv event loop on which to receive perf events\n   * @param container: the perf container to extract messages from\n   * @param writer: the writer using which to send messages\n   * @param time_adjustment: how much to add to CLOCK_MONOTONIC when comparing\n   *   to ring timestamp\n   */\n  BufferedPoller(\n      uv_loop_t &loop,\n      PerfContainer &container,\n      IBufferedWriter &writer,\n      u64 time_adjustment,\n      CurlEngine &curl_engine,\n      FileDescriptor &bpf_dump_file,\n      logging::Logger &log,\n      ProbeHandler &probe_handler,\n      struct render_bpf_bpf *skel,\n      u64 socket_stats_interval_sec,\n      CgroupHandler::CgroupSettings const &cgroup_settings,\n      ::ebpf_net::ingest::Encoder *encoder,\n      KernelCollectorRestarter &kernel_collector_restarter);\n\n  /**\n   * batches as many entries into buffer as possible before calling\n   * send_buffer(). always flushes the buffer at the end.\n   *\n   * there are four possible cases where it will throw:\n   * 1) a record is larger than an empty buffer\n   * 2) a record of unknown type was read\n   * 3) call to send_buffer failed (there are two call locations)\n   *\n   */\n  void process_samples(bool is_event);\n\n  /**\n   * entrypoint for event-driven polling when the perf container announces\n   * it is half full or whatever notification limit is set.\n   * calls through to process_samples()\n   */\n  void handle_event();\n\n  /**\n   * entrypoint for manual polling. calls through to process_samples()\n   * @see PerfPoller::poll\n   */\n\n  virtual void poll();\n\n  /**\n   * Sends a bpf loss notification to backend, if a loss happened since\n   *   the last notification call\n   */\n  void send_report_if_recent_loss();\n\n  /**\n   * accessor for lost_count_\n   */\n  u64 serv_lost_count();\n\n  void slow_poll();\n\n  /**\n   * let us know when all the probes are loaded\n   * and have achieved steady-state\n   */\n  void set_all_probes_loaded(void);\n\n#ifndef NDEBUG\n  /**\n   * Debug code for internal development to simulate lost BPF samples (PERF_RECORD_LOST) in BufferedPoller.\n   */\n  void debug_bpf_lost_samples();\n#endif\n\nprivate:\n  /**\n   * polling point for dns timeout detection\n   * called via slow poll\n   */\n  void process_dns_timeouts(u64 t);\n\n  /**\n   * A message handler is a member function of this class\n   * @returns true if message should be copied, false otherwise\n   */\n  typedef void (BufferedPoller::*handler_fn)(PerfReader &reader, u16 length);\n\n  struct message_metadata {\n    u64 timestamp;\n    std::size_t cpu_index;\n    std::basic_string_view<u8> payload;\n    std::basic_string_view<u8> padding;\n  };\n\n  template <typename MessageMetadata>\n  using message_handler_fn =\n      void (BufferedPoller::*)(message_metadata const &metadata, typename MessageMetadata::wire_message &);\n\n  template <typename MessageMetadata, message_handler_fn<MessageMetadata>, std::size_t MaxPadding, typename Alignment>\n  void message_handler_entrypoint(PerfReader &reader, u16 length);\n\n  /**\n   * Adds a handler to the hash. Throws on collision.\n   */\n  template <typename MessageMetadata, message_handler_fn<MessageMetadata>, std::size_t MaxPadding = 0, typename Alignment = u64>\n  void add_handler();\n\n  /**\n   * Handler for DNS RPC messages\n   */\n  void handle_dns_message(message_metadata const &metadata, jb_agent_internal__dns_packet &msg);\n\n  /**\n   * Handler a new socket message\n   */\n  void handle_new_socket(message_metadata const &metadata, jb_agent_internal__new_sock_created &msg);\n\n  /**\n   * Handle a ipv4 address for a socket\n   */\n  void handle_set_state_ipv4(message_metadata const &metadata, jb_agent_internal__set_state_ipv4 &msg);\n\n  /**\n   * Handle ipv6 address for a socket\n   */\n  void handle_set_state_ipv6(message_metadata const &metadata, jb_agent_internal__set_state_ipv6 &msg);\n\n  /**\n   * Handler a socket close message\n   */\n  void handle_close_socket(message_metadata const &metadata, jb_agent_internal__close_sock_info &msg);\n\n  /**\n   * Handler a rtt_estimator telemetry message\n   */\n  void handle_rtt_estimator(message_metadata const &metadata, jb_agent_internal__rtt_estimator &msg);\n\n  /**\n   * Handler a rtt_estimator telemetry message\n   */\n  void handle_reset_tcp_counters(message_metadata const &metadata, jb_agent_internal__reset_tcp_counters &msg);\n\n  /**\n   * Handle a TCP SYN timeout message\n   */\n  void handle_tcp_syn_timeout(message_metadata const &metadata, jb_agent_internal__tcp_syn_timeout &msg);\n\n  /**\n   * Handle TCP RST\n   */\n  void handle_tcp_reset(message_metadata const &metadata, jb_agent_internal__tcp_reset &msg);\n\n  /**\n   * Handle a http_response message\n   */\n  void handle_http_response(message_metadata const &metadata, jb_agent_internal__http_response &msg);\n\n  /**\n   * Sends a message with statistics for the entry\n   *\n   * Also marks the entry as invalid.\n   */\n  void send_socket_stats(u64 t, u64 sk, tcp_statistics &stats);\n\n  /**\n   * Processes the current queue in socket_stats_, sending out messages and\n   *   setting entries to stats.queued=false, stats.valid=false, then advances\n   *   the queue.\n   */\n  void send_stats_from_queue(u64 t);\n\n  /*** UDP ***/\n  /**\n   * Handler a new or existing udp socket message\n   */\n  void handle_udp_new_socket(message_metadata const &metadata, jb_agent_internal__udp_new_socket &msg);\n\n  /**\n   * Handler a UDP socket close message\n   */\n  void handle_udp_destroy_socket(message_metadata const &metadata, jb_agent_internal__udp_destroy_socket &msg);\n\n  /**\n   * Handler for udp TX notification\n   */\n  void handle_udp_stats(message_metadata const &metadata, jb_agent_internal__udp_stats &msg);\n\n  /**\n   * Handler for new process\n   */\n  void handle_pid_info(message_metadata const &metadata, jb_agent_internal__pid_info &msg);\n\n  /**\n   * Handler for close process\n   */\n  void handle_pid_close(message_metadata const &metadata, jb_agent_internal__pid_close &msg);\n\n  /**\n   * Handler for process comm change\n   */\n  void handle_pid_set_comm(message_metadata const &metadata, jb_agent_internal__pid_set_comm &msg);\n\n  /**\n   * Handler for process exit\n   */\n  void handle_pid_exit(message_metadata const &metadata, jb_agent_internal__pid_exit &msg);\n\n  /**\n   * Sends a udp socket message\n   *\n   * Only sends when lport is != 0\n   */\n  void udp_send_new_socket(u64 ts, udp_socket_entry *entry, u64 index);\n\n  /**\n   * Sends a message with statistics for the entry\n   *\n   * Also marks the entry as invalid.\n   * @assumes entry is valid\n   */\n  void udp_send_stats(u64 t, u32 sk_id, u8 is_rx, udp_socket_entry &entry, udp_statistics &stats);\n\n  /**\n   * Processes the current queue in socket_stats_, sending out messages and\n   *   setting entries to stats.queued=false, stats.valid=false, then advances\n   *   the queue.\n   */\n  void udp_send_stats_from_queue(u64 t);\n\n  /*** CONTAINERS ***/\n  /**\n   * Handler for a new cgroup dir\n   */\n  void handle_kill_css(message_metadata const &metadata, jb_agent_internal__kill_css &msg);\n\n  void handle_css_populate_dir(message_metadata const &metadata, jb_agent_internal__css_populate_dir &msg);\n\n  void handle_existing_cgroup_probe(message_metadata const &metadata, jb_agent_internal__existing_cgroup_probe &msg);\n\n  void handle_cgroup_attach_task(message_metadata const &metadata, jb_agent_internal__cgroup_attach_task &msg);\n\n  /*** NAT ***/\n  void handle_nf_nat_cleanup_conntrack(message_metadata const &metadata, jb_agent_internal__nf_nat_cleanup_conntrack &msg);\n\n  void handle_nf_conntrack_alter_reply(message_metadata const &metadata, jb_agent_internal__nf_conntrack_alter_reply &msg);\n\n  void handle_existing_conntrack_tuple(message_metadata const &metadata, jb_agent_internal__existing_conntrack_tuple &msg);\n\n  /*** DNS ***/\n  void timeout_dns_request(u64 timestamp_ns, const DnsRequests::Request &req);\n\n  /*** ERRORS ***/\n  void handle_bpf_log(message_metadata const &metadata, jb_agent_internal__bpf_log &msg);\n  void handle_stack_trace(message_metadata const &metadata, jb_agent_internal__stack_trace &msg);\n\n  /*** TCP DATA ***/\n  void handle_tcp_data(message_metadata const &metadata, jb_agent_internal__tcp_data &msg);\n\n  /////////////////////////////////////////////////////////////////////////\n\n  uv_loop_t &loop_;\n\n  u64 time_adjustment_;\n  FileDescriptor &bpf_dump_file_;\n  logging::Logger &log_;\n  IBufferedWriter &buffered_writer_;\n  ProbeHandler &probe_handler_;\n  struct render_bpf_bpf *skel_;\n  ::ebpf_net::ingest::Writer writer_;\n  std::unique_ptr<TCPDataHandler> tcp_data_handler_;\n  ::ebpf_net::kernel_collector::Index collector_index_;\n  ProcessHandler process_handler_;\n  u64 lost_count_ = 0;\n  u32 pid_count_ = 0;\n\n  /* the last lost count that a message was sent for */\n  u64 notified_lost_count_ = 0;\n\n  handler_fn handlers_[AGENT_INTERNAL_HASH_SIZE];\n\n  /* u64 Hasher */\n  struct u64_hasher {\n    u32 operator()(u64 const &s) const noexcept;\n  };\n\n  CgroupHandler cgroup_handler_;\n  NatHandler nat_handler_;\n\n  /* TCP */\n  typedef FixedHash<u64, tcp_socket_entry, tcp_socket_table_max_sockets, u64_hasher> TcpSocketTable;\n  typedef MetricStore<struct tcp_statistics, tcp_socket_table_max_sockets, n_epochs> TcpSocketStatistics;\n\n  TcpSocketTable tcp_socket_table_;\n  bool tcp_socket_table_ever_full_;\n  fast_div tslot_;\n  TcpSocketStatistics tcp_socket_stats_;\n  u64 tcp_index_to_sk_[tcp_socket_table_max_sockets];\n\n  /* UDP */\n  typedef FixedHash<u64, udp_socket_entry, udp_socket_table_max_sockets, u64_hasher> UdpSocketTable;\n  typedef MetricStore<struct udp_statistics, udp_socket_table_max_sockets, n_epochs> UdpSocketStatistics;\n\n  UdpSocketTable udp_socket_table_;\n  bool udp_socket_table_ever_full_;\n  std::array<UdpSocketStatistics, 2> udp_socket_stats_; /* 0: TX, 1: RX */\n\n  /* DNS */\n  DnsRequests dns_requests_;\n\n  bool all_probes_loaded_;\n\n  KernelCollectorRestarter &kernel_collector_restarter_;\n\n#ifndef NDEBUG\n  bool debug_bpf_lost_samples_ = false;\n#endif\n};\n"
  },
  {
    "path": "collector/kernel/cgroup_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/cgroup_handler.h>\n\n#include <collector/agent_log.h>\n#include <collector/constants.h>\n#include <common/constants.h>\n#include <config.h>\n#include <util/docker_host_config_metadata.h>\n#include <util/file_ops.h>\n#include <util/k8s_metadata.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/nomad_metadata.h>\n\n#include <generated/ebpf_net/agent_internal/wire_message.h>\n\n#include <nlohmann/json.hpp>\n\nusing json = nlohmann::json;\n\ninline std::string make_docker_query_url(std::string const &container_name)\n{\n  static const std::string docker_query_base = \"http://localhost/containers/\";\n  return docker_query_base + container_name + \"/json\";\n}\n\nstd::string CgroupHandler::docker_ns_label_field;\n\nCgroupHandler::CgroupHandler(\n    ::ebpf_net::ingest::Writer &writer, CurlEngine &curl_engine, CgroupSettings const &settings, logging::Logger &log)\n    : writer_(writer), curl_engine_(curl_engine), settings_(settings), log_(log)\n{}\n\nCgroupHandler::~CgroupHandler()\n{\n  // cancel all running queries\n  for (auto &entry : queries_) {\n    DockerQuery &query = entry.second;\n    curl_engine_.cancel_fetch(*query.request);\n  }\n}\n\nbool CgroupHandler::has_cgroup(u64 cgroup)\n{\n  return (cgroup_table_.find(cgroup) != cgroup_table_.end());\n}\n\nstd::string_view CgroupHandler::get_name(u64 cgroup)\n{\n  auto pos = cgroup_table_.find(cgroup);\n  if (pos == cgroup_table_.end()) {\n    return {};\n  }\n  return pos->second.name;\n}\n\n/* END */\nvoid CgroupHandler::kill_css(u64 timestamp, struct jb_agent_internal__kill_css *msg)\n{\n  std::string name{(char *)msg->name, strnlen((char *)msg->name, sizeof(msg->name))};\n\n  LOG::debug_in(\n      AgentLogKind::CGROUPS,\n      \"CgroupHandler::kill_css\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tcgroup_parent: 0x{:x}\"\n      \"\\n\\tname: \"\n      \"\\n}}\",\n      msg->cgroup,\n      msg->cgroup_parent,\n      name);\n\n  if (has_cgroup(msg->cgroup)) {\n    LOG::debug_in(AgentLogKind::CGROUPS, \"Success: cgroup found. \\t{}\", get_name(msg->cgroup));\n  } else {\n    log_.warn(\"kill_css(): cgroup not found: 0x{:x}\", msg->cgroup);\n  }\n\n  auto pos = cgroup_table_.find(msg->cgroup);\n  if (pos == cgroup_table_.end()) {\n    return;\n  }\n\n  cgroup_table_.erase(pos);\n}\n\n/* START */\nvoid CgroupHandler::css_populate_dir(u64 timestamp, struct jb_agent_internal__css_populate_dir *msg)\n{\n  std::string name{(char *)msg->name, strnlen((char *)msg->name, sizeof(msg->name))};\n\n  LOG::debug_in(\n      AgentLogKind::CGROUPS,\n      \"CgroupHandler::css_populate_dir\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tcgroup_parent: 0x{:x}\"\n      \"\\n\\tname: {}\"\n      \"\\n}}\",\n      msg->cgroup,\n      msg->cgroup_parent,\n      name);\n\n  if (has_cgroup(msg->cgroup_parent)) {\n    LOG::debug_in(AgentLogKind::CGROUPS, \"Success: cgroup->parent found. \\t{}\", get_name(msg->cgroup_parent));\n  } else {\n    log_.warn(\"cgroup->parent not found: cgroup_parent={:x}, cgroup={:x}, name={}\", msg->cgroup_parent, msg->cgroup, name);\n  }\n\n  handle_cgroup(msg->cgroup, msg->cgroup_parent, name);\n}\n\n/* EXISTING */\nvoid CgroupHandler::existing_cgroup_probe(u64 timestamp, struct jb_agent_internal__existing_cgroup_probe *msg)\n{\n  std::string name{(char *)msg->name, strnlen((char *)msg->name, sizeof(msg->name))};\n\n  // msg->cgroup_parent, msg->name);\n  LOG::debug_in(\n      AgentLogKind::CGROUPS,\n      \"CgroupHandler::existing_cgroup_probe\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tcgroup_parent: 0x{:x}\"\n      \"\\n\\tname: {}\"\n      \"\\n}}\",\n      msg->cgroup,\n      msg->cgroup_parent,\n      name);\n\n  handle_cgroup(msg->cgroup, msg->cgroup_parent, name);\n}\n\nvoid CgroupHandler::cgroup_attach_task(u64 timestamp, struct jb_agent_internal__cgroup_attach_task *msg)\n{\n  LOG::debug_in(\n      AgentLogKind::CGROUPS,\n      \"CgroupHandler::cgroup_attach_task\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tpid: {}\"\n      \"\\n\\tcomm: {}\"\n      \"\\n}}\",\n      msg->cgroup,\n      msg->pid,\n      std::string_view((char *)msg->comm, strnlen((char *)msg->comm, sizeof(msg->comm))));\n\n  if (has_cgroup(msg->cgroup)) {\n    LOG::debug_in(AgentLogKind::CGROUPS, \"Success: cgroup found. \\t{}\", get_name(msg->cgroup));\n  } else {\n    log_.warn(\"cgroup_attach_task(): cgroup not found: 0x{:x}\", msg->cgroup);\n  }\n}\n\nvoid CgroupHandler::handle_pid_info(u32 pid, u64 cgroup, uint8_t comm[16])\n{\n  LOG::debug_in(\n      AgentLogKind::CGROUPS,\n      \"CgroupHandler::handle_pid_info\"\n      \"\\n{{\"\n      \"\\n\\tpid: {}\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tcomm: {}\"\n      \"\\n}}\",\n      pid,\n      cgroup,\n      std::string_view((char *)comm, strnlen((char *)comm, 16)));\n\n  if (has_cgroup(cgroup)) {\n    LOG::debug_in(AgentLogKind::CGROUPS, \"Success: cgroup found. \\t{}\", get_name(cgroup));\n  } else {\n    log_.warn(\"handle_pid_info(): cgroup not found: 0x{:x}\", cgroup);\n  }\n}\n\nvoid CgroupHandler::handle_cgroup(u64 cgroup, u64 cgroup_parent, std::string const &name)\n{\n  auto emp = cgroup_table_.emplace(cgroup, CgroupEntry{cgroup_parent, name});\n  if (emp.second == false) {\n    return;\n  }\n\n  auto parent_pos = cgroup_table_.find(cgroup_parent);\n  if (parent_pos == cgroup_table_.end()) {\n    return;\n  }\n\n  bool is_docker = settings_.force_docker_metadata;\n\n  if (!is_docker) {\n    if (parent_pos->second.name == \"docker\") {\n      // parent container's name is docker\n      is_docker = true;\n    } else {\n      // grandparent\n      auto gp_pos = cgroup_table_.find(parent_pos->second.cgroup_parent);\n      if (gp_pos != cgroup_table_.end()) {\n        if (gp_pos->second.name == \"ecs\") {\n          // grandparent container's name is ecs\n          is_docker = true;\n        }\n      }\n    }\n  }\n\n  if (is_docker) {\n    handle_docker_container(cgroup, name);\n  }\n}\n\nvoid CgroupHandler::handle_docker_container(u64 cgroup, std::string const &name)\n{\n  LOG::debug_in(\n      AgentLogKind::DOCKER,\n      \"CgroupHandler::handle_docker_container:\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tname: {}\"\n      \"\\n}}\",\n      cgroup,\n      name);\n\n  auto request = std::make_unique<CurlEngine::FetchRequest>(\n      make_docker_query_url(name),\n      [this, cgroup](const char *data, size_t data_length) { this->data_available_cb(data, data_length, cgroup); },\n      [this, cgroup](CurlEngineStatus status, int responseCode, std::string_view curlError) {\n        this->fetch_done_cb(status, responseCode, curlError, cgroup);\n      });\n\n  request->unix_socket(UNIX_SOCKET_PATH);\n\n  // debug mode curl if debugging docker\n  request->debug_mode(is_log_whitelisted(AgentLogKind::DOCKER));\n\n  auto emp = queries_.emplace(cgroup, DockerQuery{std::move(request)});\n  if (emp.second == false) {\n    log_.error(\"query for cgroup:{} is already running\", cgroup);\n    return;\n  }\n  auto status = curl_engine_.schedule_fetch(*emp.first->second.request);\n  if (status != CurlEngineStatus::OK) {\n    // scheduling failure. curl engine will have called the done_fn, which\n    // cleans up queries_\n    log_.error(\"failed to schedule a fetch request\");\n    return;\n  }\n\n  LOG::debug_in(AgentLogKind::DOCKER, \"\\tqueries_.size(): {}\", queries_.size());\n}\n\nvoid CgroupHandler::data_available_cb(const char *data, size_t data_length, u64 cgroup)\n{\n  std::string s(data, data_length);\n  LOG::debug_in(\n      AgentLogKind::DOCKER,\n      \"DataAvailableFn:\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tdata_length: {}\"\n      \"\\n\\ts: {}\"\n      \"\\n}}\",\n      cgroup,\n      data_length,\n      s);\n\n  auto pos = queries_.find(cgroup);\n  if (pos == queries_.end()) {\n    log_.error(\"query entry for cgroup: 0x{:x} not found\", cgroup);\n    return;\n  }\n\n  DockerQuery &query = pos->second;\n  query.response.append(s);\n}\n\nvoid CgroupHandler::fetch_done_cb(CurlEngineStatus status, long responseCode, std::string_view curlError, u64 cgroup)\n{\n  bool success = (status == CurlEngineStatus::OK);\n\n  LOG::debug_in(\n      AgentLogKind::DOCKER,\n      \"FetchDoneFn:\"\n      \"\\n{{\"\n      \"\\n\\tcgroup: 0x{:x}\"\n      \"\\n\\tsuccess: {}\"\n      \"\\n}}\",\n      cgroup,\n      success);\n\n  auto pos = queries_.find(cgroup);\n  if (pos == queries_.end()) {\n    log_.error(\"query entry for cgroup: 0x{:x} not found\", cgroup);\n    return;\n  }\n\n  std::string response_data(std::move(pos->second.response));\n  queries_.erase(pos);\n\n  if (!success) {\n    const std::string_view status_text = to_string(status);\n    log_.error(\"docker fetch failed [{}:{}]: {}\", status_text, responseCode, curlError);\n    return;\n  }\n\n  if ((responseCode >= 200) && (responseCode <= 299)) {\n    // success\n    handle_docker_response(cgroup, response_data);\n  } else if ((responseCode >= 500) && (responseCode <= 599)) {\n    // server error\n    log_.error(\"docker fetch failed with response {}\", responseCode);\n  }\n\n  LOG::debug_in(AgentLogKind::DOCKER, \"\\tqueries_.size(): {}\", queries_.size());\n}\n\ninline std::string get_string(json const &j)\n{\n  if (j.is_string()) {\n    return j.get<std::string>();\n  } else {\n    return std::string();\n  }\n}\n\ninline std::string get_string(json const &object, char const *key)\n{\n  if (!object.is_object()) {\n    return std::string();\n  }\n\n  auto pos = object.find(key);\n  if (pos != object.end()) {\n    return get_string(*pos);\n  } else {\n    return std::string();\n  }\n}\n\ninline jb_blob blob(std::string const &str)\n{\n  return jb_blob{str.c_str(), static_cast<u16>(str.size())};\n}\n\nvoid CgroupHandler::handle_docker_response(u64 cgroup, std::string const &response_data)\n{\n  std::string id;\n  std::string name;\n  std::string image;\n  std::string ip_addr;\n  std::string cluster;\n  std::string container;\n  std::string task_family;\n  std::string task_version;\n  std::string ns;\n  std::string pod_name;\n\n  std::optional<DockerHostConfigMetadata> docker_host_config;\n  std::optional<NomadMetadata> nomad_metadata;\n  std::optional<K8sMetadata> k8s_metadata;\n\n  if (settings_.docker_metadata_dump_dir) {\n    auto const dump_filename = fmt::format(\n        \"{}/docker-inspect.{}.{}.json\",\n        *settings_.docker_metadata_dump_dir,\n        cgroup,\n        std::chrono::system_clock::now().time_since_epoch().count());\n\n    if (auto const error = write_file(dump_filename.c_str(), response_data)) {\n      LOG::warn(\"failed to dump docker metadata to {}: {}\", dump_filename, error);\n    }\n  }\n\n  try {\n    json root = json::parse(response_data);\n\n    auto network = root[\"NetworkSettings\"];\n    auto config = root[\"Config\"];\n    auto host_config = root[\"HostConfig\"];\n    auto labels = config[\"Labels\"];\n    auto env = config[\"Env\"];\n\n    id = get_string(root, \"Id\");\n    name = get_string(root, \"Name\");\n    image = get_string(config, \"Image\");\n    ip_addr = get_string(network, \"IPAddress\");\n\n    cluster = get_string(labels, \"com.amazonaws.ecs.cluster\");\n\n    container = get_string(labels, \"com.amazonaws.ecs.container-name\");\n    if (container.empty()) {\n      // Try k8s if ECS container is missing\n      container = get_string(labels, \"io.kubernetes.container.name\");\n    }\n\n    pod_name = get_string(labels, \"com.amazonaws.ecs.task-arn\");\n    if (pod_name.empty()) {\n      // k8s\n      pod_name = get_string(labels, \"io.kubernetes.pod.name\");\n    }\n\n    task_family = get_string(labels, \"com.amazonaws.ecs.task-definition-family\");\n    task_version = get_string(labels, \"com.amazonaws.ecs.task-definition-version\");\n\n    if (!docker_ns_label_field.empty()) {\n      ns = get_string(labels, docker_ns_label_field.c_str());\n    }\n\n    docker_host_config.emplace(host_config);\n    nomad_metadata.emplace(NomadMetadata(env));\n    k8s_metadata.emplace(labels);\n\n    for (auto const &item : labels.items()) {\n      if (!item.value().is_string()) {\n        continue;\n      }\n\n      std::string_view key = item.key();\n      std::string const value = item.value().get<std::string>();\n\n      if ((key.size() + value.size() + sizeof(u64) + jb_ingest__container_annotation__data_size) > WRITE_BUFFER_SIZE) {\n        // NOTE: we use a substring of the key to make sure it fits in the\n        // warning message.\n        log_.warn(\"Docker metadata label for key '{}' is too large to send\", key.substr(0, 256));\n        continue;\n      }\n\n      writer_.container_annotation(cgroup, jb_blob{key}, jb_blob{value});\n    }\n  } catch (json::exception &e) {\n    log_.error(\"failed to parse response data: {}\", e.what());\n    return;\n  }\n\n  if (docker_host_config.has_value()) {\n    LOG::debug_in(AgentLogKind::DOCKER, \"container resource limits: {}\", fmt::streamed(*docker_host_config));\n\n    auto const cpu_period = docker_host_config->cpu_period();\n    auto const cpu_quota = docker_host_config->cpu_quota();\n    writer_.container_resource_limits(\n        cgroup,\n        std::max(kernel::MIN_CGROUP_CPU_SHARES, std::min(kernel::MAX_CGROUP_CPU_SHARES, docker_host_config->cpu_shares())),\n        cpu_period <= 0 ? kernel::DEFAULT_CGROUP_QUOTA : cpu_period,\n        cpu_quota < 0 ? kernel::DEFAULT_CGROUP_QUOTA : cpu_quota,\n        docker_host_config->memory_swappiness(),\n        docker_host_config->memory_limit(),\n        docker_host_config->memory_soft_limit(),\n        docker_host_config->total_memory_limit());\n  }\n\n  LOG::debug_in(\n      AgentLogKind::DOCKER,\n      \"container metadata:\"\n      \"\\n{{\"\n      \"\\n\\tid: {}\"\n      \"\\n\\tname: {}\"\n      \"\\n\\timage: {}\"\n      \"\\n\\tip_addr: {}\"\n      \"\\n\\tcluster: {}\"\n      \"\\n\\tcontainer: {}\"\n      \"\\n\\ttask_family: {}\"\n      \"\\n\\ttask_version: {}\"\n      \"\\n\\tns: {}\"\n      \"\\n}}\",\n      id,\n      name,\n      image,\n      ip_addr,\n      cluster,\n      container,\n      task_family,\n      task_version,\n      ns);\n\n  writer_.container_metadata(\n      cgroup,\n      blob(id),\n      blob(name),\n      blob(image),\n      blob(ip_addr),\n      blob(cluster),\n      blob(container),\n      blob(task_family),\n      blob(task_version),\n      blob(ns));\n\n  if (nomad_metadata.has_value() && *nomad_metadata) {\n    writer_.nomad_metadata(\n        cgroup,\n        jb_blob{nomad_metadata->ns()},\n        jb_blob{nomad_metadata->group_name()},\n        jb_blob{nomad_metadata->task_name()},\n        jb_blob{nomad_metadata->job_name()});\n  }\n\n  if (!pod_name.empty()) {\n    LOG::debug_in(\n        AgentLogKind::DOCKER,\n        \"pod_name:\"\n        \"\\n{{\"\n        \"\\n\\tname: {}\"\n        \"\\n}}\",\n        pod_name);\n\n    writer_.pod_name(cgroup, blob(\"\") /* deprecated pod_uid */, blob(pod_name));\n  }\n\n  if (k8s_metadata.has_value() && *k8s_metadata) {\n    writer_.k8s_metadata(\n        cgroup,\n        jb_blob{k8s_metadata->container_name()},\n        jb_blob{k8s_metadata->pod_name()},\n        jb_blob{k8s_metadata->pod_ns()},\n        jb_blob{k8s_metadata->pod_uid()},\n        jb_blob{k8s_metadata->sandbox_uid()});\n\n    for (auto const &port : k8s_metadata->ports()) {\n      writer_.k8s_metadata_port(cgroup, port.second.port, integer_value(port.second.protocol), jb_blob{port.second.name});\n    }\n  };\n\n  writer_.flush();\n}\n"
  },
  {
    "path": "collector/kernel/cgroup_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/writer.h>\n#include <util/curl_engine.h>\n#include <util/logger.h>\n#include <util/lookup3_hasher.h>\n\n#include <optional>\n#include <unordered_map>\n\nstatic constexpr std::string_view UNIX_SOCKET_PATH = \"/var/run/docker.sock\";\n\nclass CgroupHandler {\npublic:\n  struct CgroupSettings {\n    bool force_docker_metadata = false;\n    std::optional<std::string> docker_metadata_dump_dir;\n  };\n\n  // When set, specifies the name of the docker label that will be used for\n  // obtaining the 'namespace' value.\n  static std::string docker_ns_label_field;\n\n  CgroupHandler(\n      ::ebpf_net::ingest::Writer &writer, CurlEngine &curl_engine, CgroupSettings const &settings, logging::Logger &log);\n  ~CgroupHandler();\n\n  void kill_css(u64 timestamp, struct jb_agent_internal__kill_css *msg);\n  void css_populate_dir(u64 timestamp, struct jb_agent_internal__css_populate_dir *msg);\n  void existing_cgroup_probe(u64 timestamp, struct jb_agent_internal__existing_cgroup_probe *msg);\n  void cgroup_attach_task(u64 timestamp, struct jb_agent_internal__cgroup_attach_task *msg);\n  void handle_pid_info(u32 pid, u64 cgroup, uint8_t comm[16]);\n\nprivate:\n  friend class CgroupHandlerTest_handle_docker_response_Test;\n\n  struct CgroupEntry {\n    u64 cgroup_parent;\n    std::string name;\n  };\n\n  struct DockerQuery {\n    std::unique_ptr<CurlEngine::FetchRequest> request;\n    std::string response;\n  };\n\n  ::ebpf_net::ingest::Writer &writer_;\n  CurlEngine &curl_engine_;\n  CgroupSettings const &settings_;\n  logging::Logger &log_;\n  std::unordered_map<u64, CgroupEntry> cgroup_table_;\n  std::unordered_map<u64, DockerQuery> queries_;\n\n  bool has_cgroup(u64 cgroup);\n  // returns empty string for unknown cgroups\n  std::string_view get_name(u64 cgroup);\n\n  void handle_cgroup(u64 cgroup, u64 cgroup_parent, std::string const &name);\n  void handle_docker_container(u64 cgroup, std::string const &name);\n\n  void data_available_cb(const char *data, size_t data_length, u64 cgroup);\n  void fetch_done_cb(CurlEngineStatus status, long responseCode, std::string_view curlError, u64 cgroup);\n\n  void handle_docker_response(u64 cgroup, std::string const &response_data);\n};\n"
  },
  {
    "path": "collector/kernel/cgroup_handler_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/buffered_writer.h>\n#include <channel/test_channel.h>\n#include <collector/kernel/cgroup_handler.h>\n#include <gtest/gtest.h>\n#include <util/curl_engine.h>\n#include <util/json.h>\n#include <util/log.h>\n\n#include <string>\n#include <unordered_map>\n\n#include <uv.h>\n\nconst char *dummy_json_response_data = R\"delim(\n{\n  \"Id\": \"ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90\",\n  \"Created\": \"2021-11-04T16:54:03.099687792Z\",\n  \"Path\": \"/srv/entrypoint.sh\",\n  \"Args\": [\n    \"--port=8000\",\n    \"--internal-prom=0.0.0.0:7000\",\n    \"--prom=0.0.0.0:7001\",\n    \"--prom-queue-count=1\",\n    \"--ingest_shard_count=1\",\n    \"--matching_shard_count=1\",\n    \"--aggregation_shard_count=1\",\n    \"--enable-aws-enrichment\",\n    \"--log-console\",\n    \"--debug\"\n  ],\n  \"State\": {\n    \"Status\": \"running\",\n    \"Running\": true,\n    \"Paused\": false,\n    \"Restarting\": false,\n    \"OOMKilled\": false,\n    \"Dead\": false,\n    \"Pid\": 4251,\n    \"ExitCode\": 0,\n    \"Error\": \"\",\n    \"StartedAt\": \"2021-11-04T16:54:05.666083454Z\",\n    \"FinishedAt\": \"0001-01-01T00:00:00Z\"\n  },\n  \"Image\": \"sha256:5c9ce00b94a01bf23a44eaf995f068d1993baefccf45ee37e5448f03f8499ab0\",\n  \"ResolvConfPath\": \"/var/lib/docker/containers/ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90/resolv.conf\",\n  \"HostnamePath\": \"/var/lib/docker/containers/ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90/hostname\",\n  \"HostsPath\": \"/var/lib/docker/containers/ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90/hosts\",\n  \"LogPath\": \"/var/lib/docker/containers/ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90/ad87fc49c1389b0939d637ae700aa4eb4f51cf7da569551857e80789305a7d90-json.log\",\n  \"Name\": \"/amazing_chatterjee\",\n  \"RestartCount\": 0,\n  \"Driver\": \"overlay2\",\n  \"Platform\": \"linux\",\n  \"MountLabel\": \"\",\n  \"ProcessLabel\": \"\",\n  \"AppArmorProfile\": \"unconfined\",\n  \"ExecIDs\": null,\n  \"HostConfig\": {\n    \"Binds\": [\n      \"/home/vagrant/src/:/root/src\",\n      \"/home/vagrant/out/:/root/out\"\n    ],\n    \"ContainerIDFile\": \"\",\n    \"LogConfig\": {\n      \"Type\": \"json-file\",\n      \"Config\": {}\n    },\n    \"NetworkMode\": \"default\",\n    \"PortBindings\": {\n      \"7000/tcp\": [\n        {\n          \"HostIp\": \"\",\n          \"HostPort\": \"7000\"\n        }\n      ],\n      \"7001/tcp\": [\n        {\n          \"HostIp\": \"\",\n          \"HostPort\": \"7001\"\n        },\n        {\n          \"HostIp\": \"\",\n          \"HostPort\": \"7002\"\n        },\n        {\n          \"HostIp\": \"\",\n          \"HostPort\": \"7003\"\n        }\n      ],\n      \"8000/tcp\": [\n        {\n          \"HostIp\": \"\",\n          \"HostPort\": \"8000\"\n        }\n      ]\n    },\n    \"RestartPolicy\": {\n      \"Name\": \"no\",\n      \"MaximumRetryCount\": 0\n    },\n    \"AutoRemove\": true,\n    \"VolumeDriver\": \"\",\n    \"VolumesFrom\": null,\n    \"CapAdd\": null,\n    \"CapDrop\": null,\n    \"CgroupnsMode\": \"host\",\n    \"Dns\": [],\n    \"DnsOptions\": [],\n    \"DnsSearch\": [],\n    \"ExtraHosts\": null,\n    \"GroupAdd\": null,\n    \"IpcMode\": \"private\",\n    \"Cgroup\": \"\",\n    \"Links\": null,\n    \"OomScoreAdj\": 0,\n    \"PidMode\": \"\",\n    \"Privileged\": true,\n    \"PublishAllPorts\": false,\n    \"ReadonlyRootfs\": false,\n    \"SecurityOpt\": [\n      \"label=disable\"\n    ],\n    \"UTSMode\": \"\",\n    \"UsernsMode\": \"\",\n    \"ShmSize\": 67108864,\n    \"Runtime\": \"runc\",\n    \"ConsoleSize\": [\n      0,\n      0\n    ],\n    \"Isolation\": \"\",\n    \"CpuShares\": 0,\n    \"Memory\": 0,\n    \"NanoCpus\": 0,\n    \"CgroupParent\": \"\",\n    \"BlkioWeight\": 0,\n    \"BlkioWeightDevice\": [],\n    \"BlkioDeviceReadBps\": null,\n    \"BlkioDeviceWriteBps\": null,\n    \"BlkioDeviceReadIOps\": null,\n    \"BlkioDeviceWriteIOps\": null,\n    \"CpuPeriod\": 0,\n    \"CpuQuota\": 0,\n    \"CpuRealtimePeriod\": 0,\n    \"CpuRealtimeRuntime\": 0,\n    \"CpusetCpus\": \"\",\n    \"CpusetMems\": \"\",\n    \"Devices\": [],\n    \"DeviceCgroupRules\": null,\n    \"DeviceRequests\": null,\n    \"KernelMemory\": 0,\n    \"KernelMemoryTCP\": 0,\n    \"MemoryReservation\": 0,\n    \"MemorySwap\": 0,\n    \"MemorySwappiness\": null,\n    \"OomKillDisable\": false,\n    \"PidsLimit\": null,\n    \"Ulimits\": null,\n    \"CpuCount\": 0,\n    \"CpuPercent\": 0,\n    \"IOMaximumIOps\": 0,\n    \"IOMaximumBandwidth\": 0,\n    \"MaskedPaths\": null,\n    \"ReadonlyPaths\": null\n  },\n  \"GraphDriver\": {\n    \"Data\": {\n      \"LowerDir\": \"/var/lib/docker/overlay2/925ab32b43b5e0b27aae80af7cf4edfe129b79e56aca5c41337094eb95b83fea-init/diff:/var/lib/docker/overlay2/835a05eea84b538ad3fa865e50895ce03c59517ea7db69ce7313a3b421321c2d/diff:/var/lib/docker/overlay2/3b05911a571c14d6965e32a2cbe998bf75ae287c5969cef9e9350958ed805b95/diff:/var/lib/docker/overlay2/15982a35f2ac7e3e362106b03e8ece88a0b959407147e483e09dc5674fa1566d/diff:/var/lib/docker/overlay2/1c7fb4b897d78db2bb5da88423bfafc92ea0df95fdce8b8c91316a985fe59738/diff:/var/lib/docker/overlay2/5a32678605742638e9c3f784e9f11eddf36f9c8a1436711feadc685d7f0800ec/diff:/var/lib/docker/overlay2/94cba6304ed8b8f0f3c71b192e100eb51d9fe663f6cbd909adbf71f2a23166a0/diff:/var/lib/docker/overlay2/531befd27476430f7b6df70dde3120cb16eedc9aef0ba86042b097655d3e90f4/diff:/var/lib/docker/overlay2/0c1ef5b3e5063760f7f87674f5957937ab46f5162b5bffaad5ead3df0b6b3657/diff:/var/lib/docker/overlay2/85ac7b1e505a2705a28d2a24d16e905035a851c60e09f8949d498dbe5590dcd0/diff\",\n      \"MergedDir\": \"/var/lib/docker/overlay2/925ab32b43b5e0b27aae80af7cf4edfe129b79e56aca5c41337094eb95b83fea/merged\",\n      \"UpperDir\": \"/var/lib/docker/overlay2/925ab32b43b5e0b27aae80af7cf4edfe129b79e56aca5c41337094eb95b83fea/diff\",\n      \"WorkDir\": \"/var/lib/docker/overlay2/925ab32b43b5e0b27aae80af7cf4edfe129b79e56aca5c41337094eb95b83fea/work\"\n    },\n    \"Name\": \"overlay2\"\n  },\n  \"Mounts\": [\n    {\n      \"Type\": \"bind\",\n      \"Source\": \"/home/vagrant/src\",\n      \"Destination\": \"/root/src\",\n      \"Mode\": \"\",\n      \"RW\": true,\n      \"Propagation\": \"rprivate\"\n    },\n    {\n      \"Type\": \"bind\",\n      \"Source\": \"/home/vagrant/out\",\n      \"Destination\": \"/root/out\",\n      \"Mode\": \"\",\n      \"RW\": true,\n      \"Propagation\": \"rprivate\"\n    }\n  ],\n  \"Config\": {\n    \"Hostname\": \"ad87fc49c138\",\n    \"Domainname\": \"\",\n    \"User\": \"\",\n    \"AttachStdin\": false,\n    \"AttachStdout\": true,\n    \"AttachStderr\": true,\n    \"ExposedPorts\": {\n      \"7000/tcp\": {},\n      \"7001/tcp\": {},\n      \"7010/tcp\": {},\n      \"8000/tcp\": {}\n    },\n    \"Tty\": true,\n    \"OpenStdin\": false,\n    \"StdinOnce\": false,\n    \"Env\": [\n      \"EBPF_NET_RUN_UNDER_GDB=\",\n      \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",\n      \"OPENSSL_CONF=/etc/openssl/openssl.cnf\",\n      \"GEOIP_PATH=/usr/share/GeoIP\"\n    ],\n    \"Cmd\": [\n      \"--port=8000\",\n      \"--internal-prom=0.0.0.0:7000\",\n      \"--prom=0.0.0.0:7001\",\n      \"--prom-queue-count=1\",\n      \"--ingest_shard_count=1\",\n      \"--matching_shard_count=1\",\n      \"--aggregation_shard_count=1\",\n      \"--enable-aws-enrichment\",\n      \"--log-console\",\n      \"--debug\"\n    ],\n    \"Image\": \"localhost:5000/reducer\",\n    \"Volumes\": null,\n    \"WorkingDir\": \"\",\n    \"Entrypoint\": [\n      \"/srv/entrypoint.sh\"\n    ],\n    \"OnBuild\": null,\n    \"Labels\": {\n      \"org.label-schema.description\": \"Network Explorer reducer\",\n      \"org.label-schema.name\": \"network-explorer/reducer\",\n      \"org.label-schema.schema-version\": \"1.0\",\n      \"org.label-schema.usage\": \"./README.md\"\n    }\n  },\n  \"NetworkSettings\": {\n    \"Bridge\": \"\",\n    \"SandboxID\": \"da32f06c760e26fb0c9a476711bc83b76c5d12513ab45d38076c7f2359782d38\",\n    \"HairpinMode\": false,\n    \"LinkLocalIPv6Address\": \"\",\n    \"LinkLocalIPv6PrefixLen\": 0,\n    \"Ports\": {\n      \"7000/tcp\": [\n        {\n          \"HostIp\": \"0.0.0.0\",\n          \"HostPort\": \"7000\"\n        },\n        {\n          \"HostIp\": \"::\",\n          \"HostPort\": \"7000\"\n        }\n      ],\n      \"7001/tcp\": [\n        {\n          \"HostIp\": \"0.0.0.0\",\n          \"HostPort\": \"7003\"\n        },\n        {\n          \"HostIp\": \"::\",\n          \"HostPort\": \"7003\"\n        },\n        {\n          \"HostIp\": \"0.0.0.0\",\n          \"HostPort\": \"7002\"\n        },\n        {\n          \"HostIp\": \"::\",\n          \"HostPort\": \"7002\"\n        },\n        {\n          \"HostIp\": \"0.0.0.0\",\n          \"HostPort\": \"7001\"\n        },\n        {\n          \"HostIp\": \"::\",\n          \"HostPort\": \"7001\"\n        }\n      ],\n      \"7010/tcp\": null,\n      \"8000/tcp\": [\n        {\n          \"HostIp\": \"0.0.0.0\",\n          \"HostPort\": \"8000\"\n        },\n        {\n          \"HostIp\": \"::\",\n          \"HostPort\": \"8000\"\n        }\n      ]\n    },\n    \"SandboxKey\": \"/var/run/docker/netns/da32f06c760e\",\n    \"SecondaryIPAddresses\": null,\n    \"SecondaryIPv6Addresses\": null,\n    \"EndpointID\": \"29e7a05fc7beb59d78b2262e1290751ab4fe2b21e4a9711a0793e558ab57a43c\",\n    \"Gateway\": \"172.17.0.1\",\n    \"GlobalIPv6Address\": \"\",\n    \"GlobalIPv6PrefixLen\": 0,\n    \"IPAddress\": \"172.17.0.2\",\n    \"IPPrefixLen\": 16,\n    \"IPv6Gateway\": \"\",\n    \"MacAddress\": \"02:42:ac:11:00:02\",\n    \"Networks\": {\n      \"bridge\": {\n        \"IPAMConfig\": null,\n        \"Links\": null,\n        \"Aliases\": null,\n        \"NetworkID\": \"2fa7167f0c612eda844cb3e9465e92108b5d10a4304033191007c00916eb661e\",\n        \"EndpointID\": \"29e7a05fc7beb59d78b2262e1290751ab4fe2b21e4a9711a0793e558ab57a43c\",\n        \"Gateway\": \"172.17.0.1\",\n        \"IPAddress\": \"172.17.0.2\",\n        \"IPPrefixLen\": 16,\n        \"IPv6Gateway\": \"\",\n        \"GlobalIPv6Address\": \"\",\n        \"GlobalIPv6PrefixLen\": 0,\n        \"MacAddress\": \"02:42:ac:11:00:02\",\n        \"DriverOpts\": null\n      }\n    }\n  }\n}\n)delim\";\n\nclass CgroupHandlerTest : public ::testing::Test {\nprotected:\n  void SetUp() override { ASSERT_EQ(0, uv_loop_init(&loop_)); }\n\n  void TearDown() override\n  {\n    // Clean up loop_ to avoid valgrind and asan complaints about memory leaks.\n    close_uv_loop_cleanly(&loop_);\n  }\n\n  uv_loop_t loop_;\n};\n\nTEST_F(CgroupHandlerTest, handle_docker_response)\n{\n  channel::TestChannel test_channel(std::nullopt, IntakeEncoder::binary);\n  channel::BufferedWriter buffered_writer(test_channel, 1024);\n  ebpf_net::ingest::Writer writer(buffered_writer, monotonic, 0, nullptr);\n\n  std::unique_ptr<CurlEngine> curl_engine = CurlEngine::create(&loop_);\n\n  CgroupHandler::CgroupSettings cgroup_settings;\n\n  logging::Logger logger(writer);\n\n  CgroupHandler cgroup_handler(writer, *curl_engine.get(), cgroup_settings, logger);\n\n  nlohmann::json const dummy_json_response_object = nlohmann::json::parse(dummy_json_response_data);\n\n  // Populate a map with expected key/value pairs to compare against results from cgroup_handler.handle_docker_response().\n  std::unordered_map<std::string, std::string> key_value_map;\n  for (auto item : dummy_json_response_object[\"/Config/Labels\"_json_pointer].items()) {\n    key_value_map[item.key()] = item.value().get<std::string>();\n    LOG::debug(\"key_value_map[{}]={}\", item.key(), item.value());\n  }\n\n  ASSERT_NE(0UL, key_value_map.size());\n\n  // Pass the dummy response_data to CgroupHandler::handle_docker_response().\n  cgroup_handler.handle_docker_response(1, dummy_json_response_object.dump());\n\n  // Validate that the writer got the expected values.\n  auto validate_key_value = [&](channel::TestChannel::JsonMessageType const &msg) {\n    LOG::debug(\"{}\", log_waive(msg.dump()));\n\n    auto key_json_ptr = \"/data/key\"_json_pointer;\n    auto value_json_ptr = \"/data/value\"_json_pointer;\n    if (msg.contains(key_json_ptr) && msg.contains(value_json_ptr)) {\n      std::string key = msg[key_json_ptr];\n      std::string value = msg[value_json_ptr];\n      LOG::debug(\"key={}, value={}\", key, value);\n      EXPECT_TRUE(key_value_map[key] == value) << \"key_value_map[key]=\" << key_value_map[key] << \" value=\" << value;\n      key_value_map.erase(key);\n    }\n  };\n\n  test_channel.json_messages_for_each(validate_key_value);\n\n  EXPECT_EQ(0UL, key_value_map.size());\n}\n"
  },
  {
    "path": "collector/kernel/cgroup_prober.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/agent_log.h>\n#include <collector/kernel/cgroup_prober.h>\n#include <collector/kernel/fd_reader.h>\n#include <collector/kernel/probe_handler.h>\n#include <collector/kernel/proc_reader.h>\n#include <common/host_info.h>\n\n#include <fstream>\n#include <iostream>\n#include <set>\n#include <stack>\n#include <string>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/bpf.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nCgroupProber::CgroupProber(\n    ProbeHandler &probe_handler,\n    struct render_bpf_bpf *skel,\n    HostInfo const &host_info,\n    std::function<void(void)> periodic_cb,\n    std::function<void(std::string)> check_cb)\n    : host_info_(host_info), close_dir_error_count_(0)\n{\n  // END\n  ProbeAlternatives kill_kss_probe_alternatives{\n      \"kill css\",\n      {\n          {\"on_kill_css\", \"kill_css\"},\n          // Attaching probe to kill_css fails on some distros and kernel builds, for example Ubuntu Jammy.\n          {\"on_kill_css\", \"css_clear_dir\"},\n          // If the previous two fail try an alternative for kernel versions older than 3.12.\n          {\"on_cgroup_destroy_locked\", \"cgroup_destroy_locked\"},\n      }};\n  probe_handler.start_probe(skel, kill_kss_probe_alternatives);\n  periodic_cb();\n\n  // START\n  ProbeAlternatives css_populate_dir_probe_alternatives{\n      \"css populate dir\",\n      {\n          {\"on_css_populate_dir\", \"css_populate_dir\"},\n          {\"on_cgroup_populate_dir\", \"cgroup_populate_dir\"},\n      }};\n  probe_handler.start_probe(skel, css_populate_dir_probe_alternatives);\n  periodic_cb();\n\n  // check both cgroups v1 and v2 because it is possible for active cgroups to exist in both (hybrid mode)\n\n  // EXISTING cgroups v1\n  probe_handler.start_probe(skel, \"on_cgroup_clone_children_read\", \"cgroup_clone_children_read\");\n\n  periodic_cb();\n  check_cb(\"cgroup prober startup\");\n\n  // locate the cgroup v1 mount directory\n  std::string cgroup_v1_mountpoint = find_cgroup_v1_mountpoint();\n\n  if (!cgroup_v1_mountpoint.empty()) {\n    // now iterate over cgroups and trigger cgroup_clone_children_read\n    trigger_existing_cgroup_probe(cgroup_v1_mountpoint, \"cgroup.clone_children\", periodic_cb);\n    check_cb(\"trigger_cgroup_clone_children_read()\");\n  }\n\n  /* can remove existing now */\n  probe_handler.cleanup_probe(\"cgroup_clone_children_read\");\n\n  // EXISTING cgroups v2\n  static const std::string cgroup_v2_first_kernel_version(\"4.6\");\n  if (host_info_.kernel_version >= cgroup_v2_first_kernel_version) {\n    // Try cgroup_get_from_fd first (only needs kretprobe)\n    int ret = probe_handler.start_kretprobe(skel, \"onret_cgroup_get_from_fd\", \"cgroup_get_from_fd\");\n    bool cgroup_get_from_fd_success = (ret == 0);\n\n    if (cgroup_get_from_fd_success) {\n      check_cb(\"cgroup_get_from_fd probe started\");\n    }\n\n    // If cgroup_get_from_fd failed, fallback to cgroup_control\n    if (!cgroup_get_from_fd_success) {\n      probe_handler.start_probe(skel, \"on_cgroup_control\", \"cgroup_control\");\n      probe_handler.start_kretprobe(skel, \"onret_cgroup_control\", \"cgroup_control\");\n      check_cb(\"cgroup_control probe started (fallback)\");\n    }\n\n    // locate the cgroup v2 mount directory\n    std::string cgroup_v2_mountpoint = find_cgroup_v2_mountpoint();\n\n    if (!cgroup_v2_mountpoint.empty()) {\n      // now iterate over cgroups and trigger the appropriate probe\n      if (cgroup_get_from_fd_success) {\n        trigger_cgroup_get_from_fd_probe(cgroup_v2_mountpoint, periodic_cb);\n        check_cb(\"trigger_cgroup_get_from_fd()\");\n      } else {\n        trigger_existing_cgroup_probe(cgroup_v2_mountpoint, \"cgroup.controllers\", periodic_cb);\n        check_cb(\"trigger_cgroup_control()\");\n      }\n    }\n\n    /* cleanup probes */\n    if (cgroup_get_from_fd_success) {\n      probe_handler.cleanup_kretprobe(\"cgroup_get_from_fd\");\n    } else {\n      probe_handler.cleanup_kretprobe(\"cgroup_control\");\n      probe_handler.cleanup_probe(\"cgroup_control\");\n    }\n  }\n\n  periodic_cb();\n  check_cb(\"cgroup prober cleanup()\");\n}\n\nvoid CgroupProber::trigger_cgroup_get_from_fd_probe(std::string const &cgroup_dir_name, std::function<void(void)> periodic_cb)\n{\n  std::stack<std::string> dirs_stack;\n  dirs_stack.emplace(cgroup_dir_name);\n  while (!dirs_stack.empty()) {\n    periodic_cb();\n    // get the directory on the top of our stack\n    std::string dir_name(dirs_stack.top());\n    dirs_stack.pop();\n\n    DIR *dir;\n    dir = opendir(dir_name.c_str());\n    if (!dir)\n      continue;\n\n    // trigger cgroup_get_from_fd via BPF_PROG_QUERY for this directory\n    int cgroup_fd = open(dir_name.c_str(), O_RDONLY);\n    if (cgroup_fd >= 0) {\n      LOG::debug_in(AgentLogKind::CGROUPS, \"cgroup_get_from_fd probe: path={}\", dir_name);\n\n      // Set up BPF_PROG_QUERY attributes\n      union bpf_attr attr = {};\n      attr.query.target_fd = cgroup_fd;\n      attr.query.attach_type = BPF_CGROUP_INET_INGRESS; // Any valid attach type\n      attr.query.query_flags = 0;\n      attr.query.prog_cnt = 0; // Set to 0 to just get count\n      attr.query.prog_ids = 0; // NULL to just query count\n\n      // This triggers cgroup_get_from_fd()!\n      int result = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, sizeof(attr));\n      (void)result; // Suppress unused variable warning\n\n      close(cgroup_fd);\n    } else {\n      LOG::debug_in(AgentLogKind::CGROUPS, \"   fail to open path={}\", dir_name);\n    }\n\n    // iterate over the elements of this directory and add any\n    // subdirectories to dirs_stack\n    struct dirent *ent;\n    while ((ent = readdir(dir)) != NULL) {\n      if (ent->d_type == DT_DIR) {\n        // skip over \".\" and \"..\" entries in the directory\n        if ((strcmp(ent->d_name, \".\") == 0) || (strcmp(ent->d_name, \"..\") == 0))\n          continue;\n\n        dirs_stack.emplace(dir_name + \"/\" + ent->d_name);\n      }\n      periodic_cb();\n    }\n    int status = closedir(dir);\n    if (status != 0) {\n      close_dir_error_count_++;\n    }\n  }\n}\n\nvoid CgroupProber::trigger_existing_cgroup_probe(\n    std::string const &cgroup_dir_name, std::string const &file_name, std::function<void(void)> periodic_cb)\n{\n  std::stack<std::string> dirs_stack;\n  dirs_stack.emplace(cgroup_dir_name);\n  while (!dirs_stack.empty()) {\n    periodic_cb();\n    // get the directory on the top of our stack\n    std::string dir_name(dirs_stack.top());\n    dirs_stack.pop();\n\n    DIR *dir;\n    dir = opendir(dir_name.c_str());\n    if (!dir)\n      continue;\n\n    // trigger the cgroup existing probe for this directory\n    std::string path = dir_name + \"/\" + file_name;\n    LOG::debug_in(AgentLogKind::CGROUPS, \"cgroup existing probe: path={}\", path);\n    std::ifstream file(path.c_str());\n    if (file.fail()) {\n      LOG::debug_in(AgentLogKind::CGROUPS, \"   fail for path={}\", path);\n      int status = closedir(dir);\n      if (status != 0) {\n        close_dir_error_count_++;\n      }\n      continue;\n    } else {\n      LOG::debug_in(AgentLogKind::CGROUPS, \"   success for path={}\", path);\n    }\n    std::string line;\n    std::getline(file, line);\n\n    // iterate over the elements of this directory and add any\n    // subdirectories to dirs_stack\n    struct dirent *ent;\n    while ((ent = readdir(dir)) != NULL) {\n      if (ent->d_type == DT_DIR) {\n        // skip over \".\" and \"..\" entries in the directory\n        if ((strcmp(ent->d_name, \".\") == 0) || (strcmp(ent->d_name, \"..\") == 0))\n          continue;\n\n        dirs_stack.emplace(dir_name + \"/\" + ent->d_name);\n      }\n      periodic_cb();\n    }\n    int status = closedir(dir);\n    if (status != 0) {\n      close_dir_error_count_++;\n    }\n  }\n}\n\nstatic bool file_exists(std::string file_path)\n{\n  struct stat sb;\n\n  if (stat(file_path.c_str(), &sb) == -1) {\n    return false;\n  }\n\n  return S_ISREG(sb.st_mode);\n}\n\nstatic bool is_cgroup_v1_mountpoint(std::string dir_path)\n{\n  static const std::string file_name(\"/cgroup.clone_children\");\n\n  return file_exists(dir_path + file_name);\n}\n\nstatic bool is_cgroup_v2_mountpoint(std::string dir_path)\n{\n  static const std::string file_name(\"/cgroup.controllers\");\n\n  return file_exists(dir_path + file_name);\n}\n\nstd::string CgroupProber::find_cgroup_v1_mountpoint()\n{\n  static const std::vector<std::string> cgroup_v1_mountpoints = {\n      \"/hostfs/sys/fs/cgroup/memory\", \"/hostfs/cgroup/memory\", \"/sys/fs/cgroup/memory\", \"/cgroup/memory\"};\n\n  for (auto const &mountpoint : cgroup_v1_mountpoints) {\n    if (is_cgroup_v1_mountpoint(mountpoint)) {\n      return mountpoint;\n    }\n  }\n\n  return std::string();\n}\n\nstd::string CgroupProber::find_cgroup_v2_mountpoint()\n{\n  static const std::vector<std::string> cgroup_v2_mountpoints = {\"/hostfs/sys/fs/cgroup\", \"/sys/fs/cgroup\"};\n\n  for (auto const &mountpoint : cgroup_v2_mountpoints) {\n    if (is_cgroup_v2_mountpoint(mountpoint)) {\n      return mountpoint;\n    }\n  }\n\n  return std::string();\n}\n"
  },
  {
    "path": "collector/kernel/cgroup_prober.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/host_info.h>\n#include <util/logger.h>\n\n#include <linux/bpf.h>\n\n#include <functional>\n#include <string>\n\n/* forward declarations */\nclass ProbeHandler;\nstruct render_bpf_bpf;\n\n/**\n * Adds BPF probes for new and existing cgroups, and iterates through existing\n *   cgroups to obtain an up-to-date view of system cgroups\n */\nclass CgroupProber {\npublic:\n  /**\n   * C'tor\n   *\n   * @param probe_handler: a ProbeHandler where new probes can be registered\n   * @param bpf_module: the module from the bpf source code\n   * @param periodic_cb: a callback to be called every once in a while, to\n   *   allow user to e.g., flush rings\n   */\n  CgroupProber(\n      ProbeHandler &probe_handler,\n      struct render_bpf_bpf *skel,\n      HostInfo const &host_info,\n      std::function<void(void)> periodic_cb,\n      std::function<void(std::string)> check_cb);\n\n  int error_count() { return close_dir_error_count_; };\n\nprivate:\n  /**\n   * Locates the cgroup v1 directory that should be used for probing.\n   */\n  static std::string find_cgroup_v1_mountpoint();\n\n  /**\n   * Locates the cgroup v2 directory that should be used for probing.\n   */\n  static std::string find_cgroup_v2_mountpoint();\n\n  /**\n   * Recursively walks through directory structure and triggers the\n   * corresponding existing croup probe by reading the file_name specified.\n   *\n   * @param cgroup_dir_name: path to directory in which to perform the search\n   * @param file_name: file name to read\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void trigger_existing_cgroup_probe(\n      std::string const &cgroup_dir_name, std::string const &file_name, std::function<void(void)> periodic_cb);\n\n  /**\n   * Recursively walks through directory structure and triggers the\n   * cgroup_get_from_fd probe by calling BPF_PROG_QUERY on each cgroup directory.\n   *\n   * @param cgroup_dir_name: path to directory in which to perform the search\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void trigger_cgroup_get_from_fd_probe(std::string const &cgroup_dir_name, std::function<void(void)> periodic_cb);\n\n  HostInfo const host_info_;\n  int close_dir_error_count_;\n};\n"
  },
  {
    "path": "collector/kernel/dns/ares.h",
    "content": "\n/* Copyright 1998 by the Massachusetts Institute of Technology.\n * Copyright (C) 2007-2013 by Daniel Stenberg\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n#ifndef ARES__H\n#define ARES__H\n\n#include \"ares_build.h\"   /* c-ares build definitions */\n#include \"ares_version.h\" /* c-ares version defines   */\n\n/*\n * Define WIN32 when build target is Win32 API\n */\n\n#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) && !defined(__SYMBIAN32__)\n#define WIN32\n#endif\n\n#include <sys/types.h>\n\n/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish\n   libc5-based Linux systems. Only include it on system that are known to\n   require it! */\n#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || defined(__minix) || defined(__SYMBIAN32__) ||          \\\n    defined(__INTEGRITY) || defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || defined(__QNXNTO__)\n#include <sys/select.h>\n#endif\n#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))\n#include <sys/bsdskt.h>\n#endif\n\n#if defined(WATT32)\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <tcp.h>\n#elif defined(_WIN32_WCE)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#include <winsock.h>\n#elif defined(WIN32)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#else\n#include <netinet/in.h>\n#include <sys/socket.h>\n#endif\n\n#if defined(ANDROID) || defined(__ANDROID__)\n#include <jni.h>\n#endif\n\ntypedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t;\ntypedef CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n** c-ares external API function linkage decorations.\n*/\n\n#ifdef CARES_STATICLIB\n#define CARES_EXTERN\n#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)\n#if defined(CARES_BUILDING_LIBRARY)\n#define CARES_EXTERN __declspec(dllexport)\n#else\n#define CARES_EXTERN __declspec(dllimport)\n#endif\n#elif defined(CARES_BUILDING_LIBRARY) && defined(CARES_SYMBOL_HIDING)\n#define CARES_EXTERN CARES_SYMBOL_SCOPE_EXTERN\n#else\n#define CARES_EXTERN\n#endif\n\n#define ARES_SUCCESS 0\n\n/* Server error codes (ARES_ENODATA indicates no relevant answer) */\n#define ARES_ENODATA 1\n#define ARES_EFORMERR 2\n#define ARES_ESERVFAIL 3\n#define ARES_ENOTFOUND 4\n#define ARES_ENOTIMP 5\n#define ARES_EREFUSED 6\n\n/* Locally generated error codes */\n#define ARES_EBADQUERY 7\n#define ARES_EBADNAME 8\n#define ARES_EBADFAMILY 9\n#define ARES_EBADRESP 10\n#define ARES_ECONNREFUSED 11\n#define ARES_ETIMEOUT 12\n#define ARES_EOF 13\n#define ARES_EFILE 14\n#define ARES_ENOMEM 15\n#define ARES_EDESTRUCTION 16\n#define ARES_EBADSTR 17\n\n/* ares_getnameinfo error codes */\n#define ARES_EBADFLAGS 18\n\n/* ares_getaddrinfo error codes */\n#define ARES_ENONAME 19\n#define ARES_EBADHINTS 20\n\n/* Uninitialized library error code */\n#define ARES_ENOTINITIALIZED 21 /* introduced in 1.7.0 */\n\n/* ares_library_init error codes */\n#define ARES_ELOADIPHLPAPI 22         /* introduced in 1.7.0 */\n#define ARES_EADDRGETNETWORKPARAMS 23 /* introduced in 1.7.0 */\n\n/* More error codes */\n#define ARES_ECANCELLED 24 /* introduced in 1.7.0 */\n\n/* Flag values */\n#define ARES_FLAG_USEVC (1 << 0)\n#define ARES_FLAG_PRIMARY (1 << 1)\n#define ARES_FLAG_IGNTC (1 << 2)\n#define ARES_FLAG_NORECURSE (1 << 3)\n#define ARES_FLAG_STAYOPEN (1 << 4)\n#define ARES_FLAG_NOSEARCH (1 << 5)\n#define ARES_FLAG_NOALIASES (1 << 6)\n#define ARES_FLAG_NOCHECKRESP (1 << 7)\n#define ARES_FLAG_EDNS (1 << 8)\n\n/* Option mask values */\n#define ARES_OPT_FLAGS (1 << 0)\n#define ARES_OPT_TIMEOUT (1 << 1)\n#define ARES_OPT_TRIES (1 << 2)\n#define ARES_OPT_NDOTS (1 << 3)\n#define ARES_OPT_UDP_PORT (1 << 4)\n#define ARES_OPT_TCP_PORT (1 << 5)\n#define ARES_OPT_SERVERS (1 << 6)\n#define ARES_OPT_DOMAINS (1 << 7)\n#define ARES_OPT_LOOKUPS (1 << 8)\n#define ARES_OPT_SOCK_STATE_CB (1 << 9)\n#define ARES_OPT_SORTLIST (1 << 10)\n#define ARES_OPT_SOCK_SNDBUF (1 << 11)\n#define ARES_OPT_SOCK_RCVBUF (1 << 12)\n#define ARES_OPT_TIMEOUTMS (1 << 13)\n#define ARES_OPT_ROTATE (1 << 14)\n#define ARES_OPT_EDNSPSZ (1 << 15)\n#define ARES_OPT_NOROTATE (1 << 16)\n\n/* Nameinfo flag values */\n#define ARES_NI_NOFQDN (1 << 0)\n#define ARES_NI_NUMERICHOST (1 << 1)\n#define ARES_NI_NAMEREQD (1 << 2)\n#define ARES_NI_NUMERICSERV (1 << 3)\n#define ARES_NI_DGRAM (1 << 4)\n#define ARES_NI_TCP 0\n#define ARES_NI_UDP ARES_NI_DGRAM\n#define ARES_NI_SCTP (1 << 5)\n#define ARES_NI_DCCP (1 << 6)\n#define ARES_NI_NUMERICSCOPE (1 << 7)\n#define ARES_NI_LOOKUPHOST (1 << 8)\n#define ARES_NI_LOOKUPSERVICE (1 << 9)\n/* Reserved for future use */\n#define ARES_NI_IDN (1 << 10)\n#define ARES_NI_IDN_ALLOW_UNASSIGNED (1 << 11)\n#define ARES_NI_IDN_USE_STD3_ASCII_RULES (1 << 12)\n\n/* Addrinfo flag values */\n#define ARES_AI_CANONNAME (1 << 0)\n#define ARES_AI_NUMERICHOST (1 << 1)\n#define ARES_AI_PASSIVE (1 << 2)\n#define ARES_AI_NUMERICSERV (1 << 3)\n#define ARES_AI_V4MAPPED (1 << 4)\n#define ARES_AI_ALL (1 << 5)\n#define ARES_AI_ADDRCONFIG (1 << 6)\n/* Reserved for future use */\n#define ARES_AI_IDN (1 << 10)\n#define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11)\n#define ARES_AI_IDN_USE_STD3_ASCII_RULES (1 << 12)\n#define ARES_AI_CANONIDN (1 << 13)\n\n#define ARES_AI_MASK                                                                                                           \\\n  (ARES_AI_CANONNAME | ARES_AI_NUMERICHOST | ARES_AI_PASSIVE | ARES_AI_NUMERICSERV | ARES_AI_V4MAPPED | ARES_AI_ALL |          \\\n   ARES_AI_ADDRCONFIG)\n#define ARES_GETSOCK_MAXNUM                                                                                                    \\\n  16 /* ares_getsock() can return info about this                                                                              \\\n        many sockets */\n#define ARES_GETSOCK_READABLE(bits, num) (bits & (1 << (num)))\n#define ARES_GETSOCK_WRITABLE(bits, num) (bits & (1 << ((num) + ARES_GETSOCK_MAXNUM)))\n\n/* c-ares library initialization flag values */\n#define ARES_LIB_INIT_NONE (0)\n#define ARES_LIB_INIT_WIN32 (1 << 0)\n#define ARES_LIB_INIT_ALL (ARES_LIB_INIT_WIN32)\n\n/*\n * Typedef our socket type\n */\n\n#ifndef ares_socket_typedef\n#ifdef WIN32\ntypedef SOCKET ares_socket_t;\n#define ARES_SOCKET_BAD INVALID_SOCKET\n#else\ntypedef int ares_socket_t;\n#define ARES_SOCKET_BAD -1\n#endif\n#define ares_socket_typedef\n#endif /* ares_socket_typedef */\n\ntypedef void (*ares_sock_state_cb)(void *data, ares_socket_t socket_fd, int readable, int writable);\n\nstruct apattern;\n\n/* NOTE about the ares_options struct to users and developers.\n\n   This struct will remain looking like this. It will not be extended nor\n   shrunk in future releases, but all new options will be set by ares_set_*()\n   options instead of with the ares_init_options() function.\n\n   Eventually (in a galaxy far far away), all options will be settable by\n   ares_set_*() options and the ares_init_options() function will become\n   deprecated.\n\n   When new options are added to c-ares, they are not added to this\n   struct. And they are not \"saved\" with the ares_save_options() function but\n   instead we encourage the use of the ares_dup() function. Needless to say,\n   if you add config options to c-ares you need to make sure ares_dup()\n   duplicates this new option.\n\n */\nstruct ares_options {\n  int flags;\n  int timeout; /* in seconds or milliseconds, depending on options */\n  int tries;\n  int ndots;\n  unsigned short udp_port;\n  unsigned short tcp_port;\n  int socket_send_buffer_size;\n  int socket_receive_buffer_size;\n  struct in_addr *servers;\n  int nservers;\n  char **domains;\n  int ndomains;\n  char *lookups;\n  ares_sock_state_cb sock_state_cb;\n  void *sock_state_cb_data;\n  struct apattern *sortlist;\n  int nsort;\n  int ednspsz;\n};\n\nstruct hostent;\nstruct timeval;\nstruct sockaddr;\nstruct ares_channeldata;\n\ntypedef struct ares_channeldata *ares_channel;\n\ntypedef void (*ares_callback)(void *arg, int status, int timeouts, unsigned char *abuf, int alen);\n\ntypedef void (*ares_host_callback)(void *arg, int status, int timeouts, struct hostent *hostent);\n\ntypedef void (*ares_nameinfo_callback)(void *arg, int status, int timeouts, char *node, char *service);\n\ntypedef int (*ares_sock_create_callback)(ares_socket_t socket_fd, int type, void *data);\n\ntypedef int (*ares_sock_config_callback)(ares_socket_t socket_fd, int type, void *data);\n\nCARES_EXTERN int ares_library_init(int flags);\n\nCARES_EXTERN int ares_library_init_mem(\n    int flags, void *(*amalloc)(size_t size), void (*afree)(void *ptr), void *(*arealloc)(void *ptr, size_t size));\n\n#if defined(ANDROID) || defined(__ANDROID__)\nCARES_EXTERN void ares_library_init_jvm(JavaVM *jvm);\nCARES_EXTERN int ares_library_init_android(jobject connectivity_manager);\nCARES_EXTERN int ares_library_android_initialized(void);\n#endif\n\nCARES_EXTERN int ares_library_initialized(void);\n\nCARES_EXTERN void ares_library_cleanup(void);\n\nCARES_EXTERN const char *ares_version(int *version);\n\nCARES_EXTERN int ares_init(ares_channel *channelptr);\n\nCARES_EXTERN int ares_init_options(ares_channel *channelptr, struct ares_options *options, int optmask);\n\nCARES_EXTERN int ares_save_options(ares_channel channel, struct ares_options *options, int *optmask);\n\nCARES_EXTERN void ares_destroy_options(struct ares_options *options);\n\nCARES_EXTERN int ares_dup(ares_channel *dest, ares_channel src);\n\nCARES_EXTERN void ares_destroy(ares_channel channel);\n\nCARES_EXTERN void ares_cancel(ares_channel channel);\n\n/* These next 3 configure local binding for the out-going socket\n * connection.  Use these to specify source IP and/or network device\n * on multi-homed systems.\n */\nCARES_EXTERN void ares_set_local_ip4(ares_channel channel, unsigned int local_ip);\n\n/* local_ip6 should be 16 bytes in length */\nCARES_EXTERN void ares_set_local_ip6(ares_channel channel, const unsigned char *local_ip6);\n\n/* local_dev_name should be null terminated. */\nCARES_EXTERN void ares_set_local_dev(ares_channel channel, const char *local_dev_name);\n\nCARES_EXTERN void ares_set_socket_callback(ares_channel channel, ares_sock_create_callback callback, void *user_data);\n\nCARES_EXTERN void ares_set_socket_configure_callback(ares_channel channel, ares_sock_config_callback callback, void *user_data);\n\nCARES_EXTERN int ares_set_sortlist(ares_channel channel, const char *sortstr);\n\n/*\n * Virtual function set to have user-managed socket IO.\n * Note that all functions need to be defined, and when\n * set, the library will not do any bind nor set any\n * socket options, assuming the client handles these\n * through either socket creation or the\n * ares_sock_config_callback call.\n */\nstruct iovec;\nstruct ares_socket_functions {\n  ares_socket_t (*asocket)(int, int, int, void *);\n  int (*aclose)(ares_socket_t, void *);\n  int (*aconnect)(ares_socket_t, const struct sockaddr *, ares_socklen_t, void *);\n  ares_ssize_t (*arecvfrom)(ares_socket_t, void *, size_t, int, struct sockaddr *, ares_socklen_t *, void *);\n  ares_ssize_t (*asendv)(ares_socket_t, const struct iovec *, int, void *);\n};\n\nCARES_EXTERN void ares_set_socket_functions(ares_channel channel, const struct ares_socket_functions *funcs, void *user_data);\n\nCARES_EXTERN void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, ares_callback callback, void *arg);\n\nCARES_EXTERN void ares_query(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg);\n\nCARES_EXTERN void\nares_search(ares_channel channel, const char *name, int dnsclass, int type, ares_callback callback, void *arg);\n\nCARES_EXTERN void\nares_gethostbyname(ares_channel channel, const char *name, int family, ares_host_callback callback, void *arg);\n\nCARES_EXTERN int ares_gethostbyname_file(ares_channel channel, const char *name, int family, struct hostent **host);\n\nCARES_EXTERN void\nares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen, int family, ares_host_callback callback, void *arg);\n\nCARES_EXTERN void ares_getnameinfo(\n    ares_channel channel,\n    const struct sockaddr *sa,\n    ares_socklen_t salen,\n    int flags,\n    ares_nameinfo_callback callback,\n    void *arg);\n\nCARES_EXTERN int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds);\n\nCARES_EXTERN int ares_getsock(ares_channel channel, ares_socket_t *socks, int numsocks);\n\nCARES_EXTERN struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv, struct timeval *tv);\n\nCARES_EXTERN void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds);\n\nCARES_EXTERN void ares_process_fd(ares_channel channel, ares_socket_t read_fd, ares_socket_t write_fd);\n\nCARES_EXTERN int ares_create_query(\n    const char *name, int dnsclass, int type, unsigned short id, int rd, unsigned char **buf, int *buflen, int max_udp_size);\n\nCARES_EXTERN int\nares_mkquery(const char *name, int dnsclass, int type, unsigned short id, int rd, unsigned char **buf, int *buflen);\n\nCARES_EXTERN int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen);\n\nCARES_EXTERN int\nares_expand_string(const unsigned char *encoded, const unsigned char *abuf, int alen, unsigned char **s, long *enclen);\n\n/*\n * NOTE: before c-ares 1.7.0 we would most often use the system in6_addr\n * struct below when ares itself was built, but many apps would use this\n * private version since the header checked a HAVE_* define for it. Starting\n * with 1.7.0 we always declare and use our own to stop relying on the\n * system's one.\n */\nstruct ares_in6_addr {\n  union {\n    unsigned char _S6_u8[16];\n  } _S6_un;\n};\n\nstruct ares_addrttl {\n  struct in_addr ipaddr;\n  int ttl;\n};\n\nstruct ares_addr6ttl {\n  struct ares_in6_addr ip6addr;\n  int ttl;\n};\n\nstruct ares_srv_reply {\n  struct ares_srv_reply *next;\n  char *host;\n  unsigned short priority;\n  unsigned short weight;\n  unsigned short port;\n};\n\nstruct ares_mx_reply {\n  struct ares_mx_reply *next;\n  char *host;\n  unsigned short priority;\n};\n\nstruct ares_txt_reply {\n  struct ares_txt_reply *next;\n  unsigned char *txt;\n  size_t length; /* length excludes null termination */\n};\n\n/* NOTE: This structure is a superset of ares_txt_reply\n */\nstruct ares_txt_ext {\n  struct ares_txt_ext *next;\n  unsigned char *txt;\n  size_t length;\n  /* 1 - if start of new record\n   * 0 - if a chunk in the same record */\n  unsigned char record_start;\n};\n\nstruct ares_naptr_reply {\n  struct ares_naptr_reply *next;\n  unsigned char *flags;\n  unsigned char *service;\n  unsigned char *regexp;\n  char *replacement;\n  unsigned short order;\n  unsigned short preference;\n};\n\nstruct ares_soa_reply {\n  char *nsname;\n  char *hostmaster;\n  unsigned int serial;\n  unsigned int refresh;\n  unsigned int retry;\n  unsigned int expire;\n  unsigned int minttl;\n};\n\n/*\n** Parse the buffer, starting at *abuf and of length alen bytes, previously\n** obtained from an ares_search call.  Put the results in *host, if nonnull.\n** Also, if addrttls is nonnull, put up to *naddrttls IPv4 addresses along with\n** their TTLs in that array, and set *naddrttls to the number of addresses\n** so written.\n*/\n\nCARES_EXTERN int\nares_parse_a_reply(const unsigned char *abuf, int alen, struct hostent **host, struct ares_addrttl *addrttls, int *naddrttls);\n\nCARES_EXTERN int ares_parse_aaaa_reply(\n    const unsigned char *abuf, int alen, struct hostent **host, struct ares_addr6ttl *addrttls, int *naddrttls);\n\nCARES_EXTERN int\nares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, int addrlen, int family, struct hostent **host);\n\nCARES_EXTERN int ares_parse_ns_reply(const unsigned char *abuf, int alen, struct hostent **host);\n\nCARES_EXTERN int ares_parse_srv_reply(const unsigned char *abuf, int alen, struct ares_srv_reply **srv_out);\n\nCARES_EXTERN int ares_parse_mx_reply(const unsigned char *abuf, int alen, struct ares_mx_reply **mx_out);\n\nCARES_EXTERN int ares_parse_txt_reply(const unsigned char *abuf, int alen, struct ares_txt_reply **txt_out);\n\nCARES_EXTERN int ares_parse_txt_reply_ext(const unsigned char *abuf, int alen, struct ares_txt_ext **txt_out);\n\nCARES_EXTERN int ares_parse_naptr_reply(const unsigned char *abuf, int alen, struct ares_naptr_reply **naptr_out);\n\nCARES_EXTERN int ares_parse_soa_reply(const unsigned char *abuf, int alen, struct ares_soa_reply **soa_out);\n\nCARES_EXTERN void ares_free_string(void *str);\n\nCARES_EXTERN void ares_free_hostent(struct hostent *host);\n\nCARES_EXTERN void ares_free_data(void *dataptr);\n\nCARES_EXTERN const char *ares_strerror(int code);\n\nstruct ares_addr_node {\n  struct ares_addr_node *next;\n  int family;\n  union {\n    struct in_addr addr4;\n    struct ares_in6_addr addr6;\n  } addr;\n};\n\nstruct ares_addr_port_node {\n  struct ares_addr_port_node *next;\n  int family;\n  union {\n    struct in_addr addr4;\n    struct ares_in6_addr addr6;\n  } addr;\n  int udp_port;\n  int tcp_port;\n};\n\nCARES_EXTERN int ares_set_servers(ares_channel channel, struct ares_addr_node *servers);\nCARES_EXTERN int ares_set_servers_ports(ares_channel channel, struct ares_addr_port_node *servers);\n\n/* Incomming string format: host[:port][,host[:port]]... */\nCARES_EXTERN int ares_set_servers_csv(ares_channel channel, const char *servers);\nCARES_EXTERN int ares_set_servers_ports_csv(ares_channel channel, const char *servers);\n\nCARES_EXTERN int ares_get_servers(ares_channel channel, struct ares_addr_node **servers);\nCARES_EXTERN int ares_get_servers_ports(ares_channel channel, struct ares_addr_port_node **servers);\n\nCARES_EXTERN const char *ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size);\n\nCARES_EXTERN int ares_inet_pton(int af, const char *src, void *dst);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* ARES__H */\n"
  },
  {
    "path": "collector/kernel/dns/ares_expand_name.c",
    "content": "\n/* Copyright 1998, 2011 by the Massachusetts Institute of Technology.\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n#include <arpa/nameser.h>\n#include <netinet/in.h>\n\n#include \"ares.h\"\n\n#include <collector/kernel/dns/dns.h>\n\n/* Maximum number of indirections allowed for a name */\n#define MAX_INDIRS 50\n\n/** from ares_nowarn.c **/\n/*\n** unsigned size_t to signed long\n*/\n#define CARES_MASK_SLONG 0x7FFFFFFFL\nlong _aresx_uztosl(size_t uznum)\n{\n#ifdef __INTEL_COMPILER\n#pragma warning(push)\n#pragma warning(disable : 810) /* conversion may lose significant bits */\n#endif\n\n  return (long)(uznum & (size_t)CARES_MASK_SLONG);\n\n#ifdef __INTEL_COMPILER\n#pragma warning(pop)\n#endif\n}\n\n/* Expand an RFC1035-encoded domain name given by encoded.  The\n * containing message is given by abuf and alen.  The result is written,\n * NUL-terminated, into s, whose size must be large enough to hold the\n * domain name, i.e., no smaller than dns_name_length()+1.\n * *enclen is set to the length of the encoded name (not the length of the\n * expanded name; the goal is to tell the caller how many bytes to\n * move forward to get past the encoded name).\n *\n * In the simple case, an encoded name is a series of labels, each\n * composed of a one-byte length (limited to values between 0 and 63\n * inclusive) followed by the label contents.  The name is terminated\n * by a zero-length label.\n *\n * In the more complicated case, a label may be terminated by an\n * indirection pointer, specified by two bytes with the high bits of\n * the first byte (corresponding to INDIR_MASK) set to 11.  With the\n * two high bits of the first byte stripped off, the indirection\n * pointer gives an offset from the beginning of the containing\n * message with more labels to decode.  Indirection can happen an\n * arbitrary number of times, so we have to detect loops.\n *\n * Since the expanded name uses '.' as a label separator, we use\n * backslashes to escape periods or backslashes in the expanded name.\n */\nvoid dns_expand_name(const unsigned char *encoded, const unsigned char *abuf, int alen, char *expanded_name, long *enclen)\n{\n  int len, indir = 0;\n  const unsigned char *p;\n  char *q = expanded_name;\n\n  /* No error-checking necessary; it was all done by name_length(). */\n  p = encoded;\n  while (*p) {\n    if ((*p & INDIR_MASK) == INDIR_MASK) {\n      if (!indir) {\n        *enclen = _aresx_uztosl(p + 2U - encoded);\n        indir = 1;\n      }\n      p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1));\n    } else {\n      len = *p;\n      p++;\n      while (len--) {\n        if (*p == '.' || *p == '\\\\')\n          *q++ = '\\\\';\n        *q++ = *p;\n        p++;\n      }\n      *q++ = '.';\n    }\n  }\n  if (!indir)\n    *enclen = _aresx_uztosl(p + 1U - encoded);\n\n  /* Nuke the trailing period if we wrote one. */\n  if (q > expanded_name)\n    *(q - 1) = 0;\n  else\n    *q = 0; /* zero terminate the zero-length domain */\n}\n\n/* Return the length of the expansion of an encoded domain name, or\n * -1 if the encoding is invalid.\n */\nint dns_name_length(const unsigned char *encoded, const unsigned char *abuf, int alen)\n{\n  int n = 0, offset, indir = 0, top;\n\n  /* Allow the caller to pass us abuf + alen and have us check for it. */\n  if (encoded >= abuf + alen)\n    return -1;\n\n  while (*encoded) {\n    top = (*encoded & INDIR_MASK);\n    if (top == INDIR_MASK) {\n      /* Check the offset and go there. */\n      if (encoded + 1 >= abuf + alen)\n        return -1;\n      offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1);\n      if (offset >= alen)\n        return -1;\n      encoded = abuf + offset;\n\n      /* If we've seen more indirects than the message length,\n       * then there's a loop.\n       */\n      ++indir;\n      if (indir > alen || indir > MAX_INDIRS)\n        return -1;\n    } else if (top == 0x00) {\n      offset = *encoded;\n      if (encoded + offset + 1 >= abuf + alen)\n        return -1;\n      encoded++;\n      while (offset--) {\n        n += (*encoded == '.' || *encoded == '\\\\') ? 2 : 1;\n        encoded++;\n      }\n      n++;\n    } else {\n      /* RFC 1035 4.1.4 says other options (01, 10) for top 2\n       * bits are reserved.\n       */\n      return -1;\n    }\n  }\n\n  /* If there were any labels at all, then the number of dots is one\n   * less than the number of labels, so subtract one.\n   */\n  return (n) ? n - 1 : n;\n}\n\n/**\n * This version of expand name assumes s is of length DNS_NAME_MAX_LENGTH and\n * fails if the expanded name exceeds that. If @expanded_len is not NULL, the\n *   expanded length is written there.\n */\nint dns_expand_name_maxlen(\n    const unsigned char *encoded, const unsigned char *abuf, int alen, char *expanded_name, long *enclen, int *expanded_len)\n{\n  int len = dns_name_length(encoded, abuf, alen);\n  if (len < 0)\n    return ARES_EBADNAME;\n\n  if (len + 1 > DNS_NAME_MAX_LENGTH)\n    return ARES_ENOMEM;\n\n  dns_expand_name(encoded, abuf, alen, expanded_name, enclen);\n\n  if (expanded_len)\n    *expanded_len = len;\n\n  return ARES_SUCCESS;\n}\n"
  },
  {
    "path": "collector/kernel/dns/ares_parse_a_aaaa_reply.c",
    "content": "\n/* Copyright 1998 by the Massachusetts Institute of Technology.\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n#include <limits.h>\n#include <string.h>\n#include <strings.h>\n\n#include \"ares.h\"\n#include \"ares_dns.h\"\n\n#include <collector/kernel/dns/c_ares_nameser.h>\n#include <collector/kernel/dns/dns.h>\n\n/**\n * The hostname of the reply will be written to @hostname_out, and its length\n *   to @hostname_len. @hostname_out must be at least DNS_NAME_MAX_LENGTH\n *   characters long.\n */\n\nint dns_parse_a_aaaa_reply(\n    const unsigned char *abuf,\n    int alen,\n    char *hostname_out,\n    int *hostname_len,\n    struct in_addr *in_addrs_out,\n    int *num_in_addrs,\n    struct in6_addr *in6_addrs_out,\n    int *num_in6_addrs)\n{\n  unsigned int qdcount, ancount;\n  int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs, n6addrs;\n  int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */\n  int naliases;\n  long len;\n  const unsigned char *aptr;\n  char *hostname = hostname_out;\n  char rr_name[DNS_NAME_MAX_LENGTH], rr_data[DNS_NAME_MAX_LENGTH];\n  const int max_in_addrs = (in_addrs_out && num_in_addrs) ? *num_in_addrs : 0;\n  const int max_in6_addrs = (in6_addrs_out && num_in6_addrs) ? *num_in6_addrs : 0;\n\n  /* Set number of addresses returned to NULL for all failure cases. */\n  if (num_in_addrs) {\n    *num_in_addrs = 0;\n  }\n  if (num_in6_addrs) {\n    *num_in6_addrs = 0;\n  }\n\n  /* Give up if abuf doesn't have room for a header. */\n  if (alen < HFIXEDSZ) {\n    return ARES_EBADRESP;\n  }\n\n  /* Ensure this is a response */\n  if (DNS_HEADER_QR(abuf) != 1) {\n    return ARES_EBADRESP;\n  }\n\n  /* Fetch the question and answer count from the header. */\n  qdcount = DNS_HEADER_QDCOUNT(abuf);\n  ancount = DNS_HEADER_ANCOUNT(abuf);\n  if (qdcount != 1) {\n    return ARES_EBADRESP;\n  }\n\n  /* Expand the name from the question, and skip past the question. */\n  aptr = abuf + HFIXEDSZ;\n  status = dns_expand_name_maxlen(aptr, abuf, alen, hostname, &len, hostname_len);\n  if (status != ARES_SUCCESS) {\n    return status;\n  }\n  if (aptr + len + QFIXEDSZ > abuf + alen) {\n    return ARES_EBADRESP;\n  }\n  aptr += len + QFIXEDSZ;\n\n  naddrs = 0;\n  n6addrs = 0;\n  naliases = 0;\n\n  /* Examine each answer resource record (RR) in turn. */\n  for (i = 0; i < (int)ancount; i++) {\n    /* Decode the RR up to the data field. */\n    status = dns_expand_name_maxlen(aptr, abuf, alen, rr_name, &len, NULL);\n    if (status != ARES_SUCCESS) {\n      break;\n    }\n    aptr += len;\n    if (aptr + RRFIXEDSZ > abuf + alen) {\n      status = ARES_EBADRESP;\n      break;\n    }\n    rr_type = DNS_RR_TYPE(aptr);\n    rr_class = DNS_RR_CLASS(aptr);\n    rr_len = DNS_RR_LEN(aptr);\n    rr_ttl = DNS_RR_TTL(aptr);\n    aptr += RRFIXEDSZ;\n    if (aptr + rr_len > abuf + alen) {\n      status = ARES_EBADRESP;\n      break;\n    }\n\n    if (rr_class == C_IN && rr_type == T_A && rr_len == sizeof(struct in_addr) && strcasecmp(rr_name, hostname) == 0) {\n\n      if (naddrs < max_in_addrs) {\n        struct in_addr *at = &in_addrs_out[naddrs];\n        if (aptr + sizeof(struct in_addr) > abuf + alen) {\n          /* LCOV_EXCL_START: already checked above */\n          status = ARES_EBADRESP;\n          break;\n        } /* LCOV_EXCL_STOP */\n        memcpy(at, aptr, sizeof(struct in_addr));\n      }\n      naddrs++;\n      status = ARES_SUCCESS;\n\n    } else if (\n        rr_class == C_IN && rr_type == T_AAAA && rr_len == sizeof(struct ares_in6_addr) && strcasecmp(rr_name, hostname) == 0) {\n\n      if (n6addrs < max_in6_addrs) {\n        struct in6_addr *const at = &in6_addrs_out[n6addrs];\n        if (aptr + sizeof(struct in6_addr) > abuf + alen) {\n          /* LCOV_EXCL_START: already checked above */\n          status = ARES_EBADRESP;\n          break;\n        } /* LCOV_EXCL_STOP */\n        memcpy(at, aptr, sizeof(struct in6_addr));\n      }\n      n6addrs++;\n      status = ARES_SUCCESS;\n    }\n\n    if (rr_class == C_IN && rr_type == T_CNAME) {\n      /* Record the RR name as an alias. */\n      naliases++;\n\n      /* Decode the RR data and replace the hostname with it. */\n      status = dns_expand_name_maxlen(aptr, abuf, alen, rr_data, &len, NULL);\n      if (status != ARES_SUCCESS) {\n        break;\n      }\n      hostname = rr_data;\n\n      /* Take the min of the TTLs we see in the CNAME chain. */\n      if (cname_ttl > rr_ttl) {\n        cname_ttl = rr_ttl;\n      }\n    }\n\n    aptr += rr_len;\n    if (aptr > abuf + alen) {\n      /* LCOV_EXCL_START: already checked above */\n      status = ARES_EBADRESP;\n      break;\n    } /* LCOV_EXCL_STOP */\n  }\n\n  /* here we are a bit aggressive and write a response even on failure so\n   * caller can choose to use what we got so far */\n  if (num_in_addrs) {\n    *num_in_addrs = naddrs < max_in_addrs ? naddrs : max_in_addrs;\n  }\n  if (num_in6_addrs) {\n    *num_in6_addrs = n6addrs < max_in6_addrs ? n6addrs : max_in6_addrs;\n  }\n\n  if (status == ARES_SUCCESS && naddrs == 0 && n6addrs == 0 && naliases == 0) {\n    /* the check for naliases to be zero is to make sure CNAME responses\n       don't get caught here */\n    status = ARES_ENODATA;\n  }\n\n  return status;\n}\n"
  },
  {
    "path": "collector/kernel/dns/ares_parse_query.c",
    "content": "\n/* Copyright 1998 by the Massachusetts Institute of Technology.\n *\n * Permission to use, copy, modify, and distribute this\n * software and its documentation for any purpose and without\n * fee is hereby granted, provided that the above copyright\n * notice appear in all copies and that both that copyright\n * notice and this permission notice appear in supporting\n * documentation, and that the name of M.I.T. not be used in\n * advertising or publicity pertaining to distribution of the\n * software without specific, written prior permission.\n * M.I.T. makes no representations about the suitability of\n * this software for any purpose.  It is provided \"as is\"\n * without express or implied warranty.\n */\n\n#include <limits.h>\n#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n\n#include \"ares.h\"\n#include \"ares_dns.h\"\n\n#include <collector/kernel/dns/c_ares_nameser.h>\n#include <collector/kernel/dns/dns.h>\n\n// #define DEBUG_DNS_PARSE_QUERY 1\n\n/**\n * returns ARES_SUCCESS if this is a valid request or response,\n * ARES_EBADQUERY otherwise, or on error\n *\n * Whether or not this is a request or reply will be returned in *is_response\n * The question's type will be returned in *type_out\n * The transaction ID will returned in *qid_out\n *\n * The question (hostname) of the request will be written to @question_out, and\n * its length to @question_len. @hostname_out must be at least\n * DNS_NAME_MAX_LENGTH characters long.\n */\n\nint dns_parse_query(\n    const unsigned char *abuf,\n    int alen,\n    int *is_response,\n    uint16_t *type_out,\n    uint16_t *qid_out,\n    char *question_out,\n    int *question_len)\n{\n  unsigned int qdcount, ancount, nscount; //, arcount;\n  int qr, status, q_type, q_class;\n  long len;\n  uint16_t qid;\n  const unsigned char *aptr;\n\n  /* Give up if abuf doesn't have room for a header. */\n  if (alen < HFIXEDSZ) {\n#if DEBUG_DNS_PARSE_QUERY\n    fprintf(stderr, \"EBADQUERY: alen < HFIXEDSZ\\n\");\n#endif\n    return ARES_EBADQUERY;\n  }\n\n  /* Fetch the question and answer count from the header. */\n  qr = DNS_HEADER_QR(abuf);\n  qid = DNS_HEADER_QID(abuf);\n  qdcount = DNS_HEADER_QDCOUNT(abuf);\n  ancount = DNS_HEADER_ANCOUNT(abuf);\n  nscount = DNS_HEADER_NSCOUNT(abuf);\n  // arcount = DNS_HEADER_ARCOUNT(abuf);\n\n#if DEBUG_DNS_PARSE_QUERY\n  fprintf(\n      stderr, \"qr: %d qid: %d qdcount:%u ancount:%u nscount:%u arcount:%u\\n\", qr, (int)qid, qdcount, ancount, nscount, arcount);\n#endif\n\n  /* Determine if this is a request */\n  if (qr == 0) {\n    /* if it's a request, only accept requests with a single question and\n     * nothing else */\n    if (qdcount != 1 || ancount != 0 || nscount != 0) {\n#if DEBUG_DNS_PARSE_QUERY\n      fprintf(stderr, \"EBADQUERY: request with more than one question or other records\\n\");\n#endif\n      return ARES_EBADQUERY;\n    }\n    if (is_response)\n      *is_response = 0;\n  } else {\n    /* if it's a response, only accept responses to a single question */\n    if (qdcount != 1) {\n#if DEBUG_DNS_PARSE_QUERY\n      fprintf(stderr, \"EBADQUERY: response to not single question\\n\");\n#endif\n      return ARES_EBADQUERY;\n    }\n    if (is_response)\n      *is_response = 1;\n  }\n\n  /* Return the transaction id */\n  if (qid_out) {\n    *qid_out = qid;\n  }\n\n  /* Expand the name from the question */\n  aptr = abuf + HFIXEDSZ;\n  status = dns_expand_name_maxlen(aptr, abuf, alen, question_out, &len, question_len);\n  if (status != ARES_SUCCESS) {\n#if DEBUG_DNS_PARSE_QUERY\n    fprintf(stderr, \"EBADQUERY: couldn't expand name\\n\");\n#endif\n    return status;\n  }\n  if (aptr + len + QFIXEDSZ > abuf + alen) {\n#if DEBUG_DNS_PARSE_QUERY\n    fprintf(stderr, \"EBADQUERY: truncated packet\\n\");\n#endif\n    return ARES_EBADQUERY;\n  }\n  aptr += len;\n\n  q_type = DNS_QUESTION_TYPE(aptr);\n  q_class = DNS_QUESTION_CLASS(aptr);\n\n  /* Reject anything that isn't an Internet query */\n  if (q_class != ns_c_in) {\n#if DEBUG_DNS_PARSE_QUERY\n    fprintf(stderr, \"EBADQUERY: non-internet query\\n\");\n#endif\n    return ARES_EBADQUERY;\n  }\n\n  if (type_out) {\n    *type_out = q_type;\n  }\n\n  return ARES_SUCCESS;\n}\n"
  },
  {
    "path": "collector/kernel/dns/c_ares_nameser.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef ARES_NAMESER_H\n#define ARES_NAMESER_H\n\n/* header file provided by liren@vivisimo.com */\n\n#ifndef HAVE_ARPA_NAMESER_H\n\n#define NS_PACKETSZ 512  /* maximum packet size */\n#define NS_MAXDNAME 256  /* maximum domain name */\n#define NS_MAXCDNAME 255 /* maximum compressed domain name */\n#define NS_MAXLABEL 63\n#define NS_HFIXEDSZ 12  /* #/bytes of fixed data in header */\n#define NS_QFIXEDSZ 4   /* #/bytes of fixed data in query */\n#define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */\n#define NS_INT16SZ 2\n#define NS_INADDRSZ 4\n#define NS_IN6ADDRSZ 16\n#define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */\n#define NS_DEFAULTPORT 53 /* For both TCP and UDP. */\n\ntypedef enum __ns_class {\n  ns_c_invalid = 0, /* Cookie. */\n  ns_c_in = 1,      /* Internet. */\n  ns_c_2 = 2,       /* unallocated/unsupported. */\n  ns_c_chaos = 3,   /* MIT Chaos-net. */\n  ns_c_hs = 4,      /* MIT Hesiod. */\n  /* Query class values which do not appear in resource records */\n  ns_c_none = 254, /* for prereq. sections in update requests */\n  ns_c_any = 255,  /* Wildcard match. */\n  ns_c_max = 65536\n} ns_class;\n\ntypedef enum __ns_type {\n  ns_t_invalid = 0,   /* Cookie. */\n  ns_t_a = 1,         /* Host address. */\n  ns_t_ns = 2,        /* Authoritative server. */\n  ns_t_md = 3,        /* Mail destination. */\n  ns_t_mf = 4,        /* Mail forwarder. */\n  ns_t_cname = 5,     /* Canonical name. */\n  ns_t_soa = 6,       /* Start of authority zone. */\n  ns_t_mb = 7,        /* Mailbox domain name. */\n  ns_t_mg = 8,        /* Mail group member. */\n  ns_t_mr = 9,        /* Mail rename name. */\n  ns_t_null = 10,     /* Null resource record. */\n  ns_t_wks = 11,      /* Well known service. */\n  ns_t_ptr = 12,      /* Domain name pointer. */\n  ns_t_hinfo = 13,    /* Host information. */\n  ns_t_minfo = 14,    /* Mailbox information. */\n  ns_t_mx = 15,       /* Mail routing information. */\n  ns_t_txt = 16,      /* Text strings. */\n  ns_t_rp = 17,       /* Responsible person. */\n  ns_t_afsdb = 18,    /* AFS cell database. */\n  ns_t_x25 = 19,      /* X_25 calling address. */\n  ns_t_isdn = 20,     /* ISDN calling address. */\n  ns_t_rt = 21,       /* Router. */\n  ns_t_nsap = 22,     /* NSAP address. */\n  ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */\n  ns_t_sig = 24,      /* Security signature. */\n  ns_t_key = 25,      /* Security key. */\n  ns_t_px = 26,       /* X.400 mail mapping. */\n  ns_t_gpos = 27,     /* Geographical position (withdrawn). */\n  ns_t_aaaa = 28,     /* Ip6 Address. */\n  ns_t_loc = 29,      /* Location Information. */\n  ns_t_nxt = 30,      /* Next domain (security). */\n  ns_t_eid = 31,      /* Endpoint identifier. */\n  ns_t_nimloc = 32,   /* Nimrod Locator. */\n  ns_t_srv = 33,      /* Server Selection. */\n  ns_t_atma = 34,     /* ATM Address */\n  ns_t_naptr = 35,    /* Naming Authority PoinTeR */\n  ns_t_kx = 36,       /* Key Exchange */\n  ns_t_cert = 37,     /* Certification record */\n  ns_t_a6 = 38,       /* IPv6 address (deprecates AAAA) */\n  ns_t_dname = 39,    /* Non-terminal DNAME (for IPv6) */\n  ns_t_sink = 40,     /* Kitchen sink (experimentatl) */\n  ns_t_opt = 41,      /* EDNS0 option (meta-RR) */\n  ns_t_apl = 42,      /* Address prefix list (RFC3123) */\n  ns_t_ds = 43,       /* Delegation Signer (RFC4034) */\n  ns_t_sshfp = 44,    /* SSH Key Fingerprint (RFC4255) */\n  ns_t_rrsig = 46,    /* Resource Record Signature (RFC4034) */\n  ns_t_nsec = 47,     /* Next Secure (RFC4034) */\n  ns_t_dnskey = 48,   /* DNS Public Key (RFC4034) */\n  ns_t_tkey = 249,    /* Transaction key */\n  ns_t_tsig = 250,    /* Transaction signature. */\n  ns_t_ixfr = 251,    /* Incremental zone transfer. */\n  ns_t_axfr = 252,    /* Transfer zone of authority. */\n  ns_t_mailb = 253,   /* Transfer mailbox records. */\n  ns_t_maila = 254,   /* Transfer mail agent records. */\n  ns_t_any = 255,     /* Wildcard match. */\n  ns_t_zxfr = 256,    /* BIND-specific, nonstandard. */\n  ns_t_max = 65536\n} ns_type;\n\ntypedef enum __ns_opcode {\n  ns_o_query = 0,  /* Standard query. */\n  ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */\n  ns_o_status = 2, /* Name server status query (unsupported). */\n                   /* Opcode 3 is undefined/reserved. */\n  ns_o_notify = 4, /* Zone change notification. */\n  ns_o_update = 5, /* Zone update message. */\n  ns_o_max = 6\n} ns_opcode;\n\ntypedef enum __ns_rcode {\n  ns_r_noerror = 0,  /* No error occurred. */\n  ns_r_formerr = 1,  /* Format error. */\n  ns_r_servfail = 2, /* Server failure. */\n  ns_r_nxdomain = 3, /* Name error. */\n  ns_r_notimpl = 4,  /* Unimplemented. */\n  ns_r_refused = 5,  /* Operation refused. */\n  /* these are for BIND_UPDATE */\n  ns_r_yxdomain = 6, /* Name exists */\n  ns_r_yxrrset = 7,  /* RRset exists */\n  ns_r_nxrrset = 8,  /* RRset does not exist */\n  ns_r_notauth = 9,  /* Not authoritative for zone */\n  ns_r_notzone = 10, /* Zone of record different from zone section */\n  ns_r_max = 11,\n  /* The following are TSIG extended errors */\n  ns_r_badsig = 16,\n  ns_r_badkey = 17,\n  ns_r_badtime = 18\n} ns_rcode;\n\n#endif /* HAVE_ARPA_NAMESER_H */\n\n#ifndef HAVE_ARPA_NAMESER_COMPAT_H\n\n#define PACKETSZ NS_PACKETSZ\n#define MAXDNAME NS_MAXDNAME\n#define MAXCDNAME NS_MAXCDNAME\n#define MAXLABEL NS_MAXLABEL\n#define HFIXEDSZ NS_HFIXEDSZ\n#define QFIXEDSZ NS_QFIXEDSZ\n#define RRFIXEDSZ NS_RRFIXEDSZ\n#define INDIR_MASK NS_CMPRSFLGS\n#define NAMESERVER_PORT NS_DEFAULTPORT\n\n#define QUERY ns_o_query\n\n#define SERVFAIL ns_r_servfail\n#define NOTIMP ns_r_notimpl\n#define REFUSED ns_r_refused\n#undef NOERROR /* it seems this is already defined in winerror.h */\n#define NOERROR ns_r_noerror\n#define FORMERR ns_r_formerr\n#define NXDOMAIN ns_r_nxdomain\n\n#define C_IN ns_c_in\n#define C_CHAOS ns_c_chaos\n#define C_HS ns_c_hs\n#define C_NONE ns_c_none\n#define C_ANY ns_c_any\n\n#define T_A ns_t_a\n#define T_NS ns_t_ns\n#define T_MD ns_t_md\n#define T_MF ns_t_mf\n#define T_CNAME ns_t_cname\n#define T_SOA ns_t_soa\n#define T_MB ns_t_mb\n#define T_MG ns_t_mg\n#define T_MR ns_t_mr\n#define T_NULL ns_t_null\n#define T_WKS ns_t_wks\n#define T_PTR ns_t_ptr\n#define T_HINFO ns_t_hinfo\n#define T_MINFO ns_t_minfo\n#define T_MX ns_t_mx\n#define T_TXT ns_t_txt\n#define T_RP ns_t_rp\n#define T_AFSDB ns_t_afsdb\n#define T_X25 ns_t_x25\n#define T_ISDN ns_t_isdn\n#define T_RT ns_t_rt\n#define T_NSAP ns_t_nsap\n#define T_NSAP_PTR ns_t_nsap_ptr\n#define T_SIG ns_t_sig\n#define T_KEY ns_t_key\n#define T_PX ns_t_px\n#define T_GPOS ns_t_gpos\n#define T_AAAA ns_t_aaaa\n#define T_LOC ns_t_loc\n#define T_NXT ns_t_nxt\n#define T_EID ns_t_eid\n#define T_NIMLOC ns_t_nimloc\n#define T_SRV ns_t_srv\n#define T_ATMA ns_t_atma\n#define T_NAPTR ns_t_naptr\n#define T_KX ns_t_kx\n#define T_CERT ns_t_cert\n#define T_A6 ns_t_a6\n#define T_DNAME ns_t_dname\n#define T_SINK ns_t_sink\n#define T_OPT ns_t_opt\n#define T_APL ns_t_apl\n#define T_DS ns_t_ds\n#define T_SSHFP ns_t_sshfp\n#define T_RRSIG ns_t_rrsig\n#define T_NSEC ns_t_nsec\n#define T_DNSKEY ns_t_dnskey\n#define T_TKEY ns_t_tkey\n#define T_TSIG ns_t_tsig\n#define T_IXFR ns_t_ixfr\n#define T_AXFR ns_t_axfr\n#define T_MAILB ns_t_mailb\n#define T_MAILA ns_t_maila\n#define T_ANY ns_t_any\n\n#endif /* HAVE_ARPA_NAMESER_COMPAT_H */\n\n#endif /* ARES_NAMESER_H */\n"
  },
  {
    "path": "collector/kernel/dns/dns.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/wire_message.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n#define DNS_NAME_MAX_LENGTH 256\n#define MAX_ENCODED_IP_ADDRS 16\n\n#define MAX_ENCODED_DNS_MESSAGE                                                                                                \\\n  (/* timestamp */ sizeof(u64) + /* jb message */ jb_ingest__dns_response__data_size +                                         \\\n   /* ip addrs */ (sizeof(u32) * MAX_ENCODED_IP_ADDRS) + /* DNS name */ DNS_NAME_MAX_LENGTH)\n\nint dns_name_length(const unsigned char *encoded, const unsigned char *abuf, int alen);\n\nvoid dns_expand_name(const unsigned char *encoded, const unsigned char *abuf, int alen, char *s, long *enclen);\n\nint dns_expand_name_maxlen(\n    const unsigned char *encoded, const unsigned char *abuf, int alen, char *s, long *enclen, int *expanded_len);\n\nint dns_parse_query(\n    const unsigned char *abuf,\n    int alen,\n    int *is_response,\n    uint16_t *type_out,\n    uint16_t *qid_out,\n    char *question_out,\n    int *question_len);\n\nint dns_parse_a_aaaa_reply(\n    const unsigned char *abuf,\n    int alen,\n    char *hostname_out,\n    int *hostname_len,\n    struct in_addr *in_addrs_out,\n    int *num_in_addrs,\n    struct in6_addr *in6_addrs_out,\n    int *num_in6_addrs);\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n"
  },
  {
    "path": "collector/kernel/dns_requests.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"spdlog/common.h\"\n#include <collector/kernel/dns_requests.h>\n#include <platform/platform.h>\n#include <util/log.h>\n\n/**\n * DNS requests key hash function\n *\n * @param[in] k The dns request key to hash\n * @return the hash of the dns request key\n */\nsize_t DnsRequests::dns_request_key_hash::operator()(const dns_request_key &k) const noexcept\n{\n  return std::hash<uint16_t>{}(k.qid) ^ std::hash<uint16_t>{}(k.type) ^ std::hash<std::string>{}(k.name);\n}\n\n/**\n * DNS requests key comparison function\n *\n * @param[in] k The first dns request key to compare\n * @param[in] k2 The second dns request key to compare\n * @return true if the keys are equal, false otherwise\n */\nbool DnsRequests::dns_request_key_equal_to::operator()(const dns_request_key &k, const dns_request_key &k2) const noexcept\n{\n  return k.qid == k2.qid && k.type == k2.type && k.name == k2.name;\n}\n\n/**\n * Add a DNS Request to the data structure\n *\n * @param[in] key Key of the DNS request\n * @param[in] value Value of the DNS request\n */\nvoid DnsRequests::add(const dns_request_key &key, const dns_request_value &value)\n{\n  auto iter = dns_requests_.insert(dns_requests_.end(), std::make_pair(key, value));\n  dns_requests_by_key_.insert(std::make_pair(key, iter));\n  dns_requests_by_sock_.insert(std::make_pair(value.sk, iter));\n}\n\n/**\n * Return a list of DNS Requests that correspond to a particular key\n *\n * @param[in] key Key of the DNS Request to look up\n * @param[out] out The list of matching dns request key/value pairs\n */\nvoid DnsRequests::lookup(const dns_request_key &key, std::list<Request> &out)\n{\n  auto key_er = dns_requests_by_key_.equal_range(key);\n  for (auto eriter = key_er.first; eriter != key_er.second; eriter++) {\n    out.push_back(eriter->second);\n  }\n}\n\n/**\n * Return a list of DNS Requests that are older than a particular timestamp\n *\n * @param[in] timestamp_ns The timestamp to get requests older than\n * @param[out] out The list of matching dns request key/value pairs\n */\n\nvoid DnsRequests::lookup_older_than(u64 timestamp_ns, std::list<DnsRequests::Request> &out)\n{\n  for (auto iter = dns_requests_.begin(); iter != dns_requests_.end(); iter++) {\n    if (iter->second.timestamp_ns >= timestamp_ns) {\n      break;\n    }\n\n    out.push_back(iter);\n  }\n}\n\n/**\n * Return a list of DNS Requests that match a particular socket\n *\n * @param[in] sk The kernel `struct sock*` pointer of the socket to look up\n * @param[out] out The list of matching dns request key/value pairs\n */\nvoid DnsRequests::lookup_socket(u64 sk, std::list<DnsRequests::Request> &out)\n{\n  auto sk_er = dns_requests_by_sock_.equal_range(sk);\n  for (auto eriter = sk_er.first; eriter != sk_er.second; eriter++) {\n    out.push_back(eriter->second);\n  }\n}\n\n/**\n * Removes a specific DNS Request\n *\n * @param[in] key The DNS Request to remove, as returned by lookup* functions\n */\nvoid DnsRequests::remove(const Request &req)\n{\n  auto key_er = dns_requests_by_key_.equal_range(req->first);\n  for (auto eriter = key_er.first; eriter != key_er.second; eriter++) {\n    if (eriter->second == req) {\n      dns_requests_by_key_.erase(eriter);\n      break;\n    }\n  }\n\n  auto sk_er = dns_requests_by_sock_.equal_range(req->second.sk);\n  for (auto skiter = sk_er.first; skiter != sk_er.second; skiter++) {\n    if (skiter->second == req) {\n      dns_requests_by_sock_.erase(skiter);\n      break;\n    }\n  }\n\n  dns_requests_.erase(req);\n}\n\n/**\n * Removes all matching DNS Requests for a particular key\n *\n * @param[in] key Key of the DNS requests to remove\n */\nvoid DnsRequests::remove_all_with_key(const dns_request_key &key)\n{\n  auto key_er = dns_requests_by_key_.equal_range(key);\n  for (auto eriter = key_er.first; eriter != key_er.second;) {\n    auto req = eriter->second;\n\n    auto sk_er = dns_requests_by_sock_.equal_range(req->second.sk);\n    for (auto skiter = sk_er.first; skiter != sk_er.second; skiter++) {\n      if (skiter->second == req) {\n        dns_requests_by_sock_.erase(skiter);\n        break;\n      }\n    }\n\n    dns_requests_.erase(req);\n\n    eriter = dns_requests_by_key_.erase(eriter);\n  }\n}\n"
  },
  {
    "path": "collector/kernel/dns_requests.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <list>\n#include <unordered_map>\n\n#include <platform/platform.h>\n\nclass DnsRequests {\n  // Type declarations\npublic:\n  struct dns_request_key {\n    u16 qid;          // transaction id\n    u16 type;         // query type\n    std::string name; // query data\n    bool is_rx;       // was this request 'sent (client)' or 'received (server)'\n  };\n\n  struct dns_request_value {\n    u64 timestamp_ns; // when the query was made according to bpf\n    u64 sk;           // socket that sent the dns request\n  };\n\nprotected:\n  struct dns_request_key_hash {\n    size_t operator()(const dns_request_key &k) const noexcept;\n  };\n  struct dns_request_key_equal_to {\n    bool operator()(const dns_request_key &k, const dns_request_key &k2) const noexcept;\n  };\n\n  typedef std::list<std::pair<dns_request_key, dns_request_value>> DnsRequestsList;\n  typedef std::unordered_multimap<dns_request_key, DnsRequestsList::iterator, dns_request_key_hash, dns_request_key_equal_to>\n      DnsRequestsByKeyMap;\n  typedef std::unordered_multimap<u64, DnsRequestsList::iterator> DnsRequestsBySockMap;\n\npublic:\n  typedef DnsRequestsList::iterator Request;\n\n  // Public interface\npublic:\n  void add(const dns_request_key &key, const dns_request_value &value);\n  void lookup(const dns_request_key &key, std::list<Request> &out);\n  void lookup_older_than(u64 timestamp_ns, std::list<Request> &out);\n  void lookup_socket(u64 sk, std::list<Request> &out);\n  void remove(const Request &req);\n  void remove_all_with_key(const dns_request_key &key);\n\n  // Protected member variables\nprotected:\n  DnsRequestsByKeyMap dns_requests_by_key_;\n  DnsRequestsList dns_requests_;\n  DnsRequestsBySockMap dns_requests_by_sock_;\n};\n"
  },
  {
    "path": "collector/kernel/entrypoint-kct.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\necho \"===================== /etc/os-release =====================\"\n[[ ! -e /etc/os-release ]] || cat /etc/os-release\necho \"========================= uname -a ========================\"\nuname -a\necho \"======================= environment =======================\"\nenv | sort\necho \"===========================================================\"\n\ninstall_dir=${EBPF_NET_INSTALL_DIR:-/srv}\n\ndata_dir=${EBPF_NET_DATA_DIR:-/var/run/ebpf_net}\ndump_dir=\"${data_dir}/dump\"\nmkdir -p \"${data_dir}\" \"${dump_dir}\"\n\nif ! mountpoint -q /sys; then\n  mount -t sysfs none /sys || echo \"Warning: Could not mount sysfs\"\nfi\n\necho \"launching kernel collector test ...\"\n\n# to run the collector under gdb, set `EBPF_NET_RUN_UNDER_GDB` to the flavor of gdb\n# you want (e.g.: `cgdb` or `gdb`) - this is intended for development purposes\nif [[ -n \"${EBPF_NET_RUN_UNDER_GDB}\" ]]; then\n  if [[ \"${#EBPF_NET_GDB_COMMANDS[@]}\" -lt 1 ]]; then\n    # default behavior is to run the agent, print a stack trace after it exits\n    # and exit gdb without confirmation\n    EBPF_NET_GDB_COMMANDS=( \\\n      'set pagination off'\n      'handle SIGPIPE nostop pass'\n      'handle SIGUSR1 nostop pass'\n      'handle SIGUSR2 nostop pass'\n      run\n      bt\n      'server q'\n    )\n  fi\n\n  GDB_ARGS=()\n  for gdb_cmd in \"${EBPF_NET_GDB_COMMANDS[@]}\"; do\n    GDB_ARGS+=(-ex \"${gdb_cmd}\")\n  done\n\n  (set -x; exec \"${EBPF_NET_RUN_UNDER_GDB}\" -q \"${GDB_ARGS[@]}\" \\\n    --args \"${install_dir}/kernel_collector_test\" \"$@\" \\\n  )\nelif [[ -n \"${EBPF_NET_RUN_UNDER_VALGRIND}\" ]]; then\n  # to run the collector under valgrind, set `EBPF_NET_RUN_UNDER_VALGRIND` to the options to pass to valgrind,\n  # including at minimum the tool you want, for example:\n  # \"--tool=memcheck\", or\n  # \"--tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes\", or\n  # \"--tool=massif --stacks=yes\"\n  # note: to get full symbols from valgrind also build the kernel-collector in debug mode\n\n  # shellcheck disable=SC2086\n  (set -x; exec /usr/bin/valgrind ${EBPF_NET_RUN_UNDER_VALGRIND} \"${install_dir}/kernel_collector_test\" \"$@\")\nelse\n  # Run the test binary and capture its exit code without inverting it.\n  (set -x; \"${install_dir}/kernel_collector_test\" \"$@\")\n  test_exit_code=$?\n  if [[ ${test_exit_code} -ne 0 ]]; then\n    echo \"kernel collector test FAILED\"\n    cp /srv/core-* /hostfs/data || true\n    if [[ -n \"${DELAY_EXIT_ON_FAILURE}\" ]]; then\n      echo \"DELAY_EXIT_ON_FAILURE is set, doing 'sleep inf'\"\n      sleep inf\n    fi\n  fi\nfi\n\nif [ -e /tmp/bpf-dump-file ]\nthen\n  \"${install_dir}/bpf_wire_to_json\" < /tmp/bpf-dump-file | jq . > /tmp/bpf-dump-file.json\nfi\n\nif [ -e /tmp/intake-dump-file ]\nthen\n  \"${install_dir}/intake_wire_to_json\" < /tmp/intake-dump-file | jq . > /tmp/intake-dump-file.json\nfi\n\n# Copy converted dumps to host-mounted data directory for artifact collection\nif [ -d /hostfs/data ]; then\n  if [ -e /tmp/bpf-dump-file.json ]; then\n    cp -f /tmp/bpf-dump-file.json /hostfs/data/ || true\n  fi\n  if [ -e /tmp/intake-dump-file.json ]; then\n    cp -f /tmp/intake-dump-file.json /hostfs/data/ || true\n    # Optional: emit a filtered file with only bpf_log messages from intake JSON\n    jq '.[] | select(.name==\"bpf_log\")' /tmp/intake-dump-file.json > /hostfs/data/bpf-logs.json 2>/dev/null || true\n  fi\nfi\n\nif [[ -n \"${DELAY_EXIT}\" ]]\nthen\n  echo \"DELAY_EXIT is set, doing 'sleep inf'\"\n  sleep inf\nfi\n\n# Propagate the test's exit code so the container exit reflects pass/fail.\nif [[ -n \"${test_exit_code:-}\" ]]; then\n  exit \"${test_exit_code}\"\nfi\n"
  },
  {
    "path": "collector/kernel/entrypoint.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/component.h>\n#include <collector/agent_log.h>\n#include <collector/constants.h>\n#include <collector/kernel/cgroup_handler.h>\n#include <collector/kernel/kernel_collector.h>\n#include <collector/kernel/troubleshooting.h>\n#include <common/cloud_platform.h>\n#include <common/kernel_headers_source.h>\n#include <common/linux_distro.h>\n#include <config/config_file.h>\n#include <platform/platform.h>\n#include <util/agent_id.h>\n#include <util/args_parser.h>\n#include <util/boot_time.h>\n#include <util/curl_engine.h>\n#include <util/debug.h>\n#include <util/environment_variables.h>\n#include <util/file_ops.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/nomad_metadata.h>\n#include <util/signal_handler.h>\n#include <util/system_ops.h>\n#include <util/utility.h>\n\n#include <config.h>\n\n#include <curlpp/cURLpp.hpp>\n\n#include <bpf/bpf.h>\n#include <dirent.h>\n#include <linux/bpf.h>\n#include <linux/limits.h> /* PATH_MAX*/\n#include <sys/mount.h>\n#include <sys/resource.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n#include <uv.h>\n\n#include <fstream>\n#include <regex>\n#include <sstream>\n\nstatic constexpr size_t AGENT_MAX_MEMORY = 300ULL * 1024 * 1024; /* 300 MiB */\n\n/* agent nice(2) value: -20 highest priority, 19 lowest, 0 default */\nstatic constexpr int AGENT_NICE_VALUE = 5;\n\nstatic constexpr auto DISABLE_HTTP_METRICS_VAR = \"EBPF_NET_DISABLE_HTTP_METRICS\";\n\nstatic constexpr auto NAMESPACE_OVERRIDE_VAR = \"EBPF_NET_AGENT_NAMESPACE\";\nstatic constexpr auto CLUSTER_OVERRIDE_VAR = \"EBPF_NET_AGENT_CLUSTER\";\nstatic constexpr auto SERVICE_OVERRIDE_VAR = \"EBPF_NET_AGENT_SERVICE\";\nstatic constexpr auto HOST_OVERRIDE_VAR = \"EBPF_NET_AGENT_HOST\";\nstatic constexpr auto ZONE_OVERRIDE_VAR = \"EBPF_NET_AGENT_ZONE\";\n\nstatic void refill_log_rate_limit_cb(uv_timer_t *timer)\n{\n  LOG::refill_rate_limit_budget(200);\n}\n\n/**\n * Check whether the binary has sufficient privileges and permissions.\n * If so, return.\n * If not, throw an exception with the appropriate errno.\n *\n * It can be difficult to directly determine privileges and permissions of a running binary.  For example, containers may run\n * with root as the default user, but unless the container is run with --privileged or equivalent then not all root priviliges\n * are available. As another example, within a container it may not be possible to determine SELinux status.  It may appear to\n * be disabled when queried in the container, but if enabled on the host it will prevent certain operations even if the\n * container is running with --privileged or equivalent.\n *\n * Instead, attempt to indirectly determine privileges and permissions by running bpf_map_create() as a test operation.  Note\n * that it is intentionally called with invalid parameters so a map isn't actually created.  Typical errors from this test\n * operation are:\n * EPERM: container is not running with --privileged or equivalent\n * EACCES: SELinux policy is preventing eBPF operations\n * EINVAL: privileges and permissions are sufficient - the test operation made it to where the invalid parameters were detected\n */\nvoid check_permissions()\n{\n  int fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 0, 0, 0, NULL);\n\n  if (fd == -1) {\n    switch (errno) {\n    case EPERM:\n    case EACCES: {\n      std::string failstr = fmt::format(\"Test bpf_map_create() operation failed with errno {}\", errno);\n      throw std::system_error(errno, std::generic_category(), failstr);\n      break;\n    }\n    case EINVAL:\n      return;\n      break;\n    default:\n      LOG::warn(\"Unexpected error checking for sufficient privileges and permissions errno {}: {}\", errno, strerror(errno));\n      return;\n      break;\n    }\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\nvoid mount_debugfs_if_required()\n{\n  struct stat stat_buf = {0};\n\n  int ret = stat(\"/sys/kernel/debug/tracing\", &stat_buf);\n  if (ret != 0) {\n    if (errno == ENOENT) {\n      /* directory doesn't exist, try to mount */\n      ret = mount(\"debugfs\", \"/sys/kernel/debug/\", \"debugfs\", 0, \"\");\n      if (ret != 0) {\n        throw std::system_error(errno, std::generic_category(), \"could not mount debugfs on /sys/kernel/debug\");\n      }\n    }\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Disables stdout and/or stderr output if requested by the user\n * @param disable_stdout: should we disable stdout\n * @param disable_stderr: should we disable stderr\n *\n * @return 0 on success, non-zero on error (returns the value of errno)\n */\nint control_stdout_stderr(bool disable_stdout, bool disable_stderr)\n{\n  // if user didn't ask to disable, we're done\n  if ((disable_stdout == false) && (disable_stderr == false))\n    return 0;\n\n  // open /dev/null\n  int null_fd = open(\"/dev/null\", O_WRONLY);\n  if (null_fd == -1)\n    return errno;\n\n  if (disable_stdout) {\n    int res = dup2(null_fd, STDOUT_FILENO);\n    if (res == -1) {\n      int errno_copy = errno; // in case close changes errno\n      close(null_fd);\n      return errno_copy;\n    }\n  }\n\n  if (disable_stderr) {\n    int res = dup2(null_fd, STDERR_FILENO);\n    if (res == -1) {\n      int errno_copy = errno; // in case close changes errno\n      close(null_fd);\n      return errno_copy;\n    }\n  }\n\n  // free the file descriptor we dup'd\n  int res = close(null_fd);\n  if (res == -1)\n    return errno;\n\n  return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\nvoid set_resource_limits()\n{\n  struct rlimit l = {};\n  l.rlim_max = AGENT_MAX_MEMORY;\n  l.rlim_cur = l.rlim_max;\n  int res = setrlimit(RLIMIT_DATA, &l);\n  if (res != 0)\n    LOG::warn(\"Could not set agent memory limit\");\n\n  res = nice(AGENT_NICE_VALUE);\n  if (res == -1)\n    LOG::warn(\"Could not set agent nice(2) priority\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\nvoid get_uname(struct utsname &unamebuf)\n{\n  if (uname(&unamebuf)) {\n    throw std::runtime_error(\"Failed to get system uname\");\n  }\n\n  LOG::info(\n      \"Running on:\\n\"\n      \"   sysname: {}\\n\"\n      \"  nodename: {}\\n\"\n      \"   release: {}\\n\"\n      \"   version: {}\\n\"\n      \"   machine: {}\",\n      unamebuf.sysname,\n      unamebuf.nodename,\n      unamebuf.release,\n      unamebuf.version,\n      unamebuf.machine);\n}\n\nvoid verify_kernel_blacklist(bool override, struct utsname &unamebuf)\n{\n  bool kernel_fail = false;\n  struct blacklist_match_line {\n    const char *sysname;\n    const char *nodename;\n    const char *release;\n    const char *version;\n    const char *machine;\n  };\n  blacklist_match_line failed_line;\n  static blacklist_match_line kernel_blacklist[] = {\n#include \"kernel_blacklist.h\"\n  };\n  int pat_num = 0;\n  for (auto pat : kernel_blacklist) {\n    bool match = true;\n    if (match && pat.sysname && !std::regex_match(unamebuf.sysname, std::regex(pat.sysname))) {\n      match = false;\n    }\n    if (match && pat.nodename && !std::regex_match(unamebuf.nodename, std::regex(pat.nodename))) {\n      match = false;\n    }\n    if (match && pat.release && !std::regex_match(unamebuf.release, std::regex(pat.release))) {\n      match = false;\n    }\n    if (match && pat.version && !std::regex_match(unamebuf.version, std::regex(pat.version))) {\n      match = false;\n    }\n    if (match && pat.machine && !std::regex_match(unamebuf.machine, std::regex(pat.machine))) {\n      match = false;\n    }\n    if (match) {\n      kernel_fail = true;\n      failed_line = pat;\n      break;\n    }\n    pat_num++;\n  }\n  if (kernel_fail) {\n    std::string failstr = fmt::format(\n        \"kernel blacklist check: pattern #{}:\"\n        \"   sysname: {}\\n\"\n        \"  nodename: {}\\n\"\n        \"   release: {}\\n\"\n        \"   version: {}\\n\"\n        \"   machine: {}\",\n        pat_num,\n        failed_line.sysname ? failed_line.sysname : \"NULL\",\n        failed_line.nodename ? failed_line.nodename : \"NULL\",\n        failed_line.release ? failed_line.release : \"NULL\",\n        failed_line.version ? failed_line.version : \"NULL\",\n        failed_line.machine ? failed_line.machine : \"NULL\");\n\n    if (!override) {\n      LOG::critical(\"Failed {}\", failstr);\n      exit(-1);\n    } else {\n      LOG::info(\"Overriding {}\", failstr);\n    }\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\nstatic int kernel_collector_run(int argc, char **argv)\n{\n  /* Initialize libuv loop */\n  uv_loop_t loop;\n  int res = uv_loop_init(&loop);\n  if (res != 0) {\n    throw std::runtime_error(\"uv_loop_init failed\");\n  }\n\n  // args parsing\n\n  cli::ArgsParser parser(\"Kernel Collector.\");\n  args::HelpFlag help(*parser, \"help\", \"Display this help menu\", {'h', \"help\"});\n  args::ValueFlag<u64> filter_ns(*parser, \"nanoseconds\", \"Gap between subsequent reports\", {\"filter-ns\"}, 10 * 1000 * 1000ull);\n  args::ValueFlag<u64> socket_stats_interval_sec(\n      *parser, \"seconds\", \"Interval between sending socket stats\", {\"socket-stats-interval-sec\"}, 10);\n\n  args::ValueFlag<u64> metadata_timeout_us(\n      *parser,\n      \"microseconds\",\n      \"Microseconds to wait for cloud instance metadata\",\n      {\"cloud-metadata-timeout-ms\"},\n      std::chrono::microseconds(1s).count());\n  args::ValueFlag<std::string> conf_file(*parser, \"config_file\", \"The location of the custom config file\", {\"config-file\"}, \"\");\n  args::Flag no_stdout(*parser, \"no_stdout\", \"Disable stdout output\", {\"no-stdout\"});\n  args::Flag no_stderr(*parser, \"no_stderr\", \"Disable stderr output\", {\"no-stderr\"});\n  args::Flag override_kernel_blacklist(\n      *parser,\n      \"override_kernel_blacklist\",\n      \"Override kernel blacklist (use at your own risk, can result in kernel panic)\",\n      {\"override-kernel-blacklist\"});\n\n  /* crash reporting */\n  args::Flag disable_log_rate_limit(\n      *parser, \"disable_log_rate_limit\", \"Disable rate limit the logging\", {\"disable-log-rate-limit\"});\n  args::ValueFlag<std::string> docker_ns_label(\n      *parser, \"label\", \"Docker label to be used as namespace\", {\"docker-ns-label\"}, \"\");\n\n  auto disable_http_metrics =\n      parser.add_env_flag(\"disable-http-metrics\", \"Disable collection of HTTP metrics\", DISABLE_HTTP_METRICS_VAR, false);\n\n  args::Flag enable_userland_tcp_flag(\n      *parser, \"userland_tcp\", \"Enable userland tcp processing (experimental)\", {\"enable-userland-tcp\"});\n\n  auto force_docker_metadata = parser.add_flag(\"force-docker-metadata\", \"Forces the use of docker metadata\");\n  auto disable_nomad_metadata = parser.add_flag(\"disable-nomad-metadata\", \"Disables detection and use of Nomad metadata\");\n\n  args::ValueFlag<std::string> docker_metadata_dump_dir(\n      *parser,\n      \"docker-metadata-dump-dir\",\n      \"If set, dump docker metadata to this directory (for debug purposes)\",\n      {\"docker-metadata-dump-dir\"});\n\n  args::ValueFlag<std::string> bpf_dump_file(\n      *parser, \"bpf-dump-file\", \"If set, dumps the stream of eBPF messages to the file given by this flag\", {\"bpf-dump-file\"});\n\n#ifdef CONFIGURABLE_BPF\n  args::ValueFlag<std::string> bpf_file(*parser, \"bpf_file\", \"File containing bpf code\", {\"bpf\"}, \"\");\n#endif // CONFIGURABLE_BPF\n\n#ifndef NDEBUG\n  auto schedule_bpf_lost_samples = parser.add_arg<std::chrono::seconds::rep>(\n      \"schedule-bpf-lost-samples\",\n      \"internal development - will continuously, at the interval in seconds provided, simulate lost BPF samples (PERF_RECORD_LOST) in BufferedPoller to test KernelCollector restarts via that code path\");\n#endif\n\n  parser.new_handler<LogWhitelistHandler<AgentLogKind>>(\"agent-log\");\n  parser.new_handler<LogWhitelistHandler<channel::Component>>(\"channel\");\n  parser.new_handler<LogWhitelistHandler<CloudPlatform>>(\"cloud-platform\");\n  parser.new_handler<LogWhitelistHandler<Utility>>(\"utility\");\n\n  auto &intake_config_handler = parser.new_handler<config::IntakeConfig::ArgsHandler>();\n\n  SignalManager signal_manager(loop, \"kernel-collector\");\n\n  if (auto result = parser.process(argc, argv); !result.has_value()) {\n    return result.error();\n  }\n\n  // Initialize minimal signal handler behavior (ignore SIGPIPE, disable core dumps)\n  signal_manager.handle();\n\n  /*\n   * Set docker nameservice label from commandline flags if provided;\n   * fallback to $DOCKER_NS_LABEL environment variable if that exists.\n   */\n  if (docker_ns_label.Matched()) {\n    CgroupHandler::docker_ns_label_field = docker_ns_label.Get();\n  } else {\n    char const *docker_ns_label_env = std::getenv(\"DOCKER_NS_LABEL\");\n    if (docker_ns_label_env != nullptr) {\n      CgroupHandler::docker_ns_label_field = std::string(docker_ns_label_env);\n    }\n  }\n\n  /* set agent resource limits */\n#if USE_ADDRESS_SANITIZER == 0\n  set_resource_limits();\n#endif\n\n  /* disable stdout/stderr if requested */\n  {\n    int res = control_stdout_stderr(no_stdout, no_stderr);\n    if (res != 0) {\n      LOG::critical(\"Could not squelch stdout={} stderr={}: error={}\", no_stdout.Get(), no_stderr.Get(), res);\n      sleep(5); // make sure we don't spam too much\n      exit(-1);\n    }\n  }\n\n  auto agent_id = gen_agent_id();\n\n  /* log agent version */\n  std::string version_text =\n      fmt::format(\"{}.{}.{}\", versions::release.major(), versions::release.minor(), versions::release.patch());\n  if (auto signature = versions::release.signature(); !signature.empty()) {\n    version_text += fmt::format(\"-{}\", signature);\n  }\n  LOG::info(\"Starting Kernel Collector version {} ({})\", version_text, release_mode_string);\n  LOG::info(\"Kernel Collector agent ID is {}\", agent_id);\n\n  /* get kernel version */\n  struct utsname unamebuf;\n  get_uname(unamebuf);\n\n  /* check kernel blacklist */\n  verify_kernel_blacklist(override_kernel_blacklist.Matched(), unamebuf);\n\n  /*\n   *  Set http_metrics flag if environment variable specified as well\n   */\n  bool const enable_http_metrics = !disable_http_metrics;\n  static constexpr char const *enabled_disabled[] = {\"Disabled\", \"Enabled\"};\n  LOG::info(\"HTTP Metrics: {}\", enabled_disabled[enable_http_metrics]);\n\n  LOG::info(\"Socket stats interval in seconds: {}\", socket_stats_interval_sec.Get());\n\n  /* acknowledge userland tcp */\n  bool const enable_userland_tcp = enable_userland_tcp_flag.Matched();\n  LOG::info(\"Userland TCP: {}\", enabled_disabled[enable_userland_tcp]);\n\n  /* Initialize curl */\n  curlpp::initialize();\n\n  std::chrono::microseconds const metadata_timeout(metadata_timeout_us.Get());\n\n  /* AWS metadata */\n  LOG::trace_in(CloudPlatform::aws, \"--- resolving AWS metadata ---\");\n  auto const aws_metadata = AwsMetadata::fetch(metadata_timeout);\n  if (aws_metadata) {\n    aws_metadata->print_instance_metadata();\n    aws_metadata->print_interfaces();\n  } else {\n    LOG::debug(\"Unable to fetch AWS metadata: {}\", aws_metadata.error().what());\n  }\n\n  /* GCP metadata */\n  LOG::trace_in(CloudPlatform::gcp, \"--- resolving GCP metadata ---\");\n  auto const gcp_metadata = GcpInstanceMetadata::fetch(metadata_timeout);\n  if (gcp_metadata) {\n    gcp_metadata->print();\n  } else {\n    LOG::debug(\"Unable to fetch GCP metadata: {}\", gcp_metadata.error().what());\n  }\n\n  /* read label overrides from environment if present */\n  auto override_agent_namespace = std::string{try_get_env_var(NAMESPACE_OVERRIDE_VAR)};\n  auto override_agent_cluster = std::string{try_get_env_var(CLUSTER_OVERRIDE_VAR)};\n  auto override_agent_zone = std::string{try_get_env_var(ZONE_OVERRIDE_VAR)};\n  auto override_agent_service = std::string{try_get_env_var(SERVICE_OVERRIDE_VAR)};\n  auto override_agent_host = std::string{try_get_env_var(HOST_OVERRIDE_VAR)};\n\n  /* Nomad metadata */\n  if (!disable_nomad_metadata) {\n    LOG::trace(\"--- resolving Nomad metadata ---\");\n    if (NomadMetadata const nomad_metadata; nomad_metadata) {\n      nomad_metadata.print();\n\n      // respect explicit overrides from environment variables\n\n      // TODO: what's the equivalent of a namespace in Nomad?\n\n      if (override_agent_cluster.empty() && !nomad_metadata.ns().empty()) {\n        override_agent_cluster = std::string(nomad_metadata.ns());\n      }\n\n      if (override_agent_service.empty() && !nomad_metadata.task_name().empty()) {\n        override_agent_service = std::string(nomad_metadata.task_name());\n      }\n    } else {\n      LOG::debug(\"Unable to fetch Nomad metadata - environment variables not found\");\n    }\n  } else {\n    LOG::trace(\"--- skipping Nomad metadata resolution ---\");\n  }\n\n  // resolve hostname\n  std::string const hostname = get_host_name(MAX_HOSTNAME_LENGTH).recover([&](auto &error) {\n    LOG::error(\"Unable to retrieve host information from uname: {}\", error);\n    return aws_metadata->id().valid() ? std::string(aws_metadata->id().value()) : \"(unknown)\";\n  });\n  LOG::info(\"Kernel Collector version {} ({}) started on host {}\", version_text, release_mode_string, hostname);\n\n  config::ConfigFile configuration_data(config::ConfigFile::YamlFormat(), conf_file.Get());\n\n  /* use label overrides if present */\n  if (!override_agent_namespace.empty()) {\n    LOG::debug(\"overriding agent namespace with '{}'\", override_agent_namespace);\n    configuration_data.labels()[\"namespace\"] = override_agent_namespace;\n  }\n  if (!override_agent_cluster.empty()) {\n    LOG::debug(\"overriding agent cluster with '{}'\", override_agent_cluster);\n    configuration_data.labels()[\"cluster\"] = override_agent_cluster;\n  }\n  if (!override_agent_zone.empty()) {\n    LOG::debug(\"overriding agent zone with '{}'\", override_agent_zone);\n    configuration_data.labels()[\"zone\"] = override_agent_zone;\n  }\n  if (!override_agent_service.empty()) {\n    LOG::debug(\"overriding agent service with '{}'\", override_agent_service);\n    configuration_data.labels()[\"service\"] = override_agent_service;\n  }\n  if (!override_agent_host.empty()) {\n    LOG::debug(\"overriding agent host with '{}'\", override_agent_host);\n    configuration_data.labels()[\"host\"] = override_agent_host;\n  }\n\n  HostInfo host_info{\n      .os = OperatingSystem::Linux,\n      .os_flavor = integer_value(LinuxDistro::unknown),\n      .os_version = gcp_metadata ? gcp_metadata->image() : \"unknown\",\n      .kernel_headers_source = KernelHeadersSource::libbpf,\n      .kernel_version = unamebuf.release,\n      .hostname = hostname};\n\n  try {\n    check_permissions();\n\n    /* mount debugfs if it is not mounted */\n    mount_debugfs_if_required();\n\n    // Create BPF configuration with all required parameters\n    u64 boot_time_adjustment = get_boot_time();\n\n    BpfConfiguration bpf_config{\n        .boot_time_adjustment = boot_time_adjustment,\n        .filter_ns = args::get(filter_ns),\n        .enable_tcp_data_stream = enable_userland_tcp};\n\n    uv_timer_t refill_log_rate_limit_timer;\n    if (!disable_log_rate_limit) {\n      LOG::enable_rate_limit(5000);\n      res = uv_timer_init(&loop, &refill_log_rate_limit_timer);\n      if (res != 0) {\n        throw std::runtime_error(\"Cannot init rate limit timer.\");\n      }\n\n      res = uv_timer_start(\n          &refill_log_rate_limit_timer,\n          refill_log_rate_limit_cb,\n          /*1s*/ 1000,\n          /*1s*/ 1000);\n      if (res != 0) {\n        throw std::runtime_error(\"Cannot start rate limit timer.\");\n      }\n    }\n\n    /* initialize curl engine */\n    auto curl_engine = CurlEngine::create(&loop);\n\n    // Load the intake configuration from the configuration file and from command-line args.\n    config::IntakeConfig intake_config = configuration_data.intake_config();\n    intake_config_handler.read_config(intake_config);\n\n    // Initialize our kernel telemetry collector\n    KernelCollector kernel_collector{\n        bpf_config,\n        intake_config,\n        aws_metadata.try_value(),\n        gcp_metadata.try_value(),\n        configuration_data.labels(),\n        loop,\n        *curl_engine,\n        enable_http_metrics,\n        socket_stats_interval_sec.Get(),\n        CgroupHandler::CgroupSettings{\n            .force_docker_metadata = *force_docker_metadata,\n            .docker_metadata_dump_dir = docker_metadata_dump_dir ? std::optional(docker_metadata_dump_dir.Get()) : std::nullopt,\n        },\n        bpf_dump_file.Get(),\n        host_info};\n\n    signal_manager.handle_signals({SIGINT, SIGTERM}, std::bind(&KernelCollector::on_close, &kernel_collector));\n\n#ifndef NDEBUG\n    std::unique_ptr<scheduling::Timer> debug_bpf_lost_samples_timer;\n    if (schedule_bpf_lost_samples.given() && *schedule_bpf_lost_samples > 0) {\n      std::chrono::seconds const timeout(*schedule_bpf_lost_samples);\n\n      auto schedule_bpf_lost_samples = [&debug_bpf_lost_samples_timer, timeout]() {\n        if (auto const result = debug_bpf_lost_samples_timer->defer(timeout)) {\n          LOG::info(\"successfully scheduled inject_bpf_lost_samples() {} from now\", timeout);\n        } else {\n          LOG::error(\"failed to schedule inject_bpf_lost_samples() {} from now: {}\", timeout, result.error());\n        }\n      };\n\n      auto inject_bpf_lost_samples = [&kernel_collector, schedule_bpf_lost_samples]() {\n        kernel_collector.debug_bpf_lost_samples();\n        schedule_bpf_lost_samples(); // reschedule another callback to inject bpf lost samples\n      };\n\n      debug_bpf_lost_samples_timer = std::make_unique<scheduling::Timer>(loop, inject_bpf_lost_samples);\n      schedule_bpf_lost_samples(); // schedule the first callback to inject bpf lost samples\n    }\n#endif\n\n    LOG::debug(\"starting event loop...\");\n    uv_run(&loop, UV_RUN_DEFAULT);\n  } catch (std::system_error &e) {\n    switch (e.code().value()) {\n    case EPERM:\n      print_troubleshooting_message_and_exit(host_info, TroubleshootItem::operation_not_permitted, e);\n      break;\n    case EACCES:\n      print_troubleshooting_message_and_exit(host_info, TroubleshootItem::permission_denied, e);\n      break;\n    default:\n      print_troubleshooting_message_and_exit(host_info, TroubleshootItem::unexpected_exception, e);\n      break;\n    }\n    return 1;\n  } catch (std::exception &e) {\n    print_troubleshooting_message_and_exit(host_info, TroubleshootItem::unexpected_exception, e);\n    return 1;\n  }\n\n  return 0;\n}\n\nextern \"C\" int otn_kernel_collector_main(int argc, const char **argv)\n{\n  return kernel_collector_run(argc, const_cast<char **>(argv));\n}\n"
  },
  {
    "path": "collector/kernel/entrypoint.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\n# shellcheck disable=SC1091\n[[ ! -e ./debug-info.conf ]] || source ./debug-info.conf\n\nif [[ \"${EBPF_NET_DEBUG_MODE}\" == true ]]; then\n  echo \"===================== /etc/os-release =====================\"\n  [[ ! -e /etc/os-release ]] || cat /etc/os-release\n  echo \"========================= uname -a ========================\"\n  uname -a\n  echo \"======================= environment =======================\"\n  env | sort\n  echo \"===========================================================\"\nfi\n\ninstall_dir=${EBPF_NET_INSTALL_DIR:-/srv}\n\ndata_dir=${EBPF_NET_DATA_DIR:-/var/run/ebpf_net}\ndump_dir=\"${data_dir}/dump\"\nmkdir -p \"${data_dir}\" \"${dump_dir}\"\n\n# With libbpf, kernel headers are no longer needed\n\nif ! mountpoint -q /sys; then\n  mount -t sysfs none /sys || echo \"Warning: Could not mount sysfs\"\nfi\n\ncmd_args=()\n\necho \"launching kernel collector...\"\n# on Debug (non-production) images, devs can run in local mode by setting\n# `EBPF_NET_RUN_LOCAL` to non-empty.\nif [[ -n \"${EBPF_NET_RUN_LOCAL}\" ]]; then\n  # shellcheck disable=SC1091\n  source /srv/local.sh\n  cmd_args+=(\"${local_cmd_args[@]}\")\nfi\n\n# to run the collector under gdb, set `EBPF_NET_RUN_UNDER_GDB` to the flavor of gdb\n# you want (e.g.: `cgdb` or `gdb`) - this is intended for development purposes\nif [[ -n \"${EBPF_NET_RUN_UNDER_GDB}\" ]]; then\n  apt-get update -y\n  apt-get install -y --no-install-recommends \"${EBPF_NET_RUN_UNDER_GDB}\"\n\n  if [[ \"${#EBPF_NET_GDB_COMMANDS[@]}\" -lt 1 ]]; then\n    # default behavior is to run the agent, print a stack trace after it exits\n    # and exit gdb without confirmation\n    EBPF_NET_GDB_COMMANDS=( \\\n      'set pagination off'\n      'handle SIGPIPE nostop pass'\n      'handle SIGUSR1 nostop pass'\n      'handle SIGUSR2 nostop pass'\n      run\n      bt\n      'server q'\n    )\n  fi\n\n  GDB_ARGS=()\n  for gdb_cmd in \"${EBPF_NET_GDB_COMMANDS[@]}\"; do\n    GDB_ARGS+=(-ex \"${gdb_cmd}\")\n  done\n\n  (set -x; exec \"${EBPF_NET_RUN_UNDER_GDB}\" -q \"${GDB_ARGS[@]}\" \\\n    --args \"${install_dir}/kernel-collector\" \"${cmd_args[@]}\" \"$@\" \\\n  )\nelif [[ -n \"${EBPF_NET_RUN_UNDER_VALGRIND}\" ]]; then\n  # to run the collector under valgrind, set `EBPF_NET_RUN_UNDER_VALGRIND` to the options to pass to valgrind,\n  # including at minimum the tool you want, for example:\n  # \"--tool=memcheck\", or\n  # \"--tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes\", or\n  # \"--tool=massif --stacks=yes\"\n  # note: to get full symbols from valgrind also build the kernel-collector in debug mode\n  apt update -y\n  apt install -y valgrind\n\n  # shellcheck disable=SC2086\n  (set -x; exec /usr/bin/valgrind ${EBPF_NET_RUN_UNDER_VALGRIND} \"${install_dir}/kernel-collector\" \"${cmd_args[@]}\" \"$@\")\nelse\n  (set -x; exec \"${install_dir}/kernel-collector\" \"${cmd_args[@]}\" \"$@\")\nfi\n"
  },
  {
    "path": "collector/kernel/fd_reader.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/fd_reader.h>\n#include <cstdio>\n#include <cstring>\n#include <iostream>\n#include <string>\n#include <unistd.h>\n\nFDReader::FDReader(int pid)\n    : pid_(pid), task_tid_(-1), fd_dir_(NULL), task_dir_(NULL), task_fd_dir_(NULL), fd_dir_ent_(NULL), task_dir_ent_(NULL)\n{}\n\nFDReader::~FDReader()\n{\n  // XXX: may want to add checks for the return value of closedir\n  if (task_dir_)\n    closedir(task_dir_);\n  if (fd_dir_)\n    closedir(fd_dir_);\n}\n\nint FDReader::get_inode()\n{\n  // check if this entry is a fd\n  int fd;\n  if (sscanf(fd_dir_ent_->d_name, \"%d\", &fd) != 1)\n    return -1; // exit if the current fd_dir_ent_ is either \".\" or \"..\"\n\n  // create the link to this fd\n  char link[64];\n  if (snprintf(link, sizeof(link), \"/proc/%d/fd/%d\", pid_, fd) < 0)\n    return -1;\n\n  // read the link\n  char link_content[20];\n  int info_len = readlink(link, link_content, sizeof(link_content) - 1);\n  if (info_len == -1)\n    return -1; // exit if there was an error reading the file\n  link_content[info_len] = '\\0';\n\n  // check whether this was a socket\n  if (strncmp(link_content, \"socket:[\", strlen(\"socket:[\")))\n    return -1; // strncmp(a,b) == 0 when a == b\n\n  // get the inode number\n  int ino;\n  if (sscanf(link_content, \"socket:[%u]\", &ino) != 1)\n    return -1; // sscanf returns the number of items it matched\n  return ino;\n}\n\nint FDReader::next_task()\n{\n  int tid = -1;\n  while (tid == -1) {\n    task_dir_ent_ = readdir(task_dir_);\n    if (task_dir_ent_ == 0)\n      return -1; // there are no more entries left\n    // check that this dir was not \".\" or \"..\"\n    int temp; // XXX: not sure that sscanf won't overwrite a value\n    if (sscanf(task_dir_ent_->d_name, \"%d\", &temp) != 1) {\n      continue;\n    }\n    tid = temp;\n  }\n  task_tid_ = tid;\n  return 0;\n}\n\nint FDReader::next_fd()\n{\n  int fd = -1;\n  while (fd == -1) {\n    fd_dir_ent_ = readdir(fd_dir_);\n    if (fd_dir_ent_ == 0)\n      return -1;\n    int temp; // XXX: not sure that sscanf won't overwrite a value\n    if (sscanf(fd_dir_ent_->d_name, \"%d\", &temp) != 1) {\n      continue;\n    }\n    fd = temp;\n  }\n  return 0;\n}\n\nint FDReader::open_fd_dir()\n{\n  // create the link to the fd_dir_\n  char fd_dir_name[64];\n  if (snprintf(fd_dir_name, sizeof(fd_dir_name), \"/proc/%d/fd\", pid_) < 0)\n    return -1;\n\n  // open up the task_dir_\n  fd_dir_ = opendir(fd_dir_name);\n  if (!fd_dir_)\n    return -1;\n\n  return 0;\n}\n\nint FDReader::open_task_dir()\n{\n  // create the link to the task_dir_\n  char task_dir_name[64];\n  if (snprintf(task_dir_name, sizeof(task_dir_name), \"/proc/%d/task\", pid_) < 0)\n    return -1;\n\n  // open up the task_dir_\n  task_dir_ = opendir(task_dir_name);\n  if (!task_dir_)\n    return -1;\n\n  return 0;\n}\n\nint FDReader::open_task_comm()\n{\n  // create the link to the task comm\n  char link[64];\n  if (snprintf(link, sizeof(link), \"/proc/%d/task/%d/comm\", pid_, task_tid_) < 0)\n    return -1;\n\n  // read the link\n  char link_content[16];\n  int info_len = readlink(link, link_content, sizeof(link_content) - 1);\n  if (info_len == -1)\n    return -1; // exit if there was an error reading the file\n  link_content[info_len] = '\\0';\n\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/fd_reader.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <dirent.h>\n\n/**\n * Does two things:\n * 1. Reads over the task directory of a given pid\n *    i.e. /proc/[pid]/task\n * 2. Reads over the entries in the fd directory of a given pid\n *    i.e. /proc/[pid]/fd\n * We report the inode for entries in the fd directory that are sockets\n */\nclass FDReader {\npublic:\n  /**\n   * c'tor\n   */\n  FDReader(int pid);\n\n  /**\n   * d'tor\n   */\n  ~FDReader();\n\n  /**\n   * Returns the inode number if the fd is for a socket\n   * Returns -1 if the this fd was not a socket\n   */\n  int get_inode();\n\n  /**\n   * Read task_dir_. Returns 0 if a task was found and -1 otherwise.\n   * i.e. skips over non-task entries until we find a task or run out of entries\n   * Sets the task_dir_ent_ and task_tid_ if we do find an entry\n   *\n   */\n  int next_task();\n\n  /**\n   * f_dir_. Returns 0 if an entry was found and -1 otherwise.\n   * i.e. skips over non-task entries until we find a task or run out of entries\n   * Sets the fd_dir_ent_ if we do find an entry\n   */\n  int next_fd();\n\n  /**\n   * Opens the fd directory specified by pid_.\n   * Returns 0 on success, -1 if failed.\n   */\n  int open_fd_dir();\n\n  /**\n   * Opens the task directory specified by pid_.\n   * Returns 0 on success, -1 if failed.\n   */\n  int open_task_dir();\n\n  /**\n   * Opens the comm file specified by pid_ and tid_.\n   * Returns 0 on success, -1 if failed.\n   */\n  int open_task_comm();\n\nprivate:\n  int pid_;\n  int task_tid_;\n  DIR *fd_dir_;      // /proc/[pid]/fd\n  DIR *task_dir_;    // /proc/[pid]/task\n  DIR *task_fd_dir_; // /proc/[pid]/task/[tid]/fd\n  dirent *fd_dir_ent_;\n  dirent *task_dir_ent_;\n};\n"
  },
  {
    "path": "collector/kernel/hostport_tuple.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/lookup3.h>\n\n#include <functional>\n\n// Struct represents the 4-tuple of a connection\n// NOTE: when used as a key, src_port and dst_port are in network byte order\n// NOTE: hasher relies on the alignment of variables\nstruct hostport_tuple {\n  u32 src_ip;\n  u32 dst_ip;\n  u16 src_port;\n  u16 dst_port;\n  u32 proto;\n\n  bool operator==(const hostport_tuple &rhs) const\n  {\n    return (this->src_ip == rhs.src_ip) && (this->dst_ip == rhs.dst_ip) && (this->src_port == rhs.src_port) &&\n           (this->dst_port == rhs.dst_port) && (this->proto == rhs.proto);\n  }\n\n  hostport_tuple reversed() const { return {dst_ip, src_ip, dst_port, src_port, proto}; }\n};\n\nnamespace std {\ntemplate <> struct hash<hostport_tuple> {\n  size_t operator()(const hostport_tuple &t) const noexcept\n  {\n    return (std::size_t)lookup3_hashword((uint32_t *)&t, 4, 0x9e3779b9);\n  }\n};\n} // namespace std\n"
  },
  {
    "path": "collector/kernel/kernel_blacklist.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// Regexp patterns for kernel blacklist\n// failures must match all specified uname components\n// components specified as NULL are not matched\n\n// Pattern specification:\n// { sysname, nodename, release, version, machine }\n\n// minikube 4.15.0\n{\"Linux\", \"minikube\", R\"(^4\\.15\\.0.*)\", NULL, NULL},\n\n    // Linux 4.19.17 from Ubuntu mainline repo\n    {\"Linux\", NULL, R\"(^4\\.19\\.57.*)\", NULL, NULL},\n\n// Linux 5.1.16 from Ubuntu mainline repo\n{\n  \"Linux\", NULL, R\"(^5\\.1\\.16.*)\", NULL, NULL\n}\n"
  },
  {
    "path": "collector/kernel/kernel_collector.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/kernel_collector.h>\n#include <collector/kernel/kernel_collector_restarter.h>\n\n#include <channel/tcp_channel.h>\n#include <collector/constants.h>\n#include <collector/kernel/troubleshooting.h>\n#include <collector/server_command.h>\n#include <common/client_type.h>\n#include <common/cloud_platform.h>\n#include <common/collector_status.h>\n#include <platform/userspace-time.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/resource_usage_reporter.h>\n\n#include <absl/strings/match.h>\n\n#include <netdb.h>\n\n#include <random>\n\nnamespace {\nstatic constexpr std::chrono::milliseconds TRY_CONNECTING_TIMEOUT = 5s;\nstatic constexpr u64 connection_timeout_ms_ = 10000;\nstatic constexpr u64 probe_holdoff_timeout_ms_ = 2000;\nstatic constexpr u64 polling_timeout_ms_ = 100;\nstatic constexpr u64 slow_polling_timeout_ms_ = 1000;\n\n/* minimum time allowed between probes */\nstatic constexpr u64 inter_probe_time_ns_ = 120 * 1000 * 1000 * 1000ul;\n/* max jitter added to time between probes */\nstatic constexpr std::chrono::milliseconds MAX_JITTER_TIME = 10s;\n} // namespace\n\nvoid __try_connecting_cb(uv_timer_t *timer)\n{\n  KernelCollector *collector = (KernelCollector *)timer->data;\n  collector->try_connecting(timer);\n}\n\nvoid __connection_timeout_cb(uv_timer_t *timer)\n{\n  LOG::trace(\"KernelCollector: connection timeout\");\n\n  KernelCollector *collector = (KernelCollector *)timer->data;\n  collector->connection_timeout(timer);\n}\n\nvoid __probe_holdoff_cb(uv_timer_t *timer)\n{\n  LOG::trace(\"KernelCollector: probe holdoff timeout\");\n\n  KernelCollector *collector = (KernelCollector *)timer->data;\n  collector->probe_holdoff_timeout(timer);\n}\n\nvoid __polling_steady_state_cb(uv_timer_t *timer)\n{\n  KernelCollector *collector = (KernelCollector *)timer->data;\n  collector->polling_steady_state(timer);\n}\n\nvoid __polling_steady_state_slow_cb(uv_timer_t *timer)\n{\n  KernelCollector *collector = (KernelCollector *)timer->data;\n  collector->polling_steady_state_slow(timer);\n}\n\nvoid __handle_close_cb(uv_handle_t *handle)\n{\n  LOG::trace(\"KernelCollector: closed handle\");\n}\n\nKernelCollector::KernelCollector(\n    const BpfConfiguration &bpf_config,\n    config::IntakeConfig const &intake_config,\n    AwsMetadata const *aws_metadata,\n    GcpInstanceMetadata const *gcp_metadata,\n    std::map<std::string, std::string> config_labels,\n    uv_loop_t &loop,\n    CurlEngine &curl_engine,\n    bool enable_http_metrics,\n    u64 socket_stats_interval_sec,\n    CgroupHandler::CgroupSettings cgroup_settings,\n    std::string const &bpf_dump_file,\n    HostInfo host_info)\n    : bpf_config_(bpf_config),\n      intake_config_(std::move(intake_config)),\n      aws_metadata_(aws_metadata),\n      gcp_metadata_(gcp_metadata),\n      config_labels_(config_labels),\n      host_info_(std::move(host_info)),\n      loop_(loop),\n      last_lost_count_(0),\n      encoder_(intake_config_.make_encoder()),\n      callbacks_(*this),\n      primary_channel_(intake_config_.make_channel(loop)),\n      secondary_channel_(intake_config_.create_output_record_file()),\n      upstream_connection_(\n          WRITE_BUFFER_SIZE,\n          intake_config_.allow_compression(),\n          *primary_channel_,\n          secondary_channel_ ? &secondary_channel_ : nullptr),\n      writer_(upstream_connection_.buffered_writer(), monotonic, bpf_config_.boot_time_adjustment, encoder_.get()),\n      last_probe_monotonic_time_ns_(monotonic() - inter_probe_time_ns_),\n      is_connected_(false),\n      curl_engine_(curl_engine),\n      heartbeat_sender_(\n          loop_,\n          [this] {\n            send_heartbeat();\n            return scheduling::JobFollowUp::ok;\n          }),\n      enable_http_metrics_(enable_http_metrics),\n      socket_stats_interval_sec_(socket_stats_interval_sec),\n      cgroup_settings_(std::move(cgroup_settings)),\n      log_(writer_),\n      kernel_collector_restarter_(*this)\n{\n  if (!bpf_dump_file.empty()) {\n    auto const error = bpf_dump_file_.create(\n        bpf_dump_file.c_str(),\n        FileDescriptor::Access::write_only,\n        FileDescriptor::Positioning::append,\n        FileDescriptor::Permission::read_write,\n        FileDescriptor::Permission::read);\n\n    if (error) {\n      LOG::warn(\"unable to open/create eBPF dump file at '{}': {}\", bpf_dump_file, error);\n    }\n  }\n\n  int res;\n  /* initialize polling timer */\n  res = uv_timer_init(&loop_, &polling_timer_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not init polling_timer_\");\n  polling_timer_.data = this;\n\n  /* initialize slow polling timer */\n  res = uv_timer_init(&loop_, &slow_timer_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not init slow_timer_\");\n  slow_timer_.data = this;\n\n  /* initialize connection timeout timer */\n  res = uv_timer_init(&loop_, &connection_timeout_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not init connection_timeout_\");\n  connection_timeout_.data = this;\n\n  /* initialize probe_holdoff_timeor */\n  res = uv_timer_init(&loop_, &probe_holdoff_timer_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not init probe_holdoff_timer_\");\n  probe_holdoff_timer_.data = this;\n\n  /* initialize try_connecting timer */\n  res = uv_timer_init(&loop_, &try_connecting_timer_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not init try_connecting_timer_\");\n  try_connecting_timer_.data = this;\n\n  /* start in try_connecting state */\n  enter_try_connecting();\n}\n\nKernelCollector::~KernelCollector()\n{\n  on_close();\n}\n\nvoid KernelCollector::try_connecting(uv_timer_t *timer)\n{\n  cleanup_pointers();\n\n  try {\n    LOG::info(\"connecting to {}...\", intake_config_);\n    upstream_connection_.connect(callbacks_);\n  } catch (std::exception &e) {\n    LOG::trace(\"upstream connect threw exception: {}\", e.what());\n    return;\n  }\n\n  enter_connecting();\n}\n\nvoid KernelCollector::connection_timeout(uv_timer_t *timer)\n{\n  LOG::trace(\"connection timeout\");\n  upstream_connection_.close();\n}\n\nvoid KernelCollector::polling_steady_state(uv_timer_t *timer)\n{\n  if (disabled_) {\n    return;\n  }\n  /* push data to server */\n  try {\n    bpf_handler_->start_poll(1, 1);\n  } catch (std::exception &e) {\n    log_.error(\"BPFHandler::start_poll threw exception '{}'\", e.what());\n    enter_try_connecting();\n    return;\n  }\n\n  /* only print when some messages got lost */\n  if (bpf_handler_->serv_lost_count() > last_lost_count_) {\n    last_lost_count_ = bpf_handler_->serv_lost_count();\n    LOG::trace(\"-polling- lost count: {}\", bpf_handler_->serv_lost_count());\n  }\n}\n\nvoid KernelCollector::polling_steady_state_slow(uv_timer_t *timer)\n{\n  if (disabled_) {\n    return;\n  }\n  bpf_handler_->slow_poll();\n\n  ResourceUsageReporter::report(writer_);\n  upstream_connection_.flush();\n}\n\nvoid KernelCollector::on_close()\n{\n  cleanup_pointers();\n  upstream_connection_.close();\n  close_uv_handle_cleanly(reinterpret_cast<uv_handle_t *>(&polling_timer_), __handle_close_cb);\n  close_uv_handle_cleanly(reinterpret_cast<uv_handle_t *>(&slow_timer_), __handle_close_cb);\n  close_uv_handle_cleanly(reinterpret_cast<uv_handle_t *>(&connection_timeout_), __handle_close_cb);\n  close_uv_handle_cleanly(reinterpret_cast<uv_handle_t *>(&probe_holdoff_timer_), __handle_close_cb);\n  close_uv_handle_cleanly(reinterpret_cast<uv_handle_t *>(&try_connecting_timer_), __handle_close_cb);\n}\n\nvoid KernelCollector::on_upstream_connected()\n{\n  LOG::trace(\"upstream connected\");\n\n  try {\n    send_connection_metadata();\n  } catch (std::exception &e) {\n    LOG::error(\"Exception thrown when sending connection request and agent metadata: {}\", e.what());\n    return;\n  }\n}\n\nvoid KernelCollector::on_connected()\n{\n  LOG::trace(\"Connected, entering probe hold-off\");\n  enter_probe_holdoff();\n\n  heartbeat_sender_.start(HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL);\n}\n\nvoid KernelCollector::probe_holdoff_timeout(uv_timer_t *timer)\n{\n  auto const upstream_connection_flush_and_close = [this]() {\n    upstream_connection_.flush();\n    upstream_connection_.close();\n  };\n\n  LOG::trace(\"Adding probes\");\n\n  last_probe_monotonic_time_ns_ = monotonic();\n\n  auto const handle_exception = [&](TroubleshootItem item, std::exception const &e) {\n    log_.error(\"Exception during BPFHandler initialization, closing connection: {}\", e.what());\n    print_troubleshooting_message_and_exit(host_info_, item, e, log_, upstream_connection_flush_and_close);\n  };\n\n  auto potential_troubleshoot_item = TroubleshootItem::bpf_load_probes_failed;\n  try {\n    bpf_handler_.emplace(loop_, bpf_config_, enable_http_metrics_, bpf_dump_file_, log_, encoder_.get(), host_info_);\n\n    potential_troubleshoot_item = TroubleshootItem::unexpected_exception;\n    writer_.bpf_compiled();\n\n    kernel_collector_restarter_.reset();\n    bpf_handler_->load_buffered_poller(\n        upstream_connection_.buffered_writer(),\n        bpf_config_.boot_time_adjustment,\n        curl_engine_,\n        socket_stats_interval_sec_,\n        cgroup_settings_,\n        kernel_collector_restarter_);\n\n    potential_troubleshoot_item = TroubleshootItem::bpf_load_probes_failed;\n    bpf_handler_->load_probes(writer_);\n\n    /* Start running buf_poller in steady-state */\n    potential_troubleshoot_item = TroubleshootItem::unexpected_exception;\n    writer_.begin_telemetry();\n    writer_.collector_health(integer_value(::collector::CollectorStatus::healthy), 0);\n    LOG::info(\"Agent connected successfully. Telemetry is flowing!\");\n    is_connected_ = true;\n\n    kernel_collector_restarter_.startup_completed();\n\n    enter_polling_state();\n  } catch (std::system_error &e) {\n    if (e.code().value() == EPERM) {\n      handle_exception(TroubleshootItem::operation_not_permitted, e);\n      return;\n    }\n    handle_exception(potential_troubleshoot_item, e);\n    return;\n  } catch (std::exception &e) {\n    handle_exception(potential_troubleshoot_item, e);\n    return;\n  }\n}\n\nvoid KernelCollector::send_connection_metadata()\n{\n  // send a version_info message\n  upstream_connection_.set_compression(false);\n  writer_.version_info(versions::release.major(), versions::release.minor(), versions::release.patch());\n  upstream_connection_.flush();\n  upstream_connection_.set_compression(true);\n\n  // we use strncpy in a way that might truncate the '\\0' at the end, so need to\n  // ask GCC (8.0+ not to fail)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstringop-truncation\"\n\n  /* Write config file labels */\n#define make_bufs_from_field(struct_name, field1, buf_name1, field2, buf_name2)                                                \\\n  struct struct_name __##struct_name##__##buf_name1;                                                                           \\\n  char buf_name1[sizeof(__##struct_name##__##buf_name1.field1)] = {};                                                          \\\n  struct struct_name __##struct_name##__##buf_name2;                                                                           \\\n  char buf_name2[sizeof(__##struct_name##__##buf_name2.field2)] = {};\n\n  if (intake_config_.encoder() == IntakeEncoder::binary) {\n    writer_.connect(static_cast<u8>(ClientType::kernel), jb_blob{host_info_.hostname});\n    upstream_connection_.flush();\n  }\n\n  writer_.os_info(\n      integer_value(host_info_.os), host_info_.os_flavor, jb_blob{host_info_.os_version}, jb_blob{host_info_.kernel_version});\n\n  writer_.report_cpu_cores(std::thread::hardware_concurrency());\n\n  writer_.kernel_headers_source(integer_value(host_info_.kernel_headers_source));\n\n  for (auto const &label : config_labels_) {\n    writer_.set_config_label(jb_blob{label.first}, jb_blob{label.second});\n  }\n\n  /* Kernel version */\n  constexpr std::string_view kernel_version_label = \"__kernel_version\";\n  writer_.set_config_label(jb_blob{kernel_version_label}, jb_blob{host_info_.kernel_version});\n  upstream_connection_.flush();\n\n#define make_buf_from_field(struct_name, field, buf_name)                                                                      \\\n  struct struct_name __##struct_name##__##buf_name;                                                                            \\\n  char buf_name[sizeof(__##struct_name##__##buf_name.field)] = {};\n\n  if (aws_metadata_) {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::aws));\n    if (auto const &account_id = aws_metadata_->account_id()) {\n      LOG::trace_in(CloudPlatform::aws, \"reporting aws account id: {}\", account_id.value());\n      writer_.cloud_platform_account_info(jb_blob{account_id.value()});\n    } else {\n      LOG::trace_in(CloudPlatform::aws, \"no aws account id to report\");\n    }\n\n    auto id = aws_metadata_->id().value();\n    if (id.starts_with(std::string_view(\"i-\"))) {\n      id.remove_prefix(2);\n    }\n\n    writer_.set_node_info(\n        jb_blob{aws_metadata_->az().value()},\n        jb_blob{aws_metadata_->iam_role().value()},\n        jb_blob{id},\n        jb_blob{aws_metadata_->type().value()});\n\n    upstream_connection_.flush();\n\n    for (auto const &interface : aws_metadata_->network_interfaces()) {\n      for (auto const &ipv4 : interface.private_ipv4s()) {\n        struct sockaddr_in private_sa;\n        int res = inet_pton(AF_INET, ipv4.c_str(), &(private_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__private_ipv4_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.private_ipv4_addr(private_sa.sin_addr.s_addr, (u8 *)vpc_id_buf);\n      }\n\n      for (auto const &ipv6 : interface.ipv6s()) {\n        struct sockaddr_in6 sa;\n        int res = inet_pton(AF_INET6, ipv6.c_str(), &(sa.sin6_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__ipv6_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.ipv6_addr(sa.sin6_addr.s6_addr, (u8 *)vpc_id_buf);\n      }\n\n      for (auto const &mapped_ipv4 : interface.mapped_ipv4s()) {\n        struct sockaddr_in public_sa;\n        int res = inet_pton(AF_INET, mapped_ipv4.first.c_str(), &(public_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        struct sockaddr_in private_sa;\n        res = inet_pton(AF_INET, mapped_ipv4.second.c_str(), &(private_sa.sin_addr));\n        if (res != 1) {\n          continue;\n        }\n        make_buf_from_field(jb_ingest__public_to_private_ipv4, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.public_to_private_ipv4(public_sa.sin_addr.s_addr, private_sa.sin_addr.s_addr, (u8 *)vpc_id_buf);\n      }\n    }\n  } else if (gcp_metadata_) {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::gcp));\n    // TODO: obtain account_id for GCP and uncomment below\n    // LOG::trace_in(CloudPlatform::gcp), \"reporting gcp account id: {}\", account_id.value());\n    // writer_.cloud_platform_account_info(jb_blob{account_id});\n\n    writer_.set_node_info(\n        jb_blob{gcp_metadata_->az()},\n        jb_blob{gcp_metadata_->role()},\n        jb_blob{gcp_metadata_->hostname()},\n        jb_blob{gcp_metadata_->type()});\n\n    upstream_connection_.flush();\n\n    for (auto const &interface : gcp_metadata_->network_interfaces()) {\n      if (auto const ipv4 = interface.ipv4()) {\n        make_buf_from_field(jb_ingest__private_ipv4_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.private_ipv4_addr(ipv4->as_int(), (u8 *)vpc_id_buf);\n\n        for (auto const &public_ip : interface.public_ips()) {\n          make_buf_from_field(jb_ingest__public_to_private_ipv4, vpc_id, vpc_id_buf);\n          strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n          writer_.public_to_private_ipv4(public_ip.as_int(), ipv4->as_int(), (u8 *)vpc_id_buf);\n        }\n      } else if (auto const ipv6 = interface.ipv6()) {\n        uint8_t ipv6_buffer[16];\n        ipv6->write_to(ipv6_buffer);\n        make_buf_from_field(jb_ingest__ipv6_addr, vpc_id, vpc_id_buf);\n        strncpy(vpc_id_buf, interface.vpc_id().c_str(), sizeof(vpc_id_buf));\n        writer_.ipv6_addr(ipv6_buffer, (u8 *)vpc_id_buf);\n      }\n    }\n  } else {\n    writer_.cloud_platform(static_cast<u16>(CloudPlatform::unknown));\n\n    writer_.set_node_info(jb_blob{/* az */}, jb_blob{/* role */}, jb_blob{host_info_.hostname}, jb_blob{/* instance_type */});\n\n    // no network interface data (public/private ip) to send\n  }\n\n  // pop the \"-Wstringop-truncation\" warning quelch\n#pragma GCC diagnostic pop\n\n  /* Finished sending metadata */\n  writer_.metadata_complete(0);\n\n  upstream_connection_.flush();\n\n  on_connected();\n}\n\nvoid KernelCollector::on_error(int error)\n{\n  /* we don't want attempts to perform IO on the channel */\n  heartbeat_sender_.stop();\n  stop_all_timers();\n}\n\nvoid KernelCollector::restart()\n{\n  /* we don't want attempts to perform IO on the channel */\n  heartbeat_sender_.stop();\n\n  // flush the channel so previously sent messages make it to the reducer\n  upstream_connection_.flush();\n\n  // close the channel, it will trigger reconnect via KernelCollector::Callbacks::on_closed() which calls enter_try_connecting()\n  upstream_connection_.close();\n}\n\nvoid KernelCollector::cleanup_pointers()\n{\n  bpf_handler_.reset();\n}\n\nvoid KernelCollector::received_data(const u8 *data, int data_len)\n{\n  std::string_view const response{reinterpret_cast<char const *>(data), static_cast<std::size_t>(data_len)};\n  LOG::trace(\"KernelCollector::received_data({}): {}\", data_len);\n\n  switch (intake_config_.encoder()) {\n  case IntakeEncoder::binary: {\n    static constexpr int max_command_length = 8;\n\n    for (int i = 0; i < data_len; i++) {\n      received_command_ = (received_command_ << 8) | *(data + i);\n      recieved_length_++;\n\n      if (recieved_length_ == max_command_length) {\n        handle_received_command(received_command_);\n        recieved_length_ = 0;\n      }\n    }\n  } break;\n  }\n}\n\nvoid KernelCollector::handle_received_command(u64 command)\n{\n  if (command == static_cast<u64>(ServerCommand::DISABLE_SEND)) {\n    LOG::info(\"Stop sending data, instructed by the server.\");\n    disabled_ = true;\n  }\n}\n\nvoid KernelCollector::send_heartbeat()\n{\n  writer_.heartbeat();\n  upstream_connection_.flush();\n}\n\nKernelCollector::Callbacks::Callbacks(KernelCollector &collector) : collector_(collector) {}\n\nu32 KernelCollector::Callbacks::received_data(const u8 *data, int data_len)\n{\n  collector_.received_data(data, data_len);\n  return data_len;\n}\n\nvoid KernelCollector::Callbacks::on_error(int err)\n{\n  LOG::trace(\"upstream connection error {}\", static_cast<std::errc>(-err));\n\n  collector_.on_error(err);\n\n  /* close the channel, it will trigger reconnect */\n  collector_.upstream_connection_.close();\n}\n\nvoid KernelCollector::Callbacks::on_closed()\n{\n  LOG::trace(\"closed upstream connection, will reconnect...\");\n  collector_.enter_try_connecting();\n}\n\nvoid KernelCollector::Callbacks::on_connect()\n{\n  LOG::trace(\"established upstream connection\");\n  collector_.on_upstream_connected();\n}\n\nvoid KernelCollector::enter_try_connecting(std::chrono::milliseconds discount)\n{\n  if (is_connected_) {\n    LOG::info(\"disconnected from upstream, attempting to reconnect...\");\n    is_connected_ = false;\n  }\n\n  stop_all_timers();\n\n  auto timeout = discount > TRY_CONNECTING_TIMEOUT ? 0ms : TRY_CONNECTING_TIMEOUT - discount;\n  u64 now = monotonic();\n\n  if (now - last_probe_monotonic_time_ns_ < inter_probe_time_ns_) {\n    // need to bound using inter_probe_time_ns_\n    std::chrono::milliseconds const at_least{\n        (inter_probe_time_ns_ - (now - last_probe_monotonic_time_ns_)) / ((u64)1000 * 1000)};\n    timeout = std::max(timeout, at_least);\n  }\n\n  // add jitter\n  {\n    std::random_device rd;\n    std::uniform_int_distribution<u64> d(0, integer_time<std::chrono::milliseconds>(MAX_JITTER_TIME));\n    timeout += std::chrono::milliseconds{d(rd)};\n  }\n\n  LOG::trace(\n      \"KernelCollector: entering TRY_CONNECTING state. Next \"\n      \"reconnection attempt in {}\",\n      timeout);\n  int res = uv_timer_start(\n      &try_connecting_timer_,\n      __try_connecting_cb,\n      integer_time<std::chrono::milliseconds>(timeout),\n      integer_time<std::chrono::milliseconds>(TRY_CONNECTING_TIMEOUT));\n  if (res != 0) {\n    throw std::runtime_error(\"Could not start try_connecting_timer\");\n  }\n}\n\nvoid KernelCollector::enter_connecting()\n{\n  stop_all_timers();\n\n  LOG::trace(\"KernelCollector: entering CONNECTING state\");\n\n  int res = uv_timer_start(&connection_timeout_, __connection_timeout_cb, connection_timeout_ms_, 0);\n  if (res != 0)\n    throw std::runtime_error(\"Could not start connection_timeout timer\");\n}\n\nvoid KernelCollector::enter_probe_holdoff()\n{\n  stop_all_timers();\n\n  LOG::trace(\"KernelCollector: entering PROBE_HOLDOFF state\");\n\n  int res = uv_timer_start(&probe_holdoff_timer_, __probe_holdoff_cb, probe_holdoff_timeout_ms_, 0);\n  if (res != 0)\n    throw std::runtime_error(\"Could not start probe_holdoff_timer\");\n}\n\nvoid KernelCollector::enter_polling_state()\n{\n  stop_all_timers();\n\n  LOG::trace(\"KernelCollector: entering POLLING state\");\n\n  int res = uv_timer_start(&polling_timer_, __polling_steady_state_cb, polling_timeout_ms_, polling_timeout_ms_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not start polling_timer\");\n\n  res = uv_timer_start(&slow_timer_, __polling_steady_state_slow_cb, slow_polling_timeout_ms_, slow_polling_timeout_ms_);\n  if (res != 0)\n    throw std::runtime_error(\"Could not start slow_timer\");\n}\n\nvoid KernelCollector::stop_all_timers()\n{\n  uv_timer_stop(&try_connecting_timer_);\n  uv_timer_stop(&connection_timeout_);\n  uv_timer_stop(&probe_holdoff_timer_);\n  uv_timer_stop(&polling_timer_);\n  uv_timer_stop(&slow_timer_);\n}\n\n#ifndef NDEBUG\nvoid KernelCollector::debug_bpf_lost_samples()\n{\n  if (bpf_handler_) {\n    bpf_handler_->debug_bpf_lost_samples();\n  }\n}\n#endif\n"
  },
  {
    "path": "collector/kernel/kernel_collector.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/callbacks.h>\n#include <channel/file_channel.h>\n#include <channel/upstream_connection.h>\n#include <collector/kernel/bpf_handler.h>\n#include <collector/kernel/kernel_collector_restarter.h>\n#include <collector/kernel/probe_handler.h>\n#include <common/host_info.h>\n#include <config/intake_config.h>\n#include <generated/ebpf_net/ingest/writer.h>\n#include <platform/platform.h>\n#include <scheduling/interval_scheduler.h>\n#include <util/aws_instance_metadata.h>\n#include <util/curl_engine.h>\n#include <util/gcp_instance_metadata.h>\n#include <util/logger.h>\n\n#include <uv.h>\n\n#include <map>\n\nclass KernelCollector {\n  friend class KernelCollectorRestarter;\n  friend class KernelCollectorTest;\n\npublic:\n  /**\n   * c'tor\n   */\n  KernelCollector(\n      const BpfConfiguration &bpf_config,\n      config::IntakeConfig const &intake_config,\n      AwsMetadata const *aws_metadata,\n      GcpInstanceMetadata const *gcp_metadata,\n      std::map<std::string, std::string> config_labels,\n      uv_loop_t &loop,\n      CurlEngine &curl_engine,\n      bool enable_http_metrics,\n      u64 socket_stats_interval_sec,\n      CgroupHandler::CgroupSettings cgroup_settings,\n      std::string const &bpf_dump_file,\n      HostInfo host_info);\n\n  /**\n   * d'tor\n   */\n  virtual ~KernelCollector();\n\n  /**\n   * called by the uv_timer callback for establishing server connections\n   */\n  void try_connecting(uv_timer_t *timer);\n\n  /**\n   * called by the uv_timer callback for timing out connection attempts\n   */\n  void connection_timeout(uv_timer_t *timer);\n\n  /**\n   * called by the uv_timer callback for starting bpf probes\n   */\n  void probe_holdoff_timeout(uv_timer_t *timer);\n\n  /**\n   * called by the uv_timer callback for polling processes\n   */\n  void polling_steady_state(uv_timer_t *timer);\n\n  /**\n   * called by the uv_timer callback for slow polling processes\n   */\n  void polling_steady_state_slow(uv_timer_t *timer);\n\n  /**\n   * called when upstream is connected, to complete connection\n   */\n  void on_upstream_connected();\n\n  /* called from callbacks given to uv_close */\n  void on_close();\n\n#ifndef NDEBUG\n  /* Debug code for internal development to simulate lost BPF samples (PERF_RECORD_LOST) in BufferedPoller. */\n  void debug_bpf_lost_samples();\n#endif\n\nprivate:\n  class Callbacks : public channel::Callbacks {\n  public:\n    Callbacks(KernelCollector &collector);\n    virtual u32 received_data(const u8 *data, int data_len) override;\n    virtual void on_error(int err) override;\n    virtual void on_closed() override;\n    virtual void on_connect() override;\n\n  private:\n    KernelCollector &collector_;\n  };\n\n  /* sends info from config (if present) and AWS state */\n  void send_connection_metadata();\n\n  /* cleans up shared pointers */\n  void cleanup_pointers();\n\n  // enter try_connecting state - will take `discount` time out of\n  // the hold-off\n  void enter_try_connecting(std::chrono::milliseconds discount = 0ms);\n\n  /* connecting state, while Tcp is trying to connect and we can time out */\n  void enter_connecting();\n\n  /* probe holdoff. got upstream connection and waiting before adding probes */\n  void enter_probe_holdoff();\n\n  /* enter polling steady state */\n  void enter_polling_state();\n\n  /* stops all timers */\n  void stop_all_timers();\n\n  /* receive data from connection */\n  void received_data(const u8 *data, int data_len);\n\n  /* handles command received from the server */\n  void handle_received_command(u64 command);\n\n  /* sends a heartbeat message to the server */\n  void send_heartbeat();\n\n  void on_connected();\n  void on_error(int error);\n\n  /* called to restart the KernelCollector */\n  void restart();\n\nprivate:\n  /* parameters for establishing connections */\n  BpfConfiguration const bpf_config_;\n  config::IntakeConfig const &intake_config_;\n  AwsMetadata const *aws_metadata_;\n  GcpInstanceMetadata const *gcp_metadata_;\n  const std::map<std::string, std::string> config_labels_;\n  HostInfo const host_info_;\n\n  // Following 2 variables handle the command received from server.\n  //\n  // |received_command_| holds the paritial command received so far, of\n  // |recieved_length_| bytes.\n  // When |recieved_length_| == max_command_length, handle_recieved_command()\n  // function is invoked.\n  u64 received_command_ = 0;\n  int recieved_length_ = 0;\n\n  bool disabled_ = false;\n\n  /* lib_uv objects */\n  uv_loop_t &loop_;\n  uv_timer_t try_connecting_timer_;\n  uv_timer_t connection_timeout_;\n  uv_timer_t probe_holdoff_timer_;\n  uv_timer_t polling_timer_;\n  uv_timer_t slow_timer_;\n\n  u64 last_lost_count_;\n  std::unique_ptr<::ebpf_net::ingest::Encoder> encoder_;\n  std::optional<BPFHandler> bpf_handler_;\n  Callbacks callbacks_;\n  std::unique_ptr<channel::NetworkChannel> primary_channel_;\n  channel::FileChannel secondary_channel_;\n  channel::UpstreamConnection upstream_connection_;\n  ::ebpf_net::ingest::Writer writer_;\n\n  u64 last_probe_monotonic_time_ns_;\n\n  // is the agent in healthy steady state -- so we can log when not healthy\n  bool is_connected_;\n\n  CurlEngine &curl_engine_;\n\n  scheduling::IntervalScheduler heartbeat_sender_;\n\n  /* enable/disable features */\n  bool enable_http_metrics_;\n  u64 socket_stats_interval_sec_;\n  CgroupHandler::CgroupSettings const cgroup_settings_;\n\n  FileDescriptor bpf_dump_file_;\n  logging::Logger log_;\n\n  KernelCollectorRestarter kernel_collector_restarter_;\n};\n"
  },
  {
    "path": "collector/kernel/kernel_collector_restarter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/kernel_collector.h>\n#include <collector/kernel/kernel_collector_restarter.h>\n\nKernelCollectorRestarter::KernelCollectorRestarter(KernelCollector &collector) : collector_(collector)\n{\n  reset();\n}\n\nvoid KernelCollectorRestarter::check_restart()\n{\n  if (startup_completed_ && restart_requested_) {\n    if (restart_in_progress_) {\n      LOG::debug(\"KernelCollector restart already in progress\");\n    } else {\n      restart_in_progress_ = true;\n      LOG::debug(\"restarting KernelCollector\");\n      collector_.restart();\n    }\n  }\n}\n\nvoid KernelCollectorRestarter::startup_completed()\n{\n  LOG::debug(\"KernelCollectorRestarter: startup completed\");\n  startup_completed_ = true;\n  check_restart();\n}\n\nvoid KernelCollectorRestarter::request_restart()\n{\n  LOG::debug(\"KernelCollectorRestarter: restart requested\");\n  restart_requested_ = true;\n  check_restart();\n}\n\nvoid KernelCollectorRestarter::reset()\n{\n  startup_completed_ = false;\n  restart_requested_ = false;\n  restart_in_progress_ = false;\n}\n"
  },
  {
    "path": "collector/kernel/kernel_collector_restarter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n\nclass KernelCollector;\n\n/**\n * Helper class to facilitate restarting KernelCollector, i.e. when BufferedPoller detects lost BPF samples (PERF_RECORD_LOST).\n * If KernelCollector startup has completed, then a restart request will be processed immediately.\n * If KernelCollector startup has not completed, then the processing of a restart request will be deferred until after\n * KernelCollector startup has completed.\n * If a restart is already in progress, subsequent restart requests will be ignored.\n */\nclass KernelCollectorRestarter {\n  friend class KernelCollector;\n\npublic:\n  KernelCollectorRestarter(KernelCollector &collector);\n  void startup_completed();\n  void request_restart();\n  void reset();\n\nprivate:\n  void check_restart();\n\n  bool startup_completed_;\n  bool restart_requested_;\n  bool restart_in_progress_;\n\n  KernelCollector &collector_;\n};\n"
  },
  {
    "path": "collector/kernel/kernel_collector_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <channel/test_channel.h>\n#include <collector/kernel/cgroup_handler.h>\n#include <collector/kernel/kernel_collector.h>\n#include <common/host_info.h>\n#include <common/intake_encoder.h>\n#include <config/config_file.h>\n#include <config/intake_config.h>\n#include <generated/ebpf_net/ingest/meta.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <jitbuf/jb.h>\n#include <spdlog/fmt/chrono.h>\n#include <util/aws_instance_metadata.h>\n#include <util/boot_time.h>\n#include <util/code_timing.h>\n#include <util/common_test.h>\n#include <util/curl_engine.h>\n#include <util/error_handling.h>\n#include <util/gcp_instance_metadata.h>\n#include <util/json.h>\n#include <util/json_converter.h>\n#include <util/log.h>\n#include <util/log_whitelist.h>\n#include <util/logger.h>\n#include <util/system_ops.h>\n\n#include <sys/utsname.h>\n\n#include <map>\n#include <regex>\n#include <string>\n\n#include <uv.h>\n\n#define BPF_DUMP_FILE \"/tmp/bpf-dump-file\"\n#define INTAKE_DUMP_FILE \"/tmp/intake-dump-file\"\n\nextern \"C\" {\n/* bpf source code */\nextern char agent_bpf_c[];\nextern unsigned int agent_bpf_c_len;\n} // extern \"C\"\n\nclass TestIntakeConfig : public config::IntakeConfig {\n  using config::IntakeConfig::IntakeConfig;\n\n  bool allow_compression() const { return false; }\n\n  std::unique_ptr<channel::NetworkChannel> make_channel(uv_loop_t &loop) const override\n  {\n    return std::make_unique<channel::TestChannel>(loop, encoder());\n  }\n};\n\n// Conditions to be met before stopping test\nstruct StopConditions {\n  std::chrono::seconds timeout_sec;\n  u64 num_sends;\n  std::map<std::string, u64> names_and_counts;\n  bool wait_for_all_workloads_to_complete;\n};\n\nclass KernelCollectorTest : public CommonTest {\n\nprotected:\n  void SetUp() override\n  {\n    CommonTest::SetUp();\n\n    // Allow relevant HTTP-related logs for this test\n    set_log_whitelist<AgentLogKind>({AgentLogKind::HTTP, AgentLogKind::PROTOCOL, AgentLogKind::BPF, AgentLogKind::PERF});\n\n    ASSERT_EQ(0, uv_loop_init(&loop_));\n  }\n\n  void TearDown() override\n  {\n    // Clean up loop_ to avoid valgrind and asan complaints about memory leaks.\n    close_uv_loop_cleanly(&loop_);\n  }\n\n  void start_kernel_collector(\n      IntakeEncoder intake_encoder,\n      StopConditions const &stop_conditions,\n      std::string const &bpf_dump_file = \"\",\n      std::function<void(nlohmann::json const &)> const &ingest_msg_cb = {})\n  {\n    stop_conditions_.emplace(stop_conditions);\n\n    // This mostly duplicates the KernelCollector setup done in collector/kernel/main.cc.\n\n    // Create BPF configuration with test parameters\n    u64 boot_time_adjustment = get_boot_time();\n\n    test_intake_config_ = TestIntakeConfig(\"\", \"\", INTAKE_DUMP_FILE, intake_encoder);\n\n    auto const aws_metadata = AwsMetadata::fetch(1000ms);\n\n    auto const gcp_metadata = GcpInstanceMetadata::fetch(1000ms);\n\n    config::ConfigFile configuration_data(config::ConfigFile::YamlFormat(), \"\");\n\n    std::unique_ptr<CurlEngine> curl_engine = CurlEngine::create(&loop_);\n\n    bool const enable_http_metrics = true;\n\n    bool const enable_userland_tcp = false;\n\n    u64 const socket_stats_interval_sec = 10;\n\n    BpfConfiguration bpf_config{\n        .boot_time_adjustment = boot_time_adjustment,\n        .filter_ns = 10 * 1000 * 1000ull,\n        .enable_tcp_data_stream = enable_userland_tcp};\n\n    struct utsname unamebuf;\n    if (uname(&unamebuf)) {\n      throw std::runtime_error(\"Failed to get system uname\");\n    }\n    LOG::info(\n        \"Running on:\\n\"\n        \"   sysname: {}\\n\"\n        \"  nodename: {}\\n\"\n        \"   release: {}\\n\"\n        \"   version: {}\\n\"\n        \"   machine: {}\",\n        unamebuf.sysname,\n        unamebuf.nodename,\n        unamebuf.release,\n        unamebuf.version,\n        unamebuf.machine);\n\n    // resolve hostname\n    std::string const hostname = get_host_name(MAX_HOSTNAME_LENGTH).recover([&](auto &error) {\n      LOG::error(\"Unable to retrieve host information from uname: {}\", error);\n      return aws_metadata->id().valid() ? std::string(aws_metadata->id().value()) : \"(unknown)\";\n    });\n\n    HostInfo host_info{\n        .os = OperatingSystem::Linux,\n        .os_flavor = integer_value(LinuxDistro::unknown),\n        .os_version = \"unknown\",\n        .kernel_headers_source = KernelHeadersSource::libbpf,\n        .kernel_version = unamebuf.release,\n        .hostname = hostname};\n\n    kernel_collector_.emplace(\n        bpf_config,\n        *test_intake_config_,\n        aws_metadata.try_value(),\n        gcp_metadata.try_value(),\n        configuration_data.labels(),\n        loop_,\n        *curl_engine,\n        enable_http_metrics,\n        socket_stats_interval_sec,\n        CgroupHandler::CgroupSettings{false, std::nullopt},\n        bpf_dump_file,\n        host_info);\n\n    if (ingest_msg_cb) {\n      get_test_channel()->set_sent_msg_cb(ingest_msg_cb);\n    }\n\n    run_test_stopper();\n    run_workload_starter();\n\n    LOG::info(\"starting event loop...\");\n    uv_run(&loop_, UV_RUN_DEFAULT);\n  }\n\n  void stop_kernel_collector()\n  {\n    stop_workloads();\n\n    print_json_messages();\n    if (timeout_exceeded_) {\n      print_stop_conditions();\n    }\n    print_message_counts();\n\n    // NOTE: use EXPECT_s here because ASSERT_s fail fast, returning from the current function, skipping the cleanup below\n    EXPECT_EQ(0ull, get_probe_handler().num_failed_probes_);\n    EXPECT_TRUE(binary_messages_check_counts());\n    EXPECT_EQ(0ull, get_test_channel()->get_num_failed_sends());\n    EXPECT_EQ(false, timeout_exceeded_);\n\n    auto &message_counts = get_test_channel()->get_message_counts();\n    EXPECT_EQ(0ull, message_counts[\"bpf_log\"]);\n\n    kernel_collector_->on_close();\n\n    uv_stop(&loop_);\n\n    print_code_timings();\n  }\n\n  void run_test_stopper()\n  {\n    auto stop_test_check = [&]() {\n      SCOPED_TIMING(StopTestCheck);\n\n      auto const &stop_conditions = stop_conditions_->get();\n\n      // check for test timeout\n      if (stopwatch_) {\n        timeout_exceeded_ = stopwatch_->elapsed(stop_conditions.timeout_sec);\n        LOG::trace(\n            \"stop_test_check() stop_conditions timeout_sec {} exceeded {}\", stop_conditions.timeout_sec, timeout_exceeded_);\n        if (timeout_exceeded_) {\n          LOG::error(\"stop_test_check() test timeout of {} exceeded\", stop_conditions.timeout_sec);\n          stop_kernel_collector();\n          return;\n        }\n      }\n\n      // wait for all workloads to complete if requested\n      if (stop_conditions.wait_for_all_workloads_to_complete) {\n        LOG::trace(\"stop_test_check() num_remaining_workloads_ = {}\", num_remaining_workloads_);\n        if (num_remaining_workloads_) {\n          stop_test_timer_->defer(std::chrono::seconds(1));\n          return;\n        }\n      }\n\n      // check num_sends\n      auto channel = get_test_channel();\n      auto num_sends = channel->get_num_sends();\n      LOG::trace(\n          \"stop_test_check() channel->get_num_sends() = {} stop_conditions num_sends = {}\",\n          num_sends,\n          stop_conditions.num_sends);\n      if (num_sends < stop_conditions.num_sends) {\n        stop_test_timer_->defer(std::chrono::seconds(1));\n        return;\n      }\n\n      // check names_and_counts\n      auto &message_counts = channel->get_message_counts();\n      bool reschedule = false;\n      for (auto const &[name, count] : stop_conditions.names_and_counts) {\n        auto message_count = message_counts[name];\n        LOG::trace(\"stop_test_check() message_counts[{}] = {}  \\tstop count = {}\", name, message_count, count);\n        if (message_count < count) {\n          reschedule = true;\n        }\n      }\n      if (reschedule) {\n        stop_test_timer_->defer(std::chrono::seconds(1));\n        return;\n      }\n\n      LOG::trace(\"stop_test_check() stop_conditions have been met - calling stop_kernel_collector()\");\n      stop_kernel_collector();\n    };\n\n    stop_test_timer_ = std::make_unique<scheduling::Timer>(loop_, stop_test_check);\n    stop_test_timer_->defer(std::chrono::seconds(1));\n  }\n\n  void start_workload(std::function<void()> workload_cb)\n  {\n    auto index = workload_index_++;\n    ++num_remaining_workloads_;\n\n    auto workload_wrapper = [this, workload_cb, index]() {\n      LOG::info(\"workload {} starting\", index);\n      workload_cb();\n      LOG::info(\"workload {} complete\", index);\n      --num_remaining_workloads_;\n    };\n\n    workload_threads_.emplace_back(workload_wrapper);\n  }\n\n  void add_workload(std::function<void()> workload) { workloads_.push_back(std::move(workload)); }\n\n  void add_workload_processes()\n  {\n    add_workload([]() {\n      system(\n          \"exec 1> /tmp/workload-processes.log 2>&1; echo starting workload; set -x; whoami; pwd; ls; cd /tmp; pwd; ls; cd /; pwd; ls; cd ~; pwd; ls; echo workload complete\");\n    });\n  }\n\n  void add_workload_curl_otel()\n  {\n    add_workload([]() {\n      system(\n          \"exec 1> /tmp/workload-curl-otel.log 2>&1; echo starting workload; for n in $(seq 1 10); do curl https://opentelemetry.io; done; echo workload complete\");\n    });\n  }\n\n  void add_workload_curl_localhost()\n  {\n    add_workload([]() {\n      auto pid = fork();\n      if (pid == 0) {\n        int fd = open(\"/dev/null\", O_WRONLY);\n        dup2(fd, 1); // redirect stdout\n        dup2(fd, 2); // redirect stderr\n        execl(\"/usr/bin/python3\", \"python3\", \"-m\", \"http.server\", \"28099\", nullptr);\n        exit(1);\n      }\n\n      system(\n          \"exec 1> /tmp/workload-curl-localhost.log 2>&1; echo starting workload; for n in $(seq 1 100); do curl localhost:28099; done; echo workload complete\");\n\n      kill(pid, SIGTERM);\n    });\n  }\n\n  void add_workload_stress_ng_sock()\n  {\n    add_workload([]() {\n      // Emit stress-ng output live to console and also capture to a log file for later inspection.\n      // Use bash for process substitution; fall back gracefully if stdbuf is unavailable.\n      system(\"bash -lc '\"\n             \"exec > >(tee -a /tmp/workload-stress-ng-sock.log) 2>&1; \"\n             \"echo starting workload; \"\n             \"echo stress-ng version: $(stress-ng --version 2>&1 || true); \"\n             \"for n in $(seq 1 10); do \"\n             \"  echo [workload-0] iteration $n start $(date -Is); \"\n             \"  if command -v stdbuf >/dev/null 2>&1; then \"\n             \"    stdbuf -oL -eL stress-ng --sock 2 --sock-domain ipv4 --sock-ops 1000 --sock-port 6787 --metrics-brief; \"\n             \"  else \"\n             \"    stress-ng --sock 2 --sock-domain ipv4 --sock-ops 1000 --sock-port 6787 --metrics-brief; \"\n             \"  fi; \"\n             \"  echo [workload-0] iteration $n end $(date -Is); \"\n             \"  sleep .1; \"\n             \"done; \"\n             \"echo workload complete'\");\n    });\n  }\n\n  void start_workloads()\n  {\n    num_remaining_workloads_ = 0;\n\n    for (auto workload : workloads_) {\n      start_workload(workload);\n    }\n  };\n\n  void run_workload_starter()\n  {\n    auto &message_counts = get_test_channel()->get_message_counts();\n\n    auto start_workloads_check = [&]() {\n      LOG::trace(\"in start_workloads_check()\");\n      if ((message_counts[\"bpf_compiled\"] >= 1) && (message_counts[\"socket_steady_state\"] >= 1) &&\n          (message_counts[\"process_steady_state\"] >= 1)) {\n        LOG::trace(\"start_workloads_check() STARTING\");\n        start_workloads();\n        // this is where we start timing for purposes of the test timeout\n        stopwatch_.emplace();\n      } else {\n        start_workloads_timer_->defer(std::chrono::seconds(1));\n      }\n    };\n\n    start_workloads_timer_ = std::make_unique<scheduling::Timer>(loop_, start_workloads_check);\n    start_workloads_timer_->defer(std::chrono::seconds(1));\n  }\n\n  void stop_workloads()\n  {\n    for (auto &thr : workload_threads_) {\n      if (thr.joinable()) {\n        thr.join();\n      }\n    }\n  };\n\n  void print_stop_conditions()\n  {\n    auto &message_counts = get_test_channel()->get_message_counts();\n    LOG::debug(\"stop conditions:\");\n    for (auto const &[name, count] : stop_conditions_->get().names_and_counts) {\n      auto message_count = message_counts[name];\n      LOG::debug(\n          \"stop_conditions[\\\"{}\\\"] = {}  \\t({} received) {}\",\n          name,\n          count,\n          message_count,\n          message_count < count ? \" FAILED\" : \"\");\n    }\n  }\n\n  void print_message_counts()\n  {\n    LOG::debug(\"message_counts:\");\n    for (auto const &[name, count] : get_test_channel()->get_message_counts()) {\n      LOG::debug(\"message_counts[\\\"{}\\\"] = {}\", name, count);\n    }\n  }\n\n  void print_json_messages()\n  {\n    LOG::trace(\"json_messages:\");\n    auto print_message = [&](channel::TestChannel::JsonMessageType const &msg) { LOG::trace(\"{}\", log_waive(msg.dump())); };\n\n    get_test_channel()->json_messages_for_each(print_message);\n  }\n\n  // This is an example of using TestChannel::binary_messages_for_each().  It looks at each message, counts the message type,\n  // and compares the counts to TestChannel::message_counts_.\n  bool binary_messages_check_counts()\n  {\n    channel::TestChannel::MessageCountsType check_message_counts;\n\n    size_t num_binary_messages = 0;\n    auto count_message = [&](channel::TestChannel::BinaryMessageType const &msg) {\n      ++num_binary_messages;\n\n      std::stringstream ss;\n      json_converter::WireToJsonConverter<ebpf_net::ingest_metadata> converter(ss);\n\n      converter.process(reinterpret_cast<char const *>(msg.data()), msg.size());\n      std::string str = \"[\" + ss.str() + \"]\";\n      nlohmann::json const objects = nlohmann::json::parse(str);\n      for (auto const &object : objects) {\n        ++check_message_counts[object[\"name\"]];\n      }\n    };\n\n    get_test_channel()->binary_messages_for_each(count_message);\n\n    LOG::trace(\"check_message_counts:\");\n    for (auto const &[name, count] : check_message_counts) {\n      LOG::trace(\"check_message_counts[\\\"{}\\\"] = {}\", name, count);\n    }\n\n    return num_binary_messages ? check_message_counts == get_test_channel()->get_message_counts() : true;\n  }\n\n  channel::TestChannel *get_test_channel()\n  {\n    return dynamic_cast<channel::TestChannel *>(kernel_collector_->primary_channel_.get());\n  }\n\n  ProbeHandler &get_probe_handler()\n  {\n    if (!kernel_collector_->bpf_handler_) {\n      throw std::runtime_error(\"std::optional bpf_handler_ does not have a value\");\n    }\n    return kernel_collector_->bpf_handler_->probe_handler_;\n  }\n\n  uv_loop_t loop_;\n\n  std::optional<TestIntakeConfig> test_intake_config_;\n  std::optional<KernelCollector> kernel_collector_;\n\n  bool timeout_exceeded_ = false;\n  std::optional<StopWatch<>> stopwatch_;\n  std::unique_ptr<scheduling::Timer> stop_test_timer_;\n  std::unique_ptr<scheduling::Timer> start_workloads_timer_;\n\n  std::vector<std::thread> workload_threads_;\n  size_t workload_index_ = 0;\n  std::atomic<size_t> num_remaining_workloads_ = std::numeric_limits<size_t>::max();\n  std::vector<std::function<void()>> workloads_;\n\n  std::optional<std::reference_wrapper<const StopConditions>> stop_conditions_;\n};\n\n// clang-format off\n#define NAMES_AND_COUNTS_COMMON      \\\n  {\"bpf_compiled\", 1},               \\\n  {\"begin_telemetry\", 1},            \\\n  {\"close_sock_info\", 100},          \\\n  {\"cloud_platform\", 1},             \\\n  {\"dns_response\", 10},              \\\n  {\"http_response\", 10},             \\\n  {\"metadata_complete\", 1},          \\\n  {\"new_sock_info\", 100},            \\\n  {\"os_info\", 1},                    \\\n  {\"pid_close_info\", 5},             \\\n  {\"pid_info_create\", 5},            \\\n  {\"pid_set_comm\", 5},               \\\n  {\"process_steady_state\", 1},       \\\n  {\"set_cgroup\", 5},                 \\\n  {\"set_command\", 5},                \\\n  {\"set_config_label\", 1},           \\\n  {\"set_node_info\", 1},              \\\n  {\"set_tgid\", 5},                   \\\n  {\"socket_stats\", 100},             \\\n  {\"socket_steady_state\", 1},\n// clang-format on\n\n// Test basic kernel-collector functionality, validating that a minimum number of messages are seen as expected from running\n// process and network workloads.\nTEST_F(KernelCollectorTest, binary)\n{\n  StopConditions stop_conditions{\n      .timeout_sec = std::chrono::seconds(60),\n      .num_sends = 25,\n      .names_and_counts = {NAMES_AND_COUNTS_COMMON},\n      .wait_for_all_workloads_to_complete = true};\n\n  add_workload_processes();\n  add_workload_curl_otel();\n  add_workload_curl_localhost();\n\n  start_kernel_collector(IntakeEncoder::binary, stop_conditions, BPF_DUMP_FILE);\n}\n\n// This test was originally used to reproduce a race condition in the eBPF code that handles socket close events that would\n// cause extraneous bpf_log messages by running a socket stress workload.\nTEST_F(KernelCollectorTest, bpf_log)\n{\n  StopConditions stop_conditions{\n      .timeout_sec = std::chrono::minutes(10),\n      .num_sends = 500,\n      .names_and_counts = {},\n      .wait_for_all_workloads_to_complete = true};\n\n  add_workload_stress_ng_sock();\n\n  // This will be called for each render message sent from the kernel-collector to 'ingest' (by the reducer in a real system)\n  auto ingest_msg_cb = [&](nlohmann::json const &object) {\n    SCOPED_TIMING(BpfLogTestIngestMsgCb);\n    // Log any bpf_log messages so they are visible in CI output.\n    if (object[\"name\"] == \"bpf_log\") {\n      LOG::error(\"bpf_log: {}\", log_waive(object.dump()));\n    }\n  };\n\n  start_kernel_collector(IntakeEncoder::binary, stop_conditions, BPF_DUMP_FILE, ingest_msg_cb);\n}\n"
  },
  {
    "path": "collector/kernel/kernel_collector_test_docker/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nFROM docker.io/bitnami/minideb:trixie@sha256:0766a3b76750541ae2c5f3b806e5201e1b2ca1549a0a036a057726dc9e5dfa4d\n\n# ca-certificates are required by libcurl\nRUN install_packages ca-certificates libcurlpp0 libabsl20240722 libelf1\nENV SSL_CERT_DIR=/etc/ssl/certs\n\nENV EBPF_NET_INSTALL_DIR=/srv\nENV EBPF_NET_HOST_DIR=/hostfs\nENV EBPF_NET_DATA_DIR=/var/run/ebpf_net\n\nENTRYPOINT [ \"/srv/entrypoint-kct.sh\" ]\n\nRUN install_packages stress-ng\n\nRUN install_packages bc cgdb gawk gdb gzip iputils-ping jq netcat-openbsd procps python3 ripgrep vim valgrind curl\n\nCOPY srv /srv\nWORKDIR /srv\n"
  },
  {
    "path": "collector/kernel/kernel_symbols.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"kernel_symbols.h\"\n\n#include <util/string_view.h>\n\n#include <fstream>\n#include <stdexcept>\n#include <string>\n\nnamespace {\n\ninline std::string_view parse_symbol_name(std::string_view s)\n{\n  static constexpr std::string_view delimiters = \" \\t\";\n\n  // Skip two tokens (address and type).\n  for (size_t i = 0; i < 2; ++i) {\n    auto p = s.find_first_of(delimiters);\n\n    if (p == std::string_view::npos) {\n      return std::string_view();\n    }\n\n    s = views::ltrim_ws(s.substr(p));\n  }\n\n  // Return the next token (symbol name).\n  return s.substr(0, s.find_first_of(delimiters));\n}\n\n} // namespace\n\nKernelSymbols read_proc_kallsyms(std::istream &stream)\n{\n  KernelSymbols symbols;\n\n  while (stream.good()) {\n    std::string line;\n    std::getline(stream, line);\n\n    if (line.empty()) {\n      continue;\n    }\n\n    std::string_view symbol = parse_symbol_name(line);\n    if (symbol.empty()) {\n      throw std::runtime_error(\"parse error\");\n    }\n\n    symbols.insert(std::string(symbol));\n  }\n\n  return symbols;\n}\n\nKernelSymbols read_proc_kallsyms(const char *path)\n{\n  std::ifstream file(path);\n\n  if (!file.is_open()) {\n    throw std::system_error(errno, std::generic_category(), \"error opening file\");\n  }\n\n  return read_proc_kallsyms(file);\n}\n"
  },
  {
    "path": "collector/kernel/kernel_symbols.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <unordered_set>\n\n#include <istream>\n#include <string>\n#include <string_view>\n\nusing KernelSymbols = std::unordered_set<std::string>;\n\n// Reads and parses kernel symbol names.\n// Throws an exception on parsing error.\nKernelSymbols read_proc_kallsyms(std::istream &stream);\n\n// Reads kernel symbol names from a special file in the proc filesystem.\n// Throws an exception if the file can not be read and its content parsed.\nKernelSymbols read_proc_kallsyms(const char *path = \"/proc/kallsyms\");\n"
  },
  {
    "path": "collector/kernel/kernel_symbols_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"kernel_symbols.h\"\n\n#include <sstream>\n#include <stdexcept>\n#include <string_view>\n\n#include <gtest/gtest.h>\n\nstatic constexpr std::string_view EXAMPLE_KALLSYMS = R\"delim(\n0000000000000000 T startup_64\n0000000000000000 T _stext\n0000000000000000 T _text\n0000000000000000 T secondary_startup_64\n0000000000000000 T secondary_startup_64_no_verify\n0000000000000000 t verify_cpu\n0000000000000000 T sev_verify_cbit\n0000000000000000 T start_cpu0\n0000000000000000 T __startup_64\n0000000000000000 T startup_64_setup_env\n0000000000000000 b ignore_oc\t[ehci_hcd]\n0000000000000000 t iso_stream_find\t[ehci_hcd]\n0000000000000000 r smask_out.94\t[ehci_hcd]\n0000000000000000 d __UNIQUE_ID_ddebug215.19 \t[ehci_hcd]\n0000000000000000 d __UNIQUE_ID_ddebug121.49 \t[ehci_hcd]\n0000000000000000 t ehci_run.cold\t[ehci_hcd]\nffffffffc09a60cc r __ksymtab_nf_conntrack_alter_reply\t[nf_conntrack]\nffffffffc09aa74e r __kstrtab_nf_conntrack_alter_reply\t[nf_conntrack]\nffffffffc0996bc0 t nf_conntrack_alter_reply\t[nf_conntrack]\nffffffffc09cdbf0 t ctnetlink_dump_tuples_proto\t[nf_conntrack_netlink]\nffffffffc09ce230 t ctnetlink_dump_tuples_ip\t[nf_conntrack_netlink]\n)delim\";\n\nstatic constexpr std::string_view UNKNOWN_SYMBOL = \"DEFINITELY_NOT_A_KERNEL_SYMBOL\";\n\nTEST(KernelSymbolsTest, ReadStream)\n{\n  std::stringstream stream(EXAMPLE_KALLSYMS.data());\n\n  auto ks = read_proc_kallsyms(stream);\n\n  EXPECT_TRUE(ks.contains(\"verify_cpu\"));\n  EXPECT_TRUE(ks.contains(\"nf_conntrack_alter_reply\"));\n  EXPECT_TRUE(ks.contains(\"ctnetlink_dump_tuples_ip\"));\n  EXPECT_TRUE(ks.contains(\"iso_stream_find\"));\n\n  EXPECT_FALSE(ks.contains(UNKNOWN_SYMBOL.data()));\n}\n\nTEST(KernelSymbolsTest, ReadProcKallsyms)\n{\n  auto ks = read_proc_kallsyms();\n\n  EXPECT_TRUE(ks.contains(\"security_sk_free\"));\n  EXPECT_TRUE(ks.contains(\"inet_release\"));\n  EXPECT_TRUE(ks.contains(\"tcp_connect\"));\n  EXPECT_TRUE(ks.contains(\"inet_csk_listen_start\"));\n  EXPECT_TRUE(ks.contains(\"tcp_init_sock\"));\n  EXPECT_TRUE(ks.contains(\"inet_csk_accept\"));\n  EXPECT_TRUE(ks.contains(\"udp_v4_get_port\"));\n  EXPECT_TRUE(ks.contains(\"udp_v6_get_port\"));\n  EXPECT_TRUE(ks.contains(\"tcp4_seq_show\"));\n  EXPECT_TRUE(ks.contains(\"tcp6_seq_show\"));\n  EXPECT_TRUE(ks.contains(\"udp4_seq_show\"));\n  EXPECT_TRUE(ks.contains(\"udp6_seq_show\"));\n\n  EXPECT_TRUE(ks.contains(\"taskstats_exit\"));\n  EXPECT_TRUE(ks.contains(\"cgroup_exit\"));\n  EXPECT_TRUE(ks.contains(\"cgroup_attach_task\"));\n  EXPECT_TRUE(ks.contains(\"wake_up_new_task\"));\n  EXPECT_TRUE(ks.contains(\"__set_task_comm\"));\n  EXPECT_TRUE(ks.contains(\"get_pid_task\"));\n\n  EXPECT_FALSE(ks.contains(UNKNOWN_SYMBOL.data()));\n}\n\nTEST(KernelSymbolsTest, NoSuchFile)\n{\n  EXPECT_THROW(read_proc_kallsyms(\"/NO_SUCH_FILE\"), std::system_error);\n}\n\nTEST(KernelSymbolsTest, ParseError)\n{\n  std::stringstream stream(\"foo bar\");\n  EXPECT_THROW(read_proc_kallsyms(stream), std::runtime_error);\n}\n\nTEST(KernelSymbolsTest, NoParseError)\n{\n  std::stringstream stream(\"foo bar baz\");\n  EXPECT_NO_THROW(read_proc_kallsyms(stream));\n}\n\nTEST(KernelSymbolsTest, Empty)\n{\n  std::stringstream stream(\"\\n\\n\\n\");\n  KernelSymbols ks;\n\n  EXPECT_NO_THROW(ks = read_proc_kallsyms(stream));\n\n  EXPECT_TRUE(ks.empty());\n}\n"
  },
  {
    "path": "collector/kernel/main.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\nextern \"C\" int otn_kernel_collector_main(int argc, const char **argv);\n\nint main(int argc, char *argv[])\n{\n  return otn_kernel_collector_main(argc, const_cast<const char **>(argv));\n}\n"
  },
  {
    "path": "collector/kernel/nat_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include <collector/kernel/nat_handler.h>\n\n#include <collector/agent_log.h>\n\n#include <util/ip_address.h>\n#include <util/log.h>\n\nconstexpr auto SIZEOF_STRUCT_NF_CONNTRACK_TUPLE_HASH = 56;\n\nNatHandler::NatHandler(::ebpf_net::ingest::Writer &writer, logging::Logger &log) : writer_(writer), log_(log) {}\n\n/* END */\nvoid NatHandler::handle_nf_nat_cleanup_conntrack(u64 timestamp, struct jb_agent_internal__nf_nat_cleanup_conntrack *msg)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::handle_nf_nat_cleanup_conntrack: ct={}, \"\n        \"src={}:{}, dst={}:{}, proto={}\",\n        msg->ct,\n        IPv4Address::from(msg->src_ip),\n        ntohs(msg->src_port),\n        IPv4Address::from(msg->dst_ip),\n        ntohs(msg->dst_port),\n        msg->proto);\n  }\n\n  const hostport_tuple ft = {\n      msg->src_ip,\n      msg->dst_ip,\n      msg->src_port,\n      msg->dst_port,\n      msg->proto,\n  };\n\n  remove_nat(ft);\n}\n\n/* START */\nvoid NatHandler::handle_nf_conntrack_alter_reply(u64 timestamp, struct jb_agent_internal__nf_conntrack_alter_reply *msg)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::handle_nf_conntrack_alter_reply: ct={}, \"\n        \"src={}:{}, dst={}:{}, proto={}, \"\n        \"nat_src={}:{}, nat_dst={}:{}, nat_proto={}\",\n        msg->ct,\n        IPv4Address::from(msg->src_ip),\n        ntohs(msg->src_port),\n        IPv4Address::from(msg->dst_ip),\n        ntohs(msg->dst_port),\n        msg->proto,\n        IPv4Address::from(msg->nat_src_ip),\n        ntohs(msg->nat_src_port),\n        IPv4Address::from(msg->nat_dst_ip),\n        ntohs(msg->nat_dst_port),\n        msg->nat_proto);\n  }\n\n  const hostport_tuple map_from = {\n      .src_ip = msg->src_ip,\n      .dst_ip = msg->dst_ip,\n      .src_port = msg->src_port,\n      .dst_port = msg->dst_port,\n      .proto = msg->proto,\n  };\n\n  const hostport_tuple map_to = {\n      .src_ip = msg->nat_src_ip,\n      .dst_ip = msg->nat_dst_ip,\n      .src_port = msg->nat_src_port,\n      .dst_port = msg->nat_dst_port,\n      .proto = msg->proto,\n  };\n\n  record_nat(map_from, map_to);\n\n  // If we've seen an sk for this four-tuple already, we can report to the\n  // server\n  if (auto sk_pos = existing_sk_table_.find(map_from); sk_pos != existing_sk_table_.end()) {\n    u64 sk = sk_pos->second;\n    send_nat_remapping(timestamp, sk, map_to);\n  } else {\n    LOG::trace_in(AgentLogKind::NAT, \"sk doesn't exist for this four-tuple yet\");\n  }\n}\n\n/* EXISTING */\nvoid NatHandler::handle_existing_conntrack_tuple(u64 timestamp, struct jb_agent_internal__existing_conntrack_tuple *msg)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::handle_existing_conntrack_tuple: ct={}, dir={}, \"\n        \"src={}:{}, dst={}:{}, proto={}\",\n        msg->ct,\n        msg->dir,\n        IPv4Address::from(msg->src_ip),\n        ntohs(msg->src_port),\n        IPv4Address::from(msg->dst_ip),\n        ntohs(msg->dst_port),\n        msg->proto);\n  }\n\n  const u64 ct = msg->ct;\n  const u8 dir = msg->dir;\n\n  const hostport_tuple ft = {\n      msg->src_ip,\n      msg->dst_ip,\n      msg->src_port,\n      msg->dst_port,\n      msg->proto,\n  };\n\n  // We have two cases here: either the direction is 0 or 1\n  // (IP_CT_DIR_ORIGINAL/IP_CT_DIR_REPLY). Because of the way this probe is\n  // triggered, we expect the incoming msgs to be ordered as alternating 0,1\n  // pairs, where every pair corresponds to a given connection. dir=O is the\n  // first side we see for a connection, so we simply record the four-tuple.\n  // dir=1 should always come after 0, so we check to make sure that the\n  // corresponding dir=0 info is present in our table, and then we record the\n  // connection info if this pair is a NAT.\n  if (dir == 0) {\n    existing_conntrack_table_[ct] = ft;\n    return;\n  }\n\n  // in the dir==1 case, we reported \"ct-sizeof(struct\n  // nf_conntrack_tuple_hash)\", which should be the same addr as the\n  // corresponding dir==0 conntrack_tuple.\n  auto search = existing_conntrack_table_.find(ct);\n  if (search == existing_conntrack_table_.end()) {\n    log_.error(\"existing conntrack not found\");\n    return;\n  }\n\n  hostport_tuple const &map_from = search->second;\n  hostport_tuple const &map_to = ft;\n\n  // Check whether this is a NAT-ed connection\n  if (map_from == map_to) {\n    return;\n  }\n\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"connection is NAT-ed from: src={}:{}, dst={}:{}, proto={}\",\n        IPv4Address::from(map_from.src_ip),\n        ntohs(map_from.src_port),\n        IPv4Address::from(map_from.dst_ip),\n        ntohs(map_from.dst_port),\n        map_from.proto);\n  }\n\n  record_nat(map_from, map_to);\n\n  // Clean up the other direction for this connection\n  if (auto revct_it = existing_conntrack_table_.find(ct - SIZEOF_STRUCT_NF_CONNTRACK_TUPLE_HASH);\n      revct_it != existing_conntrack_table_.end()) {\n    existing_conntrack_table_.erase(revct_it);\n  } else {\n    LOG::debug_in(AgentLogKind::NAT, \"reverse direction not found for ct={}\", ct);\n  }\n}\n\nvoid NatHandler::handle_set_state_ipv4(u64 timestamp, jb_agent_internal__set_state_ipv4 *msg)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::handle_set_state_ipv4: \"\n        \"sk={}, src={}:{}, dest={}:{}, tx_rx={}\",\n        msg->sk,\n        IPv4Address::from(msg->src),\n        msg->sport,\n        IPv4Address::from(msg->dest),\n        msg->dport,\n        msg->tx_rx);\n  }\n\n  const u64 sk = msg->sk;\n\n  const hostport_tuple ft = {\n      .src_ip = msg->src,\n      .dst_ip = msg->dest,\n      .src_port = htons(msg->sport),\n      .dst_port = htons(msg->dport),\n      .proto = IPPROTO_TCP,\n  };\n\n  record_sk(sk, ft);\n\n  // We had a NAT table entry before getting the socket info.\n  if (auto mapping = nat_table_.find(ft); mapping != nat_table_.end()) {\n    send_nat_remapping(timestamp, sk, mapping->second);\n  }\n\n  if (auto rev_mapping = nat_table_rev_.find(ft.reversed()); rev_mapping != nat_table_rev_.end()) {\n    send_nat_remapping(timestamp, sk, rev_mapping->second.reversed());\n  }\n}\n\nvoid NatHandler::handle_set_state_ipv6(u64 timestamp, jb_agent_internal__set_state_ipv6 *msg)\n{\n  // Check if it is an ipv4 address\n  IPv6Address src = IPv6Address::from(msg->src);\n  IPv6Address dst = IPv6Address::from(msg->dest);\n\n  if (!src.is_ipv4() || !dst.is_ipv4()) {\n    if (is_log_whitelisted(AgentLogKind::NAT)) {\n      LOG::trace_in(\n          AgentLogKind::NAT,\n          \"NatHandler::handle_set_state_ipv6: \"\n          \"not a v4 address - skipping nat handling sk={}, src={}:{}, dest={}:{}, tx_rx={}\",\n          msg->sk,\n          src,\n          msg->sport,\n          dst,\n          msg->dport,\n          msg->tx_rx);\n    }\n    return;\n  }\n\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::handle_set_state_ipv6: \"\n        \"sk={}, src={}:{}, dest={}:{}, tx_rx={}\",\n        msg->sk,\n        src.to_ipv4().value(),\n        msg->sport,\n        dst.to_ipv4().value(),\n        msg->dport,\n        msg->tx_rx);\n  }\n\n  const u64 sk = msg->sk;\n\n  const hostport_tuple ft = {\n      .src_ip = src.to_ipv4().value().as_int(),\n      .dst_ip = dst.to_ipv4().value().as_int(),\n      .src_port = htons(msg->sport),\n      .dst_port = htons(msg->dport),\n      .proto = IPPROTO_TCP,\n  };\n\n  record_sk(sk, ft);\n\n  // We had a NAT table entry before getting the socket info.\n  if (auto mapping = nat_table_.find(ft); mapping != nat_table_.end()) {\n    send_nat_remapping(timestamp, sk, mapping->second);\n  }\n\n  if (auto rev_mapping = nat_table_rev_.find(ft.reversed()); rev_mapping != nat_table_rev_.end()) {\n    send_nat_remapping(timestamp, sk, rev_mapping->second.reversed());\n  }\n}\n\nvoid NatHandler::handle_close_socket(u64 timestamp, jb_agent_internal__close_sock_info *msg)\n{\n  remove_sk(msg->sk);\n}\n\nvoid NatHandler::record_sk(u64 sk, hostport_tuple const &ft)\n{\n  // We were hitting the assert in remove_sk(), which happens if two sk's use\n  // the same four-tuple without a call to remove_sk() in-between.\n  if (auto search = existing_sk_table_.find(ft); search != existing_sk_table_.end()) {\n    const auto &existing_sk = search->second;\n    LOG::debug_in(\n        AgentLogKind::NAT,\n        \"NatHandler::record_sk: rewriting existing ft->sk mapping: \"\n        \"sk={}, existing_sk={}, ft=({}:{},{}:{})\",\n        sk,\n        existing_sk,\n        IPv4Address::from(ft.src_ip),\n        ntohs(ft.src_port),\n        IPv4Address::from(ft.dst_ip),\n        ntohs(ft.dst_port));\n    remove_sk(existing_sk);\n  }\n\n  // There was also an edge-case where we'd have a memory leak of the same sk\n  // gets used for two-dfferent four-tuples without a call to remove_sk()\n  // in-between.\n  if (auto rev_search = existing_sk_table_rev_.find(sk); rev_search != existing_sk_table_rev_.end()) {\n    const auto &existing_ft = rev_search->second;\n    LOG::debug_in(\n        AgentLogKind::NAT,\n        \"NatHandler::record_sk: rewriting existing sk->ft mapping: \"\n        \"sk={}, existing_ft={{}:{},{}:{}), ft=({}:{},{}:{})\",\n        sk,\n        IPv4Address::from(existing_ft.src_ip),\n        ntohs(existing_ft.src_port),\n        IPv4Address::from(existing_ft.dst_ip),\n        ntohs(existing_ft.dst_port),\n        IPv4Address::from(ft.src_ip),\n        ntohs(ft.src_port),\n        IPv4Address::from(ft.dst_ip),\n        ntohs(ft.dst_port));\n    remove_sk(sk);\n  }\n\n  existing_sk_table_[ft] = sk;\n  existing_sk_table_rev_[sk] = ft;\n}\n\nvoid NatHandler::remove_sk(u64 sk)\n{\n  auto rev_search = existing_sk_table_rev_.find(sk);\n  if (rev_search == existing_sk_table_rev_.end()) {\n    // TODO: this happens pretty frequently. Initially this would happen in the\n    // gap between the end and set_state probes. However, this would also happen\n    // if any socket closes without reaching established as well.\n    return;\n  }\n  auto search = existing_sk_table_.find(rev_search->second);\n  assert(search != existing_sk_table_.end()); // tuple should be in existing_sk_table_\n\n  existing_sk_table_rev_.erase(rev_search);\n  existing_sk_table_.erase(search);\n}\n\nvoid NatHandler::record_nat(hostport_tuple const &map_from, hostport_tuple const &map_to)\n{\n  // Clean up possible previous records.\n  //\n  if (auto it = nat_table_.find(map_from); it != nat_table_.end()) {\n    // map_from->some_to exist, remove some_to->map_from\n    LOG::debug_in(\n        AgentLogKind::NAT,\n        \"NatHandler::record_nat: rewriting existing mapping: \"\n        \"({}:{},{}:{})->({}:{},{}:{}) with \"\n        \"({}:{},{}:{})->({}:{},{}:{})\",\n        IPv4Address::from(map_from.src_ip),\n        ntohs(map_from.src_port),\n        IPv4Address::from(map_from.dst_ip),\n        ntohs(map_from.dst_port),\n        IPv4Address::from(it->second.src_ip),\n        ntohs(it->second.src_port),\n        IPv4Address::from(it->second.dst_ip),\n        ntohs(it->second.dst_port),\n        IPv4Address::from(map_from.src_ip),\n        ntohs(map_from.src_port),\n        IPv4Address::from(map_from.dst_ip),\n        ntohs(map_from.dst_port),\n        IPv4Address::from(map_to.src_ip),\n        ntohs(map_to.src_port),\n        IPv4Address::from(map_to.dst_ip),\n        ntohs(map_to.dst_port));\n    nat_table_rev_.erase(it->second);\n  }\n  if (auto rev_it = nat_table_rev_.find(map_to); rev_it != nat_table_rev_.end()) {\n    // map_to->some_from exists, remove some_from->map_to\n    LOG::debug_in(\n        AgentLogKind::NAT,\n        \"NatHandler::record_nat: rewriting existing reverse mapping: \"\n        \"({}:{},{}:{})<-({}:{},{}:{}) with \"\n        \"({}:{},{}:{})<-({}:{},{}:{})\",\n        IPv4Address::from(rev_it->second.src_ip),\n        ntohs(rev_it->second.src_port),\n        IPv4Address::from(rev_it->second.dst_ip),\n        ntohs(rev_it->second.dst_port),\n        IPv4Address::from(map_to.src_ip),\n        ntohs(map_to.src_port),\n        IPv4Address::from(map_to.dst_ip),\n        ntohs(map_to.dst_port),\n        IPv4Address::from(map_from.src_ip),\n        ntohs(map_from.src_port),\n        IPv4Address::from(map_from.dst_ip),\n        ntohs(map_from.dst_port),\n        IPv4Address::from(map_to.src_ip),\n        ntohs(map_to.src_port),\n        IPv4Address::from(map_to.dst_ip),\n        ntohs(map_to.dst_port));\n    nat_table_.erase(rev_it->second);\n  }\n\n  nat_table_[map_from] = map_to;\n  nat_table_rev_[map_to] = map_from;\n}\n\nvoid NatHandler::remove_nat(hostport_tuple const &map_from)\n{\n  if (auto it = nat_table_.find(map_from); it != nat_table_.end()) {\n    const auto &map_to = it->second;\n    nat_table_rev_.erase(map_to);\n    nat_table_.erase(it);\n  }\n}\n\nhostport_tuple *NatHandler::get_nat_mapping(u32 src, u32 dst, u16 sport, u16 dport, u32 proto)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::get_nat_mapping: src={}:{}, dst={}:{}, proto={}\",\n        IPv4Address::from(src),\n        sport,\n        IPv4Address::from(dst),\n        dport,\n        proto);\n  }\n\n  const hostport_tuple ft = {\n      src,\n      dst,\n      htons(sport),\n      htons(dport),\n      proto,\n  };\n\n  if (auto it = nat_table_.find(ft); it != nat_table_.end()) {\n    LOG::trace_in(AgentLogKind::NAT, \"mapping found\");\n    return &(it->second);\n  } else {\n    LOG::trace_in(AgentLogKind::NAT, \"no mapping found\");\n    return nullptr;\n  }\n}\n\nvoid NatHandler::send_nat_remapping(u64 timestamp, u64 sk, hostport_tuple const &ft)\n{\n  if (is_log_whitelisted(AgentLogKind::NAT)) {\n    LOG::trace_in(\n        AgentLogKind::NAT,\n        \"NatHandler::send_nat_remapping: sk={}, src={}:{}, dst={}:{}\",\n        sk,\n        IPv4Address::from(ft.src_ip),\n        ntohs(ft.src_port),\n        IPv4Address::from(ft.dst_ip),\n        ntohs(ft.dst_port));\n  }\n\n  writer_.nat_remapping_tstamp(timestamp, sk, ft.src_ip, ft.dst_ip, ft.src_port, ft.dst_port);\n}\n"
  },
  {
    "path": "collector/kernel/nat_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <collector/kernel/hostport_tuple.h>\n\n#include <generated/ebpf_net/agent_internal/wire_message.h>\n#include <generated/ebpf_net/ingest/writer.h>\n#include <platform/platform.h>\n#include <util/logger.h>\n\n#include <absl/container/flat_hash_map.h>\n\nclass NatHandler {\npublic:\n  /**\n   * c'tor\n   */\n  NatHandler(::ebpf_net::ingest::Writer &writer, logging::Logger &log);\n\n  // end\n  void handle_nf_nat_cleanup_conntrack(u64 timestamp, struct jb_agent_internal__nf_nat_cleanup_conntrack *msg);\n  // start\n  void handle_nf_conntrack_alter_reply(u64 timestamp, struct jb_agent_internal__nf_conntrack_alter_reply *msg);\n  // existing\n  void handle_existing_conntrack_tuple(u64 timestamp, struct jb_agent_internal__existing_conntrack_tuple *msg);\n\n  void handle_set_state_ipv4(u64 timestamp, jb_agent_internal__set_state_ipv4 *msg);\n  void handle_set_state_ipv6(u64 timestamp, jb_agent_internal__set_state_ipv6 *msg);\n\n  void handle_close_socket(u64 timestamp, jb_agent_internal__close_sock_info *msg);\n\n  // Returns a pointer to the corresponding val for a key we lookup\n  // in the nat_table_. If there is no corresponding val, return nullptr.\n  hostport_tuple *get_nat_mapping(u32 src, u32 dst, u16 sport, u16 dport, u32 proto);\n\nprivate:\n  // Maps the IP_CT_DIR_ORIGINAL 4-tuple of a conntrack entry to the\n  // IP_CT_DIR_REPLY 4-tuple of a conntrack entry. Only contains NAT-ed\n  // connections\n  absl::flat_hash_map<hostport_tuple, hostport_tuple> nat_table_;\n\n  // Maps replies to originals.\n  absl::flat_hash_map<hostport_tuple, hostport_tuple> nat_table_rev_;\n\n  // Maps a struct nf_conntrack_tuple* to the corresponding 4-tuple.\n  // Used when we iterate over existing conntrack entries.\n  absl::flat_hash_map<u64, hostport_tuple> existing_conntrack_table_;\n\n  // Maps a 4-tuple to its corresponding sk. Used to keep track of\n  // existing sk's so we don't send NAT msgs for sk's we haven't\n  // seen a set_state_ipv4 msg for yet.\n  absl::flat_hash_map<hostport_tuple, u64> existing_sk_table_;\n\n  // A reverse index of our existing_sk_table_ so that we can do cleanup later.\n  absl::flat_hash_map<u64, hostport_tuple> existing_sk_table_rev_;\n\n  ::ebpf_net::ingest::Writer &writer_;\n  logging::Logger &log_;\n\n  // Adds the mapping between a socket and its (local,remote) tuple.\n  void record_sk(u64 sk, hostport_tuple const &ft);\n\n  // Removes an entry from our existing_sk_table_\n  void remove_sk(u64 sk);\n\n  // Adds the specified NAT mapping to internal tables.\n  void record_nat(hostport_tuple const &map_from, hostport_tuple const &map_to);\n\n  // Removes the specified NAT mapping from internal tables.\n  void remove_nat(hostport_tuple const &map_from);\n\n  // Sends the nat_remapping message to the specified socket.\n  void send_nat_remapping(u64 timestamp, u64 sk, hostport_tuple const &ft);\n};\n"
  },
  {
    "path": "collector/kernel/nat_prober.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/nat_prober.h>\n#include <collector/kernel/probe_handler.h>\n#include <ctime>\n#include <iostream>\n#include <linux/netfilter/nfnetlink.h>\n#include <linux/netfilter/nfnetlink_conntrack.h>\n#include <linux/netlink.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <util/log.h>\n\n#define RCV_BUFFSIZE 8192 // see libnfnetlink/include/libnfnetlink.h for NFNL_BUFFSIZE\n\nNatProber::NatProber(ProbeHandler &probe_handler, struct render_bpf_bpf *skel, std::function<void(void)> periodic_cb)\n    : periodic_cb_(periodic_cb)\n{\n  // END (cleanup): nf_ct_delete\n  probe_handler.start_probe(skel, \"on_nf_ct_delete\", \"nf_ct_delete\");\n  periodic_cb();\n\n  // START (create): __nf_conntrack_confirm entry and return\n  probe_handler.start_probe(skel, \"on___nf_conntrack_confirm\", \"__nf_conntrack_confirm\");\n  periodic_cb();\n  probe_handler.start_kretprobe(skel, \"onret___nf_conntrack_confirm\", \"__nf_conntrack_confirm\");\n  periodic_cb();\n\n  // EXISTING\n  ProbeAlternatives probe_alternatives{\n      \"ctnetlink_existing_conntrack\",\n      {\n          // nf_ct_port_tuple_to_nlattr is more widely available and has the same parameters as ctnetlink_dump_tuples\n          {\"on_ctnetlink_dump_tuples\", \"nf_ct_port_tuple_to_nlattr\"},\n          {\"on_ctnetlink_dump_tuples\", \"ctnetlink_dump_tuples\"},\n          // Attaching probe to ctnetlink_dump_tuples fails on some distros and kernel builds, for example Ubuntu Jammy.\n          {\"on_ctnetlink_dump_tuples\", \"ctnetlink_dump_tuples_ip\"},\n      }};\n  std::string ctnetlink_existing_k_func_name = probe_handler.start_probe(skel, probe_alternatives);\n\n  periodic_cb();\n  int res = query_kernel();\n  if (res != 0) {\n    if (res == EAGAIN || res == EWOULDBLOCK) {\n      LOG::warn(\n          \"While probing NAT, netfilter socket finished before seeing \"\n          \"NLMSG_DONE. {}\",\n          std::strerror(res));\n    } else {\n      LOG::error(\"NatProber::NatProber() - Error calling query_kernel(): {}\", std::strerror(res));\n    }\n  }\n  periodic_cb();\n\n  // Cleanup existing\n  probe_handler.cleanup_probe(ctnetlink_existing_k_func_name);\n  periodic_cb();\n}\n\n/* Creates a netlink socket and creates a request for the kernel to dump its\n * conntrack table information. Returns 0 on success and errno on failure.\n */\nint NatProber::query_kernel()\n{\n  // create a netlink socket\n  // domain: AF_NETLINK, type: SOCK_RAW | SOCK_NONBLOCK, protocol:\n  // NETLINK_NETFILTER\n  int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_NETFILTER);\n  if (sock_fd == -1) {\n    LOG::debug(\"NatProber::query_kernel() - Error opening netlink socket: {}\", std::strerror(errno));\n    return errno;\n  }\n\n  // Source addr info (where to bind)\n  struct sockaddr_nl src_addr;\n  memset(&src_addr, 0, sizeof(src_addr));\n  src_addr.nl_family = AF_NETLINK;\n  src_addr.nl_pid = getpid(); // self pid\n  src_addr.nl_groups = 0;     // unicast\n  int err = bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));\n  if (err == -1) {\n    int saved_errno = errno;\n    LOG::debug(\"NatProber::query_kernel() - Error binding netlink socket: {}\", std::strerror(saved_errno));\n    close(sock_fd);\n    return saved_errno;\n  }\n\n  // Dest addr_info (where to send)\n  struct sockaddr_nl dst_addr;\n  memset(&dst_addr, 0, sizeof(dst_addr));\n  dst_addr.nl_family = AF_NETLINK;\n  dst_addr.nl_pid = 0;    // for the linux kernel\n  dst_addr.nl_groups = 0; // unicast\n\n  // Setup a netlink/conntrack query message - this is based on the msg created\n  // in the conntrack tool. Specifically the codepath triggered by `sudo\n  // conntrack -L`\n  struct nlct_query_msg {\n    struct nlmsghdr nlh;\n    struct nfgenmsg nfmsg;\n    struct nfattr nfattr1;\n    u32 nfattr_data1;\n    struct nfattr nfattr2;\n    u32 nfattr_data2;\n  };\n  struct nlct_query_msg msg;\n  memset(&msg, 0, sizeof(msg));\n\n  // nlh\n  msg.nlh.nlmsg_len = sizeof(msg); // size of the entire msg\n  msg.nlh.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;\n  msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\n  msg.nlh.nlmsg_seq = (u32)std::time(nullptr); // current unix time\n  msg.nlh.nlmsg_pid = 0;                       // for the linux kernel\n\n  // nfmsg\n  msg.nfmsg.nfgen_family = AF_INET;\n  msg.nfmsg.version = NFNETLINK_V0;\n  msg.nfmsg.res_id = 0;\n\n  // nfattrs\n  msg.nfattr1.nfa_len = 0x08; // size of nfattr1 + nfattr_data1\n  msg.nfattr1.nfa_type = CTA_MARK;\n  msg.nfattr_data1 = 0;       // empty\n  msg.nfattr2.nfa_len = 0x08; // size of nfattr2 + nfattr_data2\n  msg.nfattr2.nfa_type = CTA_MARK_MASK;\n  msg.nfattr_data2 = 0; // empty\n\n  // Send msg\n  err = sendto(sock_fd, (void *)&msg, sizeof(msg), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));\n  if (err == -1) {\n    int saved_errno = errno;\n    LOG::debug(\"NatProber::query_kernel() - Error sending on netlink socket: {}\", std::strerror(saved_errno));\n    close(sock_fd);\n    return saved_errno;\n  }\n\n  // Receive responses\n  while (1) {\n    socklen_t addrlen = sizeof(dst_addr);\n    unsigned char buf[RCV_BUFFSIZE];\n    err = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr *)&dst_addr, &addrlen);\n    if (err == -1) {\n      int saved_errno = errno;\n      LOG::debug(\"NatProber::query_kernel() - Error receiving on netlink socket: {}\", std::strerror(saved_errno));\n      close(sock_fd);\n      return saved_errno;\n    }\n    // If no data was returned then we break - but this is an unexpected case,\n    // so log for debugging purposes.\n    if (err == 0) {\n      LOG::debug(\"NatProber::query_kernel() - recvfrom = 0\");\n      break;\n      ;\n    }\n    // Break once we're done receiving messages\n    // According to netlink_dump() inside of netlink/af_netlink.c, we expect the\n    // last msg in a dump to consist of only an nlmsghdr with a flag for\n    // NLMSG_DONE.\n    struct nlmsghdr *nlh = (struct nlmsghdr *)buf;\n    if (nlh->nlmsg_type == NLMSG_DONE) {\n      break;\n    }\n\n    // Ensure we handle perf events in a timely fashion\n    // else we run the risk of overflowing the event buffer\n    periodic_cb_();\n  }\n\n  // Cleanup\n  close(sock_fd);\n  return 0;\n}\n"
  },
  {
    "path": "collector/kernel/nat_prober.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <linux/bpf.h>\n\n#include <functional>\n#include <memory>\n#include <platform/types.h>\n\n// Forward declaration for the skeleton\nstruct render_bpf_bpf;\n\n/* forward declarations */\nclass ProbeHandler;\n\n/**\n * Adds BPF probes for nat translations of connections via the kernel's\n * conntrack tables\n */\nclass NatProber {\npublic:\n  /**\n   * C'tor\n   *\n   * @param probe_handler: a ProbeHandler where new probes can be registered\n   * @param bpf_module: the module from the bpf source code\n   * @param periodic_cb: a callback to be called every once in a while, to\n   *   allow user to e.g., flush rings\n   */\n  NatProber(ProbeHandler &probe_handler, struct render_bpf_bpf *skel, std::function<void(void)> periodic_cb);\n\nprivate:\n  /**\n   * Queries the kernel for conntrack info\n   */\n  int query_kernel();\n\n  std::function<void(void)> periodic_cb_;\n};\n"
  },
  {
    "path": "collector/kernel/perf_poller.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/perf_poller.h>\n\nPerfPoller::PerfPoller(PerfContainer &container) : container_(container) {}\n\nPerfPoller::~PerfPoller() {}\n\nvoid PerfPoller::start(u64 interval_useconds, u64 n_intervals)\n{\n  while (n_intervals) {\n    poll();\n    usleep(interval_useconds);\n    n_intervals--;\n  }\n}\n"
  },
  {
    "path": "collector/kernel/perf_poller.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <memory>\n\n#include <collector/kernel/perf_reader.h>\n#include <platform/platform.h>\n\nclass PerfPoller {\npublic:\n  /**\n   * c'tor\n   */\n  PerfPoller(PerfContainer &container);\n\n  /**\n   * d'tor\n   */\n  virtual ~PerfPoller();\n\n  /**\n   * poll method, to be implemented by child class.\n   *\n   * Can use container_->...\n   */\n  virtual void poll() = 0;\n\n  /**\n   * Perform @n_intervals pollings, every @interval_useconds microseconds\n   */\n  void start(u64 interval_useconds, u64 n_intervals);\n\nprotected:\n  PerfContainer &container_;\n};\n"
  },
  {
    "path": "collector/kernel/perf_reader.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/perf_reader.h>\n\n#include <algorithm>\n#include <linux/perf_event.h>\n#include <linux/unistd.h>\n#include <spdlog/fmt/fmt.h>\n#include <sstream>\n#include <stdexcept>\n#include <sys/ioctl.h>\n#include <sys/mman.h>\n#include <unistd.h>\n#include <util/perf_ring.h>\n\n#define __STRINGIZE(X) #X\n#define _STRINGIZE(X) __STRINGIZE(X)\n\nPerfContainer::PerfContainer() : n_entries_(0), readers_in_entries_(0) {}\n\nvoid PerfContainer::add_ring(PerfRing &pr)\n{\n  if (readers_.size() >= BPF_MAX_CPUS)\n    throw std::runtime_error(\"Only up to \" _STRINGIZE(BPF_MAX_CPUS) \" cpus are currently supported\");\n\n  readers_.push_back(pr);\n}\n\nvoid PerfContainer::add_data_ring(PerfRing &pr)\n{\n\n  if (data_readers_.size() >= BPF_MAX_CPUS)\n    throw std::runtime_error(\"Only up to \" _STRINGIZE(BPF_MAX_CPUS) \" cpus are currently supported\");\n\n  data_readers_.push_back(pr);\n}\n\nvoid PerfContainer::set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb)\n{\n  for (auto &reader : readers_) {\n    reader.set_callback(loop, ctx, cb);\n  }\n  for (auto &data_reader : data_readers_) {\n    data_reader.set_callback(loop, ctx, cb);\n  }\n}\n\nstd::string PerfContainer::inspect(void)\n{\n  std::string out;\n\n  size_t num_readers = readers_.size();\n  out += fmt::format(\"readers_: size={}\\n\", num_readers);\n  for (size_t n = 0; n < readers_.size(); n++) {\n    u32 total_bytes;\n    u32 bytes = readers_[n].bytes_remaining(&total_bytes);\n    out += fmt::format(\"  readers_[{}]: size={} ({}% full)\\n\", n, bytes, (((double)bytes) / (double)total_bytes) * 100.0);\n  }\n  out += fmt::format(\"entries_: n_entries_={}. readers_in_entries_={}\\n\", n_entries_, readers_in_entries_.to_string());\n  for (u32 n = 0; n < n_entries_; n++) {\n    out += fmt::format(\"  entries_[{}]={{timestamp {}, reader_index {}}}\", n, entries_[n].timestamp, entries_[n].reader_index);\n    n++;\n  }\n  return out;\n}\n\nPerfReader::PerfReader(PerfContainer &container, u64 max_timestamp)\n    : container_(container), max_timestamp_(max_timestamp), active_(true)\n{\n  for (size_t i = 0; i < container.readers_.size(); i++) {\n    auto &reader = container.readers_[i];\n    reader.start_read_batch();\n\n    auto &data_reader = container.data_readers_[i];\n    data_reader.start_read_batch();\n\n    /* if the reader is already in container.entries_, continue */\n    if (container.readers_in_entries_.test(i))\n      continue;\n\n    /* might need to add to container_.entries_. do so if required */\n    update_when_not_in_entries(i);\n  }\n}\n\nPerfReader::~PerfReader()\n{\n  stop();\n}\n\nbool PerfReader::empty()\n{\n  return (container_.n_entries_ == 0) || (container_.entries_[0].timestamp > max_timestamp_);\n}\n\nvoid PerfReader::pop_unpadded_and_copy_to(char *dest)\n{\n  auto &reader = top();\n  /* get length */\n  u32 length = reader.peek_size();\n  /* copy into buffer */\n  reader.peek_copy(dest, 0, length);\n  /* release the element */\n  reader.pop();\n  update_after_pop();\n}\n\nvoid PerfReader::pop_and_copy_to(char *dest)\n{\n  auto &reader = top();\n  /* get unpadded length */\n  u32 unpadded = reader.peek_aligned_u32(sizeof(u32));\n  /* copy into buffer */\n  reader.peek_copy(dest, sizeof(u64), unpadded);\n  /* release the element */\n  reader.pop();\n  update_after_pop();\n}\n\nvoid PerfReader::pop()\n{\n  auto &reader = top();\n  reader.pop();\n  update_after_pop();\n}\n\nvoid PerfReader::stop()\n{\n  if (!active_)\n    return;\n\n  for (auto &reader : container_.readers_)\n    reader.finish_read_batch();\n  for (auto &data_reader : container_.data_readers_)\n    data_reader.finish_read_batch();\n\n  active_ = false;\n}\n\nvoid PerfReader::update_after_pop()\n{\n  /* pop the entry in container_.entries_ */\n  size_t idx = container_.entries_[0].reader_index;\n  std::pop_heap(&container_.entries_[0], &container_.entries_[container_.n_entries_]);\n  container_.n_entries_--;\n  container_.readers_in_entries_.reset(idx);\n\n  update_when_not_in_entries(idx);\n}\n\nvoid PerfReader::update_when_not_in_entries(size_t idx)\n{\n  PerfRing &reader = container_.readers_[idx];\n\n  /* if the reader is empty, no need to add it */\n  int size = reader.peek_size();\n  if (size == -ENOENT)\n    return;\n\n  u64 timestamp = 0ull; /* PERF_RECORD_LOST is inserted as the minimum timestamp so it comes out first */\n  if (reader.peek_type() == PERF_RECORD_SAMPLE) {\n    /* will have 8 bytes for sample record, 8 bytes timestamp */\n    assert(size >= 16);\n    timestamp = reader.peek_aligned_u64(sizeof(u64));\n  }\n\n  /* add the timestamp */\n  container_.entries_[container_.n_entries_] = {.timestamp = timestamp, .reader_index = idx};\n  container_.n_entries_++;\n  std::push_heap(&container_.entries_[0], &container_.entries_[container_.n_entries_]);\n  container_.readers_in_entries_.set(idx);\n}\n"
  },
  {
    "path": "collector/kernel/perf_reader.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <bitset>\n#include <queue>\n#include <utility>\n#include <vector>\n\n#include <platform/platform.h>\n\n#include <collector/kernel/bpf_src/render_bpf.h>\n#include <util/perf_ring_cpp.h>\n\n/**\n * Container for multiple CPU readers.\n *\n * Accessing values is done through PerfReader. PerfReader starts and finishes\n *   read batches, and maintains the entries_ heap.\n *\n * If a reader is non-empty, PerfReader will populate an entry in entries_:\n *   * if the next entry is PERF_LOST_RECORD, the timestamp will be ~0ull\n *   * if the next entry is PERF_RECORD_SAMPLE, the timestamp will be the one\n *     encoded in the sample. This code assume that a sample starts with\n *      [ perf_event_header + u32 size + u32 unpadded_size + u64 timestamp ]\n */\nclass PerfContainer {\npublic:\n  /**\n   * C'tor\n   * @param cpus: the CPUs whose rings we should map\n   * @param n_pages: the number of data pages for in each ring.\n   */\n  PerfContainer();\n\n  /* disallow copy and assignment */\n  PerfContainer(const PerfContainer &) = delete;\n  void operator=(const PerfContainer &) = delete;\n\n  /**\n   * Add the control channel ring to the collection\n   *\n   * Throws if trying to add more than 64 elements\n   */\n  void add_ring(PerfRing &pr);\n\n  /**\n   * Add the data channel ring to the collection\n   *\n   * Throws if trying to add more than 64 elements\n   */\n  void add_data_ring(PerfRing &pr);\n\n  /**\n   * Set a callback to execute when events show up in the\n   * control channel perf ring\n   * Used if you are not polling for high-speed access\n   * Must be called only after all perf rings are added.\n   */\n  typedef void CALLBACK(void *ctx);\n  void set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb);\n\n  /**\n   *  Debugging routine to inspect the contents of the perf container\n   */\n  std::string inspect(void);\n\n  // returns the number of perf rings in this container\n  std::size_t size() const { return readers_.size(); }\n\n  // returns a reference to the i-th perf ring\n  PerfRing const &operator[](std::size_t i) const { return readers_[i]; }\n  PerfRing const &data_ring(std::size_t i) const { return data_readers_[i]; }\n  PerfRing &data_ring(std::size_t i) { return data_readers_[i]; }\n\nprivate:\n  friend class PerfReader;\n\n  /* Readers for each live CPU */\n  std::vector<PerfRing> readers_;\n  std::vector<PerfRing> data_readers_;\n\n  /* (timestamp, reader_index) pairs */\n  struct PerfEntry {\n    u64 timestamp;\n    size_t reader_index;\n    inline bool operator<(const PerfEntry &other) const\n    {\n      // Reverse the sense of the PerfEntry sort order\n      // this way, our entries_ heap puts the earliest timestamps first,\n      // making this a -min heap- instead of a -max heap-\n      return timestamp > other.timestamp;\n    }\n  };\n\n  PerfEntry entries_[BPF_MAX_CPUS];\n  size_t n_entries_;\n\n  /* bitmask: which readers from readers_ are already in entries_ */\n  std::bitset<BPF_MAX_CPUS> readers_in_entries_;\n};\n\n/**\n * Read sorted values from multiple queues.\n */\nclass PerfReader {\npublic:\n  /**\n   * C'tor\n   *\n   * Starts a sorted read operation.\n   */\n  PerfReader(PerfContainer &container, u64 max_timestamp);\n\n  /* D'tor */\n  ~PerfReader();\n\n  /**\n   * Returns true if there are no more events to read safely while keeping\n   *   sort.\n   */\n  bool empty();\n\n  /**\n   * Returns the number of bytes left to read of of the perf ring, and\n   * optionally the total size of the ring\n   */\n  inline u32 bytes_remaining(u32 *total_bytes);\n\n  /**\n   * Returns the type of the next value\n   *\n   * Assumes reader is not empty (i.e., !empty())\n   */\n  inline u32 peek_type() const { return top().peek_type(); }\n\n  /**\n   * Returns the total size of the next perf event\n   *\n   * Assumes reader is not empty (i.e., !empty())\n   */\n  inline u32 peek_size() { return top().peek_size(); }\n\n  /**\n   * Returns the length of the payload of the next value\n   *\n   * Assumes reader is not empty (i.e., !empty()) and type==PERF_RECORD_SAMPLE\n   */\n  inline u16 peek_unpadded_length() { return top().peek_aligned_u32(sizeof(u32)); }\n\n  /**\n   * Returns the length of the payload of the next value\n   *\n   * Assumes reader is not empty (i.e., !empty()) and type==PERF_RECORD_SAMPLE\n   */\n  inline u16 peek_rpc_id() { return top().peek_aligned_u16(2 * sizeof(u64)); }\n\n  /**\n   * Returns the number of lost samples, if type is PERF_RECORD_LOST\n   */\n  inline u64 peek_n_lost() { return top().peek_aligned_u64(sizeof(u64)); }\n\n  /**\n   * Returns a view into the sample's contents, without the perf event header.\n   *\n   * The messages are stored in a ring buffer, so if they're located at the end\n   * of the ring and wrap-around to the beginning, then the view will be spread\n   * over two chunks, in the order specified by `first` and `second` members of\n   * the returned pair. Otherwise, the view will have exactly one chunk\n   * represented by `first` in the returned pair and `second` will be empty.\n   *\n   * @assumes: `peek_type()` == `PERF_RECORD_SAMPLE`.\n   */\n  inline std::pair<std::string_view, std::string_view> peek_message() const\n  {\n    auto const &ring = top();\n    assert(ring.peek_type() == PERF_RECORD_SAMPLE);\n    return ring.peek();\n  }\n\n  /**\n   * Returns which cpu index we're reading from next\n   */\n\n  inline size_t peek_index() const\n  {\n    size_t idx = container_.entries_[0].reader_index;\n    return idx;\n  }\n\n  /**\n   * Copies the payload of a sample entry to the specified buffer.\n   *\n   * Assumes reader is not empty (i.e., !empty())\n   * Assumes type is PERF_RECORD_SAMPLE\n   * Assumes there is enough space in the destination (>= peek_sample_length())\n   */\n  void pop_and_copy_to(char *dest);\n\n  /**\n   * Copies the payload of a sample entry to the specified buffer.\n   * Uses the native size of the payload message instead of a header to\n   * determine the length Assumes there is no padding on the perf_submit side\n   *\n   * Assumes reader is not empty (i.e., !empty())\n   * Assumes type is PERF_RECORD_SAMPLE\n   * Assumes there is enough space in the destination (>= peek_sample_length())\n   */\n  void pop_unpadded_and_copy_to(char *dest);\n\n  /**\n   * Pops the entry, and updates data structures\n   *\n   * Assumes reader is not empty (i.e., !empty())\n   */\n  void pop();\n\n  /**\n   * Explicitly finish the batch\n   */\n  void stop();\n\nprivate:\n  /**\n   * Returns the reader with the smallest\n   */\n  inline PerfRing &top()\n  {\n    size_t idx = container_.entries_[0].reader_index;\n    return container_.readers_[idx];\n  }\n\n  inline PerfRing const &top() const\n  {\n    size_t idx = container_.entries_[0].reader_index;\n    return container_.readers_[idx];\n  }\n\n  /**\n   * Updates container_.entries_ and container_.readers_in_entries_\n   */\n  void update_after_pop();\n\n  /**\n   * Updates the reader's state given that it is not in container_.entries_\n   */\n  void update_when_not_in_entries(size_t idx);\n\n  /* container to read from */\n  PerfContainer &container_;\n\n  /* the maximum timestamp we should accept */\n  u64 max_timestamp_;\n\n  /* is the reader active */\n  bool active_;\n\n  /* This class acts as a \"Guard\", so disallow copy and assignment */\n  PerfReader(const PerfReader &) = delete;\n  void operator=(const PerfReader &) = delete;\n};\n"
  },
  {
    "path": "collector/kernel/probe_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <cstdarg>\n#include <iostream>\n\n#include <config.h>\n#include <linux/bpf.h>\n#include <util/log.h>\n\n#include <bpf/bpf.h>\n#include <bpf/libbpf.h>\n\n#include <collector/agent_log.h>\n#include <collector/kernel/bpf_src/render_bpf.h>\n#include <collector/kernel/probe_handler.h>\n\n// Include the generated skeleton\nextern \"C\" {\n#include \"generated/render_bpf.skel.h\"\n}\n\n#include <memory>\n#include <vector>\n\n#define EVENTS_PERF_RING_N_BYTES (1024 * 4096)\n#define EVENTS_PERF_RING_N_WATERMARK_BYTES (512 * 4096)\n#define DATA_CHANNEL_PERF_RING_N_BYTES (256 * 4096)\n#define DATA_CHANNEL_PERF_RING_N_WATERMARK_BYTES (1)\n\n// Helper function to get online CPUs\nstd::vector<int> get_online_cpus()\n{\n  std::vector<int> cpus;\n  int num_cpus = libbpf_num_possible_cpus();\n  for (int i = 0; i < num_cpus; i++) {\n    cpus.push_back(i);\n  }\n  return cpus;\n}\n\nProbeHandler::ProbeHandler(logging::Logger &log) : log_(log), num_failed_probes_(0), stack_trace_count_(0) {};\n\nvoid ProbeHandler::load_kernel_symbols()\n{\n  KernelSymbols ks;\n  try {\n    ks = read_proc_kallsyms();\n  } catch (std::exception &exc) {\n    log_.error(\"Failed to load kernel symbols: {}\", exc.what());\n    return;\n  }\n\n  if (!ks.empty()) {\n    LOG::info(\"Kernel symbols list loaded\");\n    kernel_symbols_ = std::move(ks);\n  }\n}\n\nvoid ProbeHandler::clear_kernel_symbols()\n{\n  kernel_symbols_.reset();\n}\n\nint ProbeHandler::setup_mmap(int cpu, int perf_fd, PerfContainer &perf, bool is_data, u32 n_bytes, u32 n_watermark_bytes)\n{\n  /* get mmap'd memory */\n  auto s = std::make_unique<MmapPerfRingStorage>(cpu, n_bytes, n_watermark_bytes);\n  int mmap_fd = s->fd();\n\n  /* add to perf container */\n  PerfRing ring(std::move(s));\n  if (is_data) {\n    perf.add_data_ring(ring);\n  } else {\n    perf.add_ring(ring);\n  }\n\n  /* set the file descriptor in the kernel */\n  int res = bpf_map_update_elem(perf_fd, static_cast<void *>(&cpu), static_cast<void *>(&mmap_fd), 0);\n  if (res < 0) {\n    LOG::error(\"cannot set perf fd {} for cpu {} to point to ring fd {}\", perf_fd, cpu, mmap_fd);\n    return -4;\n  }\n\n  return 0;\n}\n\nint ProbeHandler::get_bpf_map_fd(struct render_bpf_bpf *skel, const char *map_name)\n{\n  struct bpf_map *map = get_bpf_map(skel, map_name);\n  if (!map) {\n    LOG::error(\"Cannot get '{}' map\", map_name);\n    return -2;\n  }\n  int map_fd = bpf_map__fd(map);\n  if (map_fd < 0) {\n    LOG::error(\"'{}' map's fd<0: fd={}\", map_name, map_fd);\n    return -3;\n  }\n  return map_fd;\n}\n\n// Callback to route libbpf messages through our logging system\nstatic int libbpf_print_messages(enum libbpf_print_level level, const char *format, va_list args)\n{\n  char buffer[16 * 1024];\n  int len = vsnprintf(buffer, sizeof(buffer), format, args);\n\n  // Remove trailing newline if present\n  if (len > 0 && buffer[len - 1] == '\\n') {\n    buffer[len - 1] = '\\0';\n    len--;\n  }\n\n  std::string message(buffer);\n  if (level <= LIBBPF_INFO)\n    LOG::debug(\"libbpf: {}\", message);\n  else\n    LOG::trace(\"libbpf: {}\", message);\n\n  return len;\n}\n\nstruct render_bpf_bpf *ProbeHandler::open_bpf_skeleton()\n{\n  // Set a custom print callback to route libbpf messages through our logging system\n  libbpf_set_print(libbpf_print_messages);\n\n  struct render_bpf_bpf *skel = render_bpf_bpf__open();\n  if (!skel) {\n    LOG::error(\"Cannot open BPF skeleton\");\n    return nullptr;\n  }\n  return skel;\n}\n\nvoid ProbeHandler::configure_bpf_skeleton(struct render_bpf_bpf *skel, const BpfConfiguration &config)\n{\n  if (!skel) {\n    LOG::error(\"Cannot configure BPF skeleton: null skeleton\");\n    return;\n  }\n\n  // Configure global variables before loading\n  skel->rodata->boot_time_adjustment = config.boot_time_adjustment;\n  skel->rodata->filter_ns = config.filter_ns;\n  skel->rodata->enable_tcp_data_stream = config.enable_tcp_data_stream ? 1 : 0;\n\n  LOG::info(\n      \"BPF configuration: boot_time_adjustment={}, filter_ns={}, tcp_data_stream={}\",\n      config.boot_time_adjustment,\n      config.filter_ns,\n      config.enable_tcp_data_stream ? \"enabled\" : \"disabled\");\n}\n\nvoid ProbeHandler::destroy_bpf_skeleton(struct render_bpf_bpf *skel)\n{\n  if (skel) {\n    render_bpf_bpf__destroy(skel);\n  }\n}\n\nint ProbeHandler::load_bpf_skeleton(struct render_bpf_bpf *skel, PerfContainer &perf)\n{\n  int res = render_bpf_bpf__load(skel);\n  if (res != 0) {\n    LOG::error(\"Cannot load BPF skeleton, res={}\", res);\n    return res;\n  }\n  LOG::info(\"eBPF program successfully loaded\");\n\n  /* get events map descriptor */\n  int events_fd = get_bpf_map_fd(skel, \"events\");\n  if (events_fd < 0) {\n    return events_fd;\n  }\n\n  /* get data_channel map descriptor */\n  int data_channel_fd = get_bpf_map_fd(skel, \"data_channel\");\n  if (data_channel_fd < 0) {\n    return data_channel_fd;\n  }\n\n  /* get online cpus */\n  auto online_cpus = get_online_cpus();\n\n  /* open mmaps */\n  for (auto cpu : online_cpus) {\n    res = setup_mmap(cpu, events_fd, perf, false, EVENTS_PERF_RING_N_BYTES, EVENTS_PERF_RING_N_WATERMARK_BYTES);\n    if (res < 0) {\n      return res;\n    }\n    res =\n        setup_mmap(cpu, data_channel_fd, perf, true, DATA_CHANNEL_PERF_RING_N_BYTES, DATA_CHANNEL_PERF_RING_N_WATERMARK_BYTES);\n    if (res < 0) {\n      return res;\n    }\n  }\n\n  return 0;\n}\n\nstruct bpf_map *ProbeHandler::get_bpf_map(struct render_bpf_bpf *skel, const std::string &name)\n{\n  if (name == \"cgroup_exit_active\")\n    return skel->maps.cgroup_exit_active;\n  if (name == \"tcp_open_sockets\")\n    return skel->maps.tcp_open_sockets;\n  if (name == \"on_inet_csk_accept_active\")\n    return skel->maps.on_inet_csk_accept_active;\n  if (name == \"seen_inodes\")\n    return skel->maps.seen_inodes;\n  if (name == \"udp_get_port_hash\")\n    return skel->maps.udp_get_port_hash;\n  if (name == \"inet_release_active\")\n    return skel->maps.inet_release_active;\n  if (name == \"tail_calls\")\n    return skel->maps.tail_calls;\n  if (name == \"cgroup_control_active\")\n    return skel->maps.cgroup_control_active;\n  if (name == \"seen_conntracks\")\n    return skel->maps.seen_conntracks;\n  if (name == \"inet_csk_accept_active\")\n    return skel->maps.inet_csk_accept_active;\n  if (name == \"tcp_sendmsg_active\")\n    return skel->maps.tcp_sendmsg_active;\n  if (name == \"tcp_recvmsg_active\")\n    return skel->maps.tcp_recvmsg_active;\n  if (name == \"events\")\n    return skel->maps.events;\n  if (name == \"bpf_log_globals_per_cpu\")\n    return skel->maps.bpf_log_globals_per_cpu;\n  if (name == \"tgid_info_table\")\n    return skel->maps.tgid_info_table;\n  if (name == \"dead_group_tasks\")\n    return skel->maps.dead_group_tasks;\n  if (name == \"udp_open_sockets\")\n    return skel->maps.udp_open_sockets;\n  if (name == \"dns_message_array\")\n    return skel->maps.dns_message_array;\n  if (name == \"_tcp_connections\")\n    return skel->maps._tcp_connections;\n  if (name == \"_tcp_control\")\n    return skel->maps._tcp_control;\n  if (name == \"data_channel\")\n    return skel->maps.data_channel;\n  return nullptr;\n}\n\nint ProbeHandler::register_tail_call(\n    struct render_bpf_bpf *skel, const std::string &prog_array_name, int index, const std::string &func_name)\n{\n  struct bpf_map *prog_array = get_bpf_map(skel, prog_array_name);\n  if (!prog_array) {\n    log_.error(\"Failed to find prog array map {}\", prog_array_name);\n    ++num_failed_probes_;\n    return -1;\n  }\n\n  // TCP processor programs need to be looked up by function name\n  struct bpf_program *prog = bpf_object__find_program_by_name(skel->obj, func_name.c_str());\n  if (!prog) {\n    log_.error(\"Failed to register tail call for {}, could not find program\", func_name);\n    ++num_failed_probes_;\n    return -2;\n  }\n\n  int prog_fd = bpf_program__fd(prog);\n  if (prog_fd < 0) {\n    log_.error(\"Failed to register tail call for {}, could not get program fd\", func_name);\n    ++num_failed_probes_;\n    return -3;\n  }\n\n  int map_fd = bpf_map__fd(prog_array);\n  int ret = bpf_map_update_elem(map_fd, &index, &prog_fd, 0);\n  if (ret < 0) {\n    log_.error(\"Failed to update prog array for tail call {}, errno {}\", func_name, errno);\n    ++num_failed_probes_;\n    return -4;\n  }\n\n  tail_calls_.emplace_back(prog_array_name, func_name, prog_fd, index);\n  return 0;\n}\n\n#if DEBUG_ENABLE_STACKTRACE\n\nstd::string ProbeHandler::get_stack_trace(struct render_bpf_bpf *skel, s32 kernel_stack_id, s32 user_stack_id, u32 tgid)\n{\n  std::string out;\n\n  // TODO: Implement stack trace functionality for libbpf\n  // This requires additional implementation for stack trace handling\n  out += \"Stack trace functionality not yet implemented for libbpf\\n\";\n\n  stack_trace_count_++;\n  return out;\n}\n\n#endif\n\nint ProbeHandler::start_probe_common(\n    struct render_bpf_bpf *skel,\n    bool is_kretprobe,\n    const std::string &func_name,\n    const std::string &k_func_name,\n    const std::string &event_id_suffix)\n{\n  auto bpf_program = bpf_object__find_program_by_name(skel->obj, func_name.c_str());\n  if (!bpf_program) {\n    LOG::error(\"Could not get find program. func_name:{} k_func_name:{}\", func_name, k_func_name);\n    return -1;\n  }\n\n  /* attach the probe */\n  std::string probe_name = (is_kretprobe ? kretprobe_prefix_ : probe_prefix_) + k_func_name + event_id_suffix;\n\n  struct bpf_link *link;\n  link = bpf_program__attach_kprobe(\n      bpf_program,\n      is_kretprobe, /* retprobe */\n      k_func_name.c_str());\n\n  if (!link) {\n    LOG::debug_in(\n        AgentLogKind::BPF,\n        \"Unable to attach {}. probe_name:{} func_name:{} k_func_name:{} errno:{}\",\n        is_kretprobe ? \"kretprobe\" : \"kprobe\",\n        probe_name,\n        func_name,\n        k_func_name,\n        errno);\n    return -3;\n  }\n\n  // Store the link pointer for cleanup\n  probes_.push_back(link);\n  probe_names_.push_back(probe_name);\n  return 0;\n}\n\nint ProbeHandler::start_probe(\n    struct render_bpf_bpf *skel,\n    const std::string &func_name,\n    const std::string &k_func_name,\n    const std::string &event_id_suffix)\n{\n  auto ret = start_probe_common(skel, false, func_name, k_func_name, event_id_suffix);\n  if (ret != 0) {\n    log_.error(\"Failed to attach {} kprobe, error {}\", k_func_name, ret);\n    ++num_failed_probes_;\n  }\n  return ret;\n}\n\nint ProbeHandler::start_kretprobe(\n    struct render_bpf_bpf *skel,\n    const std::string &func_name,\n    const std::string &k_func_name,\n    const std::string &event_id_suffix)\n{\n  auto ret = start_probe_common(skel, true, func_name, k_func_name, event_id_suffix);\n  if (ret != 0) {\n    log_.error(\"Failed to attach {} kretprobe, error {}\", k_func_name, ret);\n    ++num_failed_probes_;\n  }\n  return ret;\n}\n\nstd::string ProbeHandler::start_probe_common(\n    struct render_bpf_bpf *skel,\n    bool is_kretprobe,\n    const ProbeAlternatives &probe_alternatives,\n    const std::string &event_id_suffix)\n{\n  size_t probe_num = 1;\n  size_t num_alternatives = probe_alternatives.func_names.size();\n  if (num_alternatives == 0) {\n    throw std::runtime_error(\"ProbeHandler:start_probe_common() no alternatives provided\");\n  }\n  for (const auto &func_and_kfunc : probe_alternatives.func_names) {\n    int ret = start_probe_common(skel, is_kretprobe, func_and_kfunc.func_name, func_and_kfunc.k_func_name, event_id_suffix);\n    if (ret == 0) {\n      LOG::debug_in(\n          AgentLogKind::BPF,\n          \"Successfully attached {} {}, alternative {} of {}, func_name={}, k_func_name={}\",\n          probe_alternatives.desc,\n          is_kretprobe ? \"kretprobe\" : \"kprobe\",\n          probe_num,\n          num_alternatives,\n          func_and_kfunc.func_name,\n          func_and_kfunc.k_func_name);\n      return func_and_kfunc.k_func_name;\n      break;\n    }\n    ++probe_num;\n  }\n  log_.error(\n      \"Failed to attach any {} {}, attempted {} alternatives\",\n      probe_alternatives.desc,\n      is_kretprobe ? \"kretprobe\" : \"kprobe\",\n      num_alternatives);\n  ++num_failed_probes_;\n  return std::string();\n}\n\nstd::string ProbeHandler::start_probe(\n    struct render_bpf_bpf *skel, const ProbeAlternatives &probe_alternatives, const std::string &event_id_suffix)\n{\n  return start_probe_common(skel, false, probe_alternatives, event_id_suffix);\n}\n\nstd::string ProbeHandler::start_kretprobe(\n    struct render_bpf_bpf *skel, const ProbeAlternatives &probe_alternatives, const std::string &event_id_suffix)\n{\n  return start_probe_common(skel, true, probe_alternatives, event_id_suffix);\n}\n\nvoid ProbeHandler::cleanup_probes()\n{\n  while (!probes_.empty()) {\n    auto probe = probes_.back();\n    probes_.pop_back();\n    std::string probe_name = probe_names_.back();\n    probe_names_.pop_back();\n\n    LOG::debug_in(AgentLogKind::BPF, \"cleanup probe for {}\", probe_name);\n\n    // Clean up the bpf_link\n    if (probe) {\n      bpf_link__destroy(probe);\n    }\n  }\n\n  LOG::debug_in(AgentLogKind::BPF, \"Done cleaning up probes\");\n}\n\nvoid ProbeHandler::cleanup_tail_calls(struct render_bpf_bpf *skel)\n{\n  while (!tail_calls_.empty()) {\n    const auto &tc = tail_calls_.back();\n\n    LOG::debug_in(AgentLogKind::BPF, \"cleanup tail call for {} from table {}\", tc.func_, tc.table_);\n\n    struct bpf_map *prog_array = get_bpf_map(skel, tc.table_);\n    if (prog_array) {\n      int map_fd = bpf_map__fd(prog_array);\n      bpf_map_delete_elem(map_fd, &tc.index_);\n    }\n\n    tail_calls_.pop_back();\n  }\n}\n\nvoid ProbeHandler::cleanup_probe_common(const std::string &probe_name)\n{\n  int i = 0;\n  for (std::vector<std::string>::iterator it = probe_names_.begin(); it != probe_names_.end(); ++it) {\n    if (!probe_names_[i].compare(probe_name)) {\n      auto probe = probes_[i];\n      std::string probe_name = probe_names_[i];\n\n      probes_.erase(probes_.begin() + i);\n      probe_names_.erase(probe_names_.begin() + i);\n\n      LOG::debug_in(AgentLogKind::BPF, \"cleanup probe for {}\", probe_name);\n\n      // Clean up the bpf_link\n      if (probe) {\n        bpf_link__destroy(probe);\n      }\n      return;\n    }\n    ++i;\n  }\n\n  log_.error(\"Error removing probe. {} was not found.\", probe_name);\n}\n\nvoid ProbeHandler::cleanup_probe(const std::string &k_func_name)\n{\n  if (k_func_name.empty())\n    return;\n\n  cleanup_probe_common(probe_prefix_ + k_func_name);\n}\n\nvoid ProbeHandler::cleanup_kretprobe(const std::string &k_func_name)\n{\n  if (k_func_name.empty())\n    return;\n\n  cleanup_probe_common(kretprobe_prefix_ + k_func_name);\n}\n"
  },
  {
    "path": "collector/kernel/probe_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"kernel_symbols.h\"\n\n#include <bpf/bpf.h>\n#include <bpf/libbpf.h>\n#include <linux/bpf.h>\n\n#include <collector/kernel/perf_reader.h>\n#include <util/logger.h>\n\n#include <optional>\n#include <string>\n#include <vector>\n\n// Forward declaration for the skeleton\nstruct render_bpf_bpf;\n\n/**\n * Configuration structure for BPF global variables\n * Used to configure the BPF program before loading\n */\nstruct BpfConfiguration {\n  u64 boot_time_adjustment = 0;\n  u64 filter_ns = 1000000000; // Default 1 second in nanoseconds\n  bool enable_tcp_data_stream = false;\n};\n\n/**\n * ProbeAlternatives encapsulates multiple alternatives to attempt when attaching a probe.  Alternatives may be needed due to\n * differences in kernel versions or builds.\n */\nstruct ProbeAlternatives {\n  struct FuncAndKfunc {\n    std::string func_name;\n    std::string k_func_name;\n  };\n\n  ProbeAlternatives(std::string desc, std::vector<FuncAndKfunc> func_names)\n      : desc(std::move(desc)), func_names(std::move(func_names))\n  {}\n\n  std::string desc;\n  std::vector<FuncAndKfunc> func_names;\n};\n\n/**\n * Handles the creation of probes and provides info for cleanup to signal\n * handler\n */\nclass ProbeHandler {\n  friend class KernelCollectorTest;\n\npublic:\n  /**\n   * c'tor\n   */\n  ProbeHandler(logging::Logger &log);\n\n  /**\n   * Loads the list of available kernel symbols from /proc/kallsyms.\n   * This list is then used to determine if a kernel function can be instrumented.\n   */\n  void load_kernel_symbols();\n\n  /**\n   * Clears the list of kernel symbols.\n   * Used to free up memory after all probes are started.\n   */\n  void clear_kernel_symbols();\n\n  struct render_bpf_bpf *open_bpf_skeleton();\n  void configure_bpf_skeleton(struct render_bpf_bpf *skel, const BpfConfiguration &config);\n  int load_bpf_skeleton(struct render_bpf_bpf *skel, PerfContainer &perf);\n  void destroy_bpf_skeleton(struct render_bpf_bpf *skel);\n\n  /**\n   * BPF table helpers\n   **/\n  struct bpf_map *get_bpf_map(struct render_bpf_bpf *skel, const std::string &name);\n\n  /**\n   * Register tail call in table\n   */\n  int register_tail_call(\n      struct render_bpf_bpf *skel, const std::string &prog_array_name, int index, const std::string &func_name);\n\n  /**\n   * Starts a kprobe\n   * on failure logs error and increments num_failed_probes_\n   * @returns 0 on success, negative value on failure\n   */\n  int start_probe(\n      struct render_bpf_bpf *skel,\n      const std::string &func_name,\n      const std::string &k_func_name,\n      const std::string &event_id_suffix = std::string());\n\n  /**\n   * Starts a kretprobe\n   * on failure logs error and increments num_failed_probes_\n   * @returns 0 on success, negative value on failure\n   */\n  int start_kretprobe(\n      struct render_bpf_bpf *skel,\n      const std::string &func_name,\n      const std::string &k_func_name,\n      const std::string &event_id_suffix = std::string());\n\n  /**\n   * Starts a kprobe from the provided alternatives.\n   * Probes are attempted in order until one succeeds.\n   * If all alternatives fail an error is logged and num_failed_probes_ is incremented.\n   * @returns string containing the k_func_name of the probe that was attached on success, empty string on failure\n   */\n  std::string start_probe(\n      struct render_bpf_bpf *skel,\n      const ProbeAlternatives &probe_alternatives,\n      const std::string &event_id_suffix = std::string());\n\n  /**\n   * Starts a kretprobe from the provided alternatives.\n   * Probes are attempted in order until one succeeds.\n   * If all alternatives fail an error is logged and num_failed_probes_ is incremented.\n   * @returns string containing the k_func_name of the probe that was attached on success, empty string on failure\n   */\n  std::string start_kretprobe(\n      struct render_bpf_bpf *skel,\n      const ProbeAlternatives &probe_alternatives,\n      const std::string &event_id_suffix = std::string());\n\n  /**\n   * Handles the cleanup of probes on exit\n   */\n  void cleanup_probes();\n\n  /**\n   * Clean up all the registered tail calls\n   */\n  void cleanup_tail_calls(struct render_bpf_bpf *skel);\n\n  /**\n   * Cleans up a single probe\n   */\n  void cleanup_probe(const std::string &k_func_name);\n\n  /**\n   * Cleans up a single kretprobe\n   */\n  void cleanup_kretprobe(const std::string &k_func_name);\n\n#if DEBUG_ENABLE_STACKTRACE\n  /**\n   * Gets a stack trace and removes it from the list\n   */\n  std::string get_stack_trace(struct render_bpf_bpf *skel, s32 kernel_stack_id, s32 user_stack_id, u32 tgid);\n#endif\n\nprotected:\n  /**\n   * Common code to start a kprobe or kretprobe\n   * @returns 0 on success, negative value on failure\n   */\n  int start_probe_common(\n      struct render_bpf_bpf *skel,\n      bool is_kretprobe,\n      const std::string &func_name,\n      const std::string &k_func_name,\n      const std::string &event_id_suffix);\n\n  /**\n   * Common code to start a kprobe or kretprobe from the provided alternatives\n   * @returns string containing the k_func_name of the probe that was attached on success, empty string on failure\n   */\n  std::string start_probe_common(\n      struct render_bpf_bpf *skel,\n      bool is_kretprobe,\n      const ProbeAlternatives &probe_alternatives,\n      const std::string &event_id_suffix = std::string());\n\n  /**\n   * Common code to clean up a single probe\n   */\n  void cleanup_probe_common(const std::string &probe_name);\n\n  /**\n   * Returns the file descriptor for a table declared in bpf\n   */\n  int get_bpf_map_fd(struct render_bpf_bpf *skel, const char *map_name);\n\n  /**\n   * Sets up memory mapping for perf rings\n   */\n  int setup_mmap(int cpu, int events_fd, PerfContainer &perf, bool is_data, u32 n_bytes, u32 n_watermark_bytes);\n\nprivate:\n  static constexpr char probe_prefix_[] = \"ebpf_net_p_\";\n  static constexpr char kretprobe_prefix_[] = \"ebpf_net_r_\";\n\n  logging::Logger &log_;\n\n  struct TailCallTuple {\n    TailCallTuple(const std::string &table, const std::string &func, int fd, int index)\n        : table_(table), func_(func), fd_(fd), index_(index)\n    {}\n    std::string table_;\n    std::string func_;\n    int fd_;\n    int index_;\n  };\n  std::vector<struct bpf_link *> probes_;\n  std::vector<TailCallTuple> tail_calls_;\n  std::vector<std::string> probe_names_;\n  size_t num_failed_probes_; // number of kprobes, kretprobes, and tail_calls that failed to attach\n  size_t stack_trace_count_;\n\n  std::optional<KernelSymbols> kernel_symbols_;\n};\n"
  },
  {
    "path": "collector/kernel/proc_cmdline.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"proc_cmdline.h\"\n\n#include <util/file_ops.h>\n\n#include <cerrno>\n#include <cstdio>\n#include <system_error>\n\n// Max number of characters we are willing to read from /proc/PID/cmdline.\nstatic constexpr size_t MAX_CMDLINE_READ_SIZE = 256;\n\nExpected<std::string, std::error_code> read_proc_cmdline(u32 pid)\n{\n  char path[64] = {0};\n\n  if (snprintf(path, sizeof(path), \"/proc/%d/cmdline\", pid) < 0) {\n    return {unexpected, std::error_code()};\n  }\n\n  FileDescriptor fd;\n\n  if (auto error = fd.open(path, FileDescriptor::Access::read_only, FileDescriptor::Positioning::beginning)) {\n    return {unexpected, std::move(error)};\n  }\n\n  std::string buffer(MAX_CMDLINE_READ_SIZE, '\\0');\n\n  if (auto const result = fd.read_all(buffer.data(), buffer.size())) {\n    buffer.resize(*result);\n    return std::move(buffer);\n  } else {\n    return {unexpected, result.error()};\n  }\n}\n\nExpected<std::string, std::error_code> try_read_proc_cmdline(u32 pid)\n{\n  auto r = read_proc_cmdline(pid);\n\n  if (!r) {\n    if ((r.error().category() == std::generic_category()) && ((r.error().value() == ENOENT) || (r.error().value() == ESRCH))) {\n      // no such process\n      return \"\";\n    }\n  }\n\n  return r;\n}\n"
  },
  {
    "path": "collector/kernel/proc_cmdline.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <util/expected.h>\n\n#include <string>\n#include <system_error>\n\n// Reads the process command-line from /proc/PID/cmdline.\nExpected<std::string, std::error_code> read_proc_cmdline(u32 pid);\n\n// Reads the process command-line from /proc/PID/cmdline.\n// On ENOENT (no such file or directory) error, returns an empty string\n// instead of an error code.\nExpected<std::string, std::error_code> try_read_proc_cmdline(u32 pid);\n"
  },
  {
    "path": "collector/kernel/proc_net_reader.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/proc_net_reader.h>\n#include <sstream>\n\nProcNetReader::ProcNetReader(std::string filename) : tcp_file_(filename), sk_ino_(0), sk_p_(0), sk_state_(0)\n{\n  // skip the first line of the file\n  std::string line;\n  getline(tcp_file_, line);\n}\n\nProcNetReader::~ProcNetReader()\n{\n  tcp_file_.close();\n}\n\nint ProcNetReader::get_ino()\n{\n  return sk_ino_;\n}\n\nunsigned long ProcNetReader::get_sk()\n{\n  return sk_p_;\n}\n\nint ProcNetReader::get_sk_state()\n{\n  return sk_state_;\n}\n\nint ProcNetReader::next()\n{\n  std::string line;\n  getline(tcp_file_, line);\n  if (line == \"\")\n    return 0;\n  std::istringstream issline(line);\n  std::string tk;\n  int tk_id = 0;\n  do {\n    issline >> tk;\n    tk_id++;\n    if (tk_id == 4) { // parse the state\n      sscanf(tk.c_str(), \"%x\", &sk_state_);\n    } else if (tk_id == 10) { // parse inode_number\n      sscanf(tk.c_str(), \"%d\", &sk_ino_);\n    } else if (tk_id == 12) { // parse sk pointer\n      sscanf(tk.c_str(), \"%lx\", &sk_p_);\n    } else if (tk_id > 12) { // done\n      break;\n    }\n  } while (issline);\n  return 1;\n}\n"
  },
  {
    "path": "collector/kernel/proc_net_reader.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <fstream>\n#include <string>\n\n/**\n * Reads through \"/proc/net/tcp\" and \"/proc/net/tcp6\"\n */\nclass ProcNetReader {\npublic:\n  /**\n   * c'tor\n   */\n  ProcNetReader(std::string filename);\n\n  /**\n   * d'tor\n   */\n  ~ProcNetReader();\n\n  /**\n   * Accessors for ino_, sk_, and sk_state_;\n   */\n  int get_ino();\n\n  unsigned long get_sk();\n\n  int get_sk_state();\n\n  //\n  // Read /proc//* for the next entry.\n  // Returns 1 if a process entry was found and 0 otherwise.\n  //\n  int next();\n\nprivate:\n  std::ifstream tcp_file_;\n  int sk_ino_;\n  unsigned long sk_p_;\n  int sk_state_;\n};\n"
  },
  {
    "path": "collector/kernel/proc_reader.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/proc_reader.h>\n#include <cstdio>\n#include <stdexcept>\n\nProcReader::ProcReader() : pid_(0)\n{\n  proc_ = opendir(\"/proc\");\n  if (proc_ == NULL)\n    throw std::runtime_error(\"ProcReader: Couldn't open proc.\");\n}\n\nProcReader::~ProcReader()\n{\n  closedir(proc_);\n}\n\nint ProcReader::get_pid()\n{\n  return pid_;\n}\n\nint ProcReader::is_pid()\n{\n  if (sscanf(proc_ent_->d_name, \"%d\", &pid_) != 1)\n    return 0; // exit if the current proc_ent_ isn't a pid directory\n  return 1;\n}\n\nint ProcReader::next()\n{\n  proc_ent_ = readdir(proc_);\n  if (proc_ent_ == 0)\n    return 0;\n  return 1;\n}\n"
  },
  {
    "path": "collector/kernel/proc_reader.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <dirent.h>\n/**\n * Reads through proc\n */\nclass ProcReader {\npublic:\n  /**\n   * c'tor\n   * throws if buff_ can't be malloc-ed\n   */\n  ProcReader();\n\n  /**\n   * d'tor\n   */\n  ~ProcReader();\n\n  /**\n   * Acts as an accessor to pid_\n   * Assumes we always call is_pid() first.\n   */\n  int get_pid();\n\n  /**\n   * Returns 1 if the current proc_ent_ is a pid directory. Returns 0 otherwise\n   */\n  int is_pid();\n\n  //\n  // Reads /proc/* for the next entry.\n  // Returns 1 if a process entry was found and 0 otherwise.\n  //\n  int next();\n\nprivate:\n  DIR *proc_;\n  dirent *proc_ent_;\n  int pid_;\n};\n"
  },
  {
    "path": "collector/kernel/process_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/process_handler.h>\n\n#include <collector/agent_log.h>\n#include <common/collected_blob_type.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/system_ops.h>\n\n#include <generated/ebpf_net/agent_internal/wire_message.h>\n\n#include <cstring>\n#include <string_view>\n\n#include <cassert>\n\nnamespace {\n\nstd::string_view comm_to_string(std::uint8_t const (&comm)[16])\n{\n  auto length = strnlen(reinterpret_cast<char const *>(comm), sizeof(comm));\n  return std::string_view(reinterpret_cast<char const *>(comm), length);\n}\n\n} // namespace\n\nProcessHandler::ProcessHandler(\n    ::ebpf_net::ingest::Writer &writer, ::ebpf_net::kernel_collector::Index &collector_index, logging::Logger &log)\n    : writer_(writer), collector_index_(collector_index), log_(log), memory_page_bytes_(memory_page_size().try_raise().value())\n{\n  LOG::trace_in(\n      AgentLogKind::TRACKED_PROCESS,\n      \"ProcessHandler: memory_page_size={} clock_ticks_per_second={}\",\n      memory_page_bytes_,\n      clock_ticks_per_second);\n\n  writer_.system_wide_process_settings(clock_ticks_per_second, memory_page_bytes_);\n}\n\nProcessHandler::~ProcessHandler()\n{\n  for (auto &process : processes_) {\n    process.second.handle.put(collector_index_);\n  }\n\n  processes_.clear();\n}\n\nvoid ProcessHandler::on_new_process(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_info const &msg)\n{\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::trace_in(\n      AgentLogKind::PID,\n      \"ProcessHandler::{}: timestamp={} pid={} parent={} cgroup=0x{:x} comm='{}'\",\n      __func__,\n      timestamp,\n      msg.pid,\n      msg.parent_pid,\n      msg.cgroup,\n      comm);\n\n  if (processes_.find(msg.pid) != processes_.end()) {\n    ++stats_.duplicate_tgid;\n#ifdef DEBUG_TGID\n    LOG::debug_in(\n        AgentLogKind::TRACKED_PROCESS,\n        \"DUPLICATE TGID pid={} parent={} cgroup=0x{:x} comm='{}'!\",\n        msg.pid,\n        msg.parent_pid,\n        msg.cgroup,\n        comm);\n#endif // DEBUG_TGID\n  }\n\n  auto weak_handle = collector_index_.tracked_process.by_key({msg.cgroup, msg.pid});\n  assert(msg.pid == weak_handle.tgid());\n  assert(msg.cgroup == weak_handle.cgroup());\n\n  weak_handle.set_tgid(msg.pid);\n  weak_handle.set_command(to_jb_blob(msg.comm));\n  weak_handle.set_cgroup(msg.cgroup);\n\n  processes_[msg.pid] = {\n      .handle = weak_handle.to_handle()\n#ifdef DEBUG_TGID\n          ,\n      .timestamp = timestamp,\n      .cgroup = msg.cgroup,\n      .command = std::string(comm)\n#endif // DEBUG_TGID\n  };\n}\n\nvoid ProcessHandler::on_process_end(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_close const &msg)\n{\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::trace_in(AgentLogKind::PID, \"ProcessHandler::{}: timestamp={} pid={} comm='{}'\", __func__, timestamp, msg.pid, comm);\n\n  if (auto i = processes_.find(msg.pid); i != processes_.end()) {\n    i->second.handle.put(collector_index_);\n    processes_.erase(i);\n  } else {\n    ++stats_.missing_tgid;\n#ifdef DEBUG_TGID\n    LOG::debug_in(AgentLogKind::TRACKED_PROCESS, \"MISSING TGID pid={} comm='{}'\", msg.pid, comm);\n#endif // DEBUG_TGID\n  }\n}\n\nvoid ProcessHandler::on_cgroup_move(std::chrono::nanoseconds timestamp, struct jb_agent_internal__cgroup_attach_task const &msg)\n{\n  LOG::trace_in(\n      AgentLogKind::PID, \"ProcessHandler::{}: timestamp={} pid={} cgroup=0x{:x}\", __func__, timestamp, msg.pid, msg.cgroup);\n\n  if (auto i = processes_.find(msg.pid); i != processes_.end()) {\n    i->second.handle.access(collector_index_).set_cgroup(msg.cgroup);\n  } else {\n    ++stats_.missing_tgid;\n#ifdef DEBUG_TGID\n    LOG::debug_in(AgentLogKind::TRACKED_PROCESS, \"MISSING TGID pid={} cgroup=0x{:x}\", msg.pid, msg.cgroup);\n#endif // DEBUG_TGID\n  }\n}\n\nvoid ProcessHandler::set_process_command(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_set_comm const &msg)\n{\n  const std::string_view comm = comm_to_string(msg.comm);\n  LOG::trace_in(AgentLogKind::PID, \"ProcessHandler::{}: timestamp={} pid={} comm='{}'\", __func__, timestamp, msg.pid, comm);\n\n  if (auto i = processes_.find(msg.pid); i != processes_.end()) {\n    i->second.handle.access(collector_index_).set_command(to_jb_blob(msg.comm));\n  } else {\n    ++stats_.missing_tgid;\n#ifdef DEBUG_TGID\n    LOG::debug_in(AgentLogKind::TRACKED_PROCESS, \"MISSING TGID pid={} comm='{}'\", msg.pid, comm);\n#endif // DEBUG_TGID\n  }\n}\n\nvoid ProcessHandler::pid_exit(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_exit const &msg)\n{\n  LOG::trace_in(\n      AgentLogKind::PID,\n      \"ProcessHandler::{}: timestamp={} tgid={} pid={} exit_code={}\",\n      __func__,\n      timestamp,\n      msg.tgid,\n      msg.pid,\n      msg.exit_code);\n\n  if (auto i = processes_.find(msg.tgid); i != processes_.end()) {\n    i->second.handle.access(collector_index_)\n        .pid_exit_tstamp(integer_time<std::chrono::nanoseconds>(timestamp), msg.tgid, msg.pid, msg.exit_code);\n  } else {\n    ++stats_.missing_tgid;\n#ifdef DEBUG_TGID\n    LOG::debug_in(AgentLogKind::TRACKED_PROCESS, \"MISSING TGID tgid={} pid={} exit_code={}\", msg.tgid, msg.pid, msg.exit_code);\n#endif // DEBUG_TGID\n  }\n}\n\n#ifdef DEBUG_TGID\nvoid ProcessHandler::debug_tgid_dump()\n{\n  auto const str = [this] {\n    std::ostringstream dump;\n    dump << \"-- BEGIN TGID DUMP --\\n\";\n\n    for (auto const &i : processes_) {\n      auto &process = i.second;\n      dump << fmt::format(\n          \"tstamp: {}, tgid: {}, cgroup: {}, comm: {}\\n\", process.timestamp, i.first, process.cgroup, process.command);\n    }\n\n    dump << \"-- END TGID DUMP --\\n\";\n    return dump.str();\n  }();\n\n  LOG::debug_in(AgentLogKind::TRACKED_PROCESS, \"{}\", str);\n}\n#endif // DEBUG_TGID\n"
  },
  {
    "path": "collector/kernel/process_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/logger.h>\n#include <util/stop_watch.h>\n\n#include <generated/ebpf_net/kernel_collector/handles.h>\n#include <generated/ebpf_net/kernel_collector/index.h>\n\n#include <absl/container/flat_hash_map.h>\n#include <absl/container/flat_hash_set.h>\n\n#include <optional>\n#include <string>\n#include <string_view>\n#include <vector>\n\n// Turn this on to enable tgid table debugging feature\n// via: kill -USR1 <agent_pid>\n#ifndef NDEBUG\n// #define DEBUG_TGID 1\n#endif // NDEBUG\n\nclass ProcessHandler {\npublic:\n  ProcessHandler(\n      ::ebpf_net::ingest::Writer &writer, ::ebpf_net::kernel_collector::Index &collector_index, logging::Logger &log);\n\n  ~ProcessHandler();\n\n  void on_new_process(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_info const &msg);\n\n  void on_process_end(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_close const &msg);\n\n  void on_cgroup_move(std::chrono::nanoseconds timestamp, struct jb_agent_internal__cgroup_attach_task const &msg);\n\n  void set_process_command(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_set_comm const &msg);\n\n  void pid_exit(std::chrono::nanoseconds timestamp, struct jb_agent_internal__pid_exit const &msg);\n\n#ifdef DEBUG_TGID\n  void debug_tgid_dump();\n#endif // DEBUG_TGID\n\nprivate:\n  struct ThreadGroupData {\n    ebpf_net::kernel_collector::handles::tracked_process handle;\n#ifdef DEBUG_TGID\n    std::chrono::nanoseconds timestamp;\n    u64 cgroup;\n    std::string command;\n#endif // DEBUG_TGID\n  };\n\n  // rpc components\n  ::ebpf_net::ingest::Writer &writer_;\n  ::ebpf_net::kernel_collector::Index &collector_index_;\n\n  // process data\n  absl::flat_hash_map<u32, ThreadGroupData> processes_;\n\n  // logging\n  logging::Logger &log_;\n\n  std::size_t memory_page_bytes_;\n\n  struct InternalStats {\n    std::size_t duplicate_tgid = 0;\n    std::size_t missing_tgid = 0;\n  } stats_;\n};\n"
  },
  {
    "path": "collector/kernel/process_prober.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/process_prober.h>\n\n#include <linux/bpf.h>\n\n#include <collector/kernel/fd_reader.h>\n#include <collector/kernel/probe_handler.h>\n#include <collector/kernel/proc_reader.h>\n\n// Forward declaration for skeleton\nstruct render_bpf_bpf;\n\nProcessProber::ProcessProber(\n    ProbeHandler &probe_handler,\n    struct render_bpf_bpf *skel,\n    std::function<void(void)> periodic_cb,\n    std::function<void(std::string)> check_cb)\n{\n  // END\n  probe_handler.start_probe(skel, \"on_taskstats_exit\", \"taskstats_exit\");\n  probe_handler.start_probe(skel, \"on_cgroup_exit\", \"cgroup_exit\");\n  probe_handler.start_kretprobe(skel, \"onret_cgroup_exit\", \"cgroup_exit\");\n\n  // STATE CHANGE (pid->cgroup)\n  probe_handler.start_probe(skel, \"on_cgroup_attach_task\", \"cgroup_attach_task\");\n\n  // START\n  /* probe for new process info\n   * Note that other functions we considered were:\n   * sys_execve, sched_fork, __sched_fork, _do_fork, sched_exec\n   */\n  probe_handler.start_probe(skel, \"on_wake_up_new_task\", \"wake_up_new_task\");\n  probe_handler.start_probe(skel, \"on_set_task_comm\", \"__set_task_comm\");\n\n  // EXISTING\n  probe_handler.start_kretprobe(skel, \"onret_get_pid_task\", \"get_pid_task\");\n  periodic_cb();\n  check_cb(\"process prober startup\");\n\n  // iterate over /proc/ to trigger on_get_pid_task()\n  trigger_get_pid_task(periodic_cb);\n  check_cb(\"trigger_get_pid_task()\");\n\n  // we can remove the probe for existing now\n  probe_handler.cleanup_kretprobe(\"get_pid_task\");\n  periodic_cb();\n  check_cb(\"process prober cleanup()\");\n}\n\nvoid ProcessProber::trigger_get_pid_task(std::function<void(void)> periodic_cb)\n{\n  ProcReader proc_reader;\n  while (proc_reader.next()) {\n    periodic_cb();\n\n    if (!proc_reader.is_pid())\n      continue; // skip this entry if this wasn't a pid directory\n\n    int pid = proc_reader.get_pid();\n    FDReader fd_reader(pid);\n    int status = fd_reader.open_task_dir();\n    if (status)\n      continue; // skip this entry because task_dir couldn't be opened\n\n    // for each thread in this group\n    while (!fd_reader.next_task()) {\n      // read a file from this tid so that our probe can generate a msg.\n      // we don't care about the return value\n      fd_reader.open_task_comm();\n\n      periodic_cb();\n    }\n  }\n}\n"
  },
  {
    "path": "collector/kernel/process_prober.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <functional>\n#include <memory>\n\nclass ProbeHandler;\nstruct render_bpf_bpf;\n\nclass ProcessProber {\npublic:\n  ProcessProber(\n      ProbeHandler &probe_handler,\n      struct render_bpf_bpf *skel,\n      std::function<void(void)> periodic_cb,\n      std::function<void(std::string)> check_cb);\n\nprivate:\n  /**\n   * Iterates over /proc and triggers calls to get_pid_task\n   */\n  void trigger_get_pid_task(std::function<void(void)> periodic_cb);\n};\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_base.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"protocol_handler_base.h\"\n#include \"platform/platform.h\"\n#include \"spdlog/common.h\"\n#include \"spdlog/fmt/bin_to_hex.h\"\n#include \"util/log.h\"\n\nProtocolHandlerBase::ProtocolHandlerBase(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid)\n    : data_handler_(data_handler), key_(key), pid_(pid)\n{}\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_base.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <memory>\n\n#include \"platform/platform.h\"\n\n#include \"collector/kernel/bpf_src/tcp-processor/tcp_processor.h\"\n\nclass TCPDataHandler;\n\nclass ProtocolHandlerBase {\npublic:\n  typedef std::shared_ptr<ProtocolHandlerBase> ptr_type;\n\n  ProtocolHandlerBase(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid);\n  virtual ~ProtocolHandlerBase() {}\n\n  virtual void handle_server_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) = 0;\n  virtual void handle_client_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) = 0;\n\n  virtual void set_upgrade(ptr_type upgrade) { upgrade_ = upgrade; }\n  virtual ptr_type get_upgrade() { return upgrade_; }\n  inline TCPDataHandler *data_handler() { return data_handler_; }\n  inline tcp_control_key_t control_key() const { return key_; }\n  inline u32 pid() const { return pid_; }\n\nprivate:\n  TCPDataHandler *data_handler_;\n  tcp_control_key_t key_;\n  ptr_type upgrade_;\n  u32 pid_;\n};\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_http.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"protocol_handler_http.h\"\n#include \"platform/platform.h\"\n#include \"protocol_tools.h\"\n#include \"spdlog/common.h\"\n#include \"spdlog/fmt/bin_to_hex.h\"\n#include \"util/log.h\"\n\nProtocolHandler_HTTP::ProtocolHandler_HTTP(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid)\n    : ProtocolHandlerBase(data_handler, key, pid),\n      request_timestamp_(0),\n      response_timestamp_(0),\n      client_stream_position_(0),\n      server_stream_position_(0),\n      client_state_(CLIENT_STATE::START),\n      server_state_(SERVER_STATE::START),\n      http_version_major_(0),\n      http_version_minor_(0),\n      http_code_(0)\n\n{\n  LOG::debug_in(AgentLogKind::PROTOCOL, \"ProtocolHandler_HTTP::$ctor(sk={:x})\", control_key().sk);\n}\n\nProtocolHandler_HTTP::~ProtocolHandler_HTTP()\n{\n  LOG::debug_in(AgentLogKind::PROTOCOL, \"ProtocolHandler_HTTP::$dtor(sk={:x})\", control_key().sk);\n}\n\nvoid ProtocolHandler_HTTP::transition(CLIENT_STATE new_state)\n{\n  client_state_ = new_state;\n}\n\nvoid ProtocolHandler_HTTP::transition(SERVER_STATE new_state)\n{\n  server_state_ = new_state;\n}\n\nvoid ProtocolHandler_HTTP::handle_server_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len)\n{\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"ProtocolHandler_HTTP::handle_server_data(sk={:x}): tstamp={}, offset={}, is_recv={}, data_len={}, data:\\n{}\",\n      tstamp,\n      control_key().sk,\n      offset,\n      stream_type_to_string(stream_type),\n      data_len,\n      std::string_view((const char *)data, data_len));\n\n  // HTTP server-side state machine\n  // TODO: Add buffering if state processing can't proceed without more bytes\n\n  if (offset > server_stream_position_) {\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL,\n        \"ProtocolHandler_HTTP::handle_server_data: server stream skipped {} bytes from {} to {}\",\n        offset - server_stream_position_,\n        server_stream_position_,\n        offset);\n  } else if (offset > server_stream_position_) {\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL,\n        \"ProtocolHandler_HTTP::handle_server_data: server stream went backward {} bytes from {} to {}\",\n        server_stream_position_ - offset,\n        server_stream_position_,\n        offset);\n  }\n\n  while (server_state_ != SERVER_STATE::END) {\n\n    switch (server_state_) {\n\n    // Beginning of stream\n    case SERVER_STATE::START:\n      response_timestamp_ = tstamp;\n      transition(SERVER_STATE::VERSION_AND_CODE);\n      break;\n\n    // Wait for HTTP version and code\n    case SERVER_STATE::VERSION_AND_CODE: {\n      if (data_len < 12) {\n        // TODO: Eventually, buffer and just return\n        transition(SERVER_STATE::STOP);\n        break;\n      }\n\n      // Pattern match against HTTP/x.y\n      if (!prefix_check(data, 12, \"HTTP/\") || data[6] != '.' || data[8] != ' ') {\n        transition(SERVER_STATE::STOP);\n        break;\n      }\n\n      http_version_major_ = char_to_number(data[5]);\n      http_version_minor_ = char_to_number(data[7]);\n\n      // Check for invalid HTTP version\n      if (http_version_major_ == -1 || http_version_minor_ == -1) {\n        transition(SERVER_STATE::STOP);\n        break;\n      }\n\n      // Convert HTTP response code to integer\n      int http_code_2 = char_to_number(data[9]);\n      int http_code_1 = char_to_number(data[10]);\n      int http_code_0 = char_to_number(data[11]);\n\n      // Ensure each digit of HTTP response code is valid\n      if (http_code_2 == -1 || http_code_1 == -1 || http_code_0 == -1) {\n        transition(SERVER_STATE::STOP);\n        break;\n      }\n\n      http_code_ = (u16)(http_code_2 * 100 + http_code_1 * 10 + http_code_0);\n\n      // Submit the http response code, and latency\n      u64 latency = response_timestamp_ - request_timestamp_;\n      enum CLIENT_SERVER_TYPE client_server = (stream_type == ST_SEND) ? SC_SERVER : SC_CLIENT;\n\n      LOG::debug_in(\n          AgentLogKind::HTTP,\n          \"handle_http_response: tstamp={}, sk={:x}, pid={}, code={}, \"\n          \"latency_ns={}, client_server={}\",\n          response_timestamp_,\n          control_key().sk,\n          pid(),\n          http_code_,\n          latency,\n          client_server_type_to_string(client_server));\n\n      data_handler()->writer().http_response_tstamp(\n          response_timestamp_, control_key().sk, pid(), http_code_, latency, (u8)client_server);\n\n      transition(SERVER_STATE::STOP);\n    } break;\n    // Stop server side processing\n    case SERVER_STATE::STOP:\n      data_handler()->enable_stream(control_key(), stream_type, false);\n      transition(SERVER_STATE::END);\n      break;\n\n    // End of server side processing\n    case SERVER_STATE::END:\n    default:\n      LOG::error(\"ProtocolHandler_HTTP::handle_server_data: invalid server state\");\n      return;\n    }\n  }\n\n  server_stream_position_ += data_len;\n}\n\nvoid ProtocolHandler_HTTP::handle_client_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len)\n{\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"ProtocolHandler_HTTP::handle_client_data(sk={:x}): tstamp={}, offset={}, is_recv={}, data_len={}, data:\\n{}\",\n      tstamp,\n      control_key().sk,\n      offset,\n      stream_type_to_string(stream_type),\n      data_len,\n      std::string_view((const char *)data, data_len));\n\n  // HTTP client-side state machine\n  // TODO: Add buffering if state processing can't proceed without more bytes\n\n  if (offset > client_stream_position_) {\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL,\n        \"ProtocolHandler_HTTP::handle_client_data: client stream skipped {} bytes from {} to {}\",\n        offset - client_stream_position_,\n        client_stream_position_,\n        offset);\n  } else if (offset > client_stream_position_) {\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL,\n        \"ProtocolHandler_HTTP::handle_client_data: client stream went backward {} bytes from {} to {}\",\n        client_stream_position_ - offset,\n        client_stream_position_,\n        offset);\n  }\n\n  while (client_state_ != CLIENT_STATE::END) {\n\n    switch (client_state_) {\n\n    // Beginning of stream\n    case CLIENT_STATE::START:\n      request_timestamp_ = tstamp;\n      transition(CLIENT_STATE::STOP);\n      break;\n\n    // Stop client side processing\n    case CLIENT_STATE::STOP:\n      data_handler()->enable_stream(control_key(), stream_type, false);\n      transition(CLIENT_STATE::END);\n      break;\n\n    // End of client side processing\n    case CLIENT_STATE::END:\n    default:\n      LOG::error(\"ProtocolHandler_HTTP::handle_client_data: invalid client state\");\n      return;\n    }\n  }\n\n  client_stream_position_ += data_len;\n}\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_http.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n#include \"../tcp_data_handler.h\"\n#include \"protocol_handler_base.h\"\n\nclass ProtocolHandler_HTTP : public ProtocolHandlerBase {\nprivate:\n  u64 request_timestamp_;\n  u64 response_timestamp_;\n  u64 client_stream_position_;\n  u64 server_stream_position_;\n  enum class CLIENT_STATE { START, STOP, END } client_state_;\n  enum class SERVER_STATE { START, VERSION_AND_CODE, STOP, END } server_state_;\n\n  int http_version_major_;\n  int http_version_minor_;\n  int http_code_;\n\n  void transition(CLIENT_STATE new_state);\n  void transition(SERVER_STATE new_state);\n\npublic:\n  ProtocolHandler_HTTP(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid);\n  virtual ~ProtocolHandler_HTTP();\n\n  virtual void handle_server_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) override;\n  virtual void handle_client_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) override;\n};\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_unknown.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"protocol_handler_unknown.h\"\n#include \"absl/base/internal/endian.h\"\n#include \"platform/platform.h\"\n#include \"protocol_tools.h\"\n#include \"spdlog/common.h\"\n#include \"spdlog/fmt/bin_to_hex.h\"\n#include \"util/log.h\"\n#include <string_view>\n\nProtocolHandler_UNKNOWN::ProtocolHandler_UNKNOWN(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid)\n    : ProtocolHandlerBase(data_handler, key, pid)\n{\n  LOG::debug_in(AgentLogKind::PROTOCOL, \"ProtocolHandler_UNKNOWN::$ctor(sk={:x})\", control_key().sk);\n\n  available_protocols_ = TCP_PROTOCOL_MASK;\n}\n\nProtocolHandler_UNKNOWN::~ProtocolHandler_UNKNOWN()\n{\n  LOG::debug_in(AgentLogKind::PROTOCOL, \"ProtocolHandler_UNKNOWN::$dtor(sk={:x})\", control_key().sk);\n}\n\nvoid ProtocolHandler_UNKNOWN::handle_server_data(\n    u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len)\n{\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"ProtocolHandler_UNKNOWN::handle_server_data(sk={:x}): tstamp={}, offset={}, is_recv={}, data_len={}, data={}\",\n      tstamp,\n      control_key().sk,\n      offset,\n      stream_type_to_string(stream_type),\n      data_len,\n      std::string_view((const char *)data, data_len));\n\n  // Currently protocol detection only happens with client data\n  // XXX: when we implement other protocols, we may need to validate a server response before upgrading, potentially\n  available_protocols_ = 0;\n  data_handler()->enable_stream(control_key(), false);\n}\n\nTCP_PROTOCOL_DETECT_RESULT\nProtocolHandler_UNKNOWN::detect_http(u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len)\n{\n  TCP_PROTOCOL_DETECT_RESULT res = TPD_UNKNOWN;\n\n  // XXX: This is not complete. Eventually we should implement buffering if data_len is less than 4, since\n  // XXX: in theory 3 bytes could show up, followed by the rest of the data, and since TCP is a stream protocol.\n\n  if (data_len < 4)\n    return res;\n\n  switch (U32IDX(data, 0)) {\n  case U32CC(\"GET \"):\n  case U32CC(\"PUT \"):\n    return TPD_SUCCESS;\n  case U32CC(\"HEAD\"):\n  case U32CC(\"POST\"):\n    if (data_len >= 5) {\n      res = data[4] == ' ' ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  case U32CC(\"DELE\"):\n    if (data_len >= 7) {\n      res = (data[4] == 'T' && data[5] == 'E' && data[6] == ' ') ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  case U32CC(\"CONN\"):\n    if (data_len >= 8) {\n      res = U32IDX(data, 1) == U32CC(\"ECT \") ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  case U32CC(\"OPTI\"):\n    if (data_len >= 8) {\n      res = U32IDX(data, 1) == U32CC(\"ONS \") ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  case U32CC(\"TRAC\"):\n    if (data_len >= 6) {\n      res = (data[4] == 'E' && data[5] == ' ') ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  case U32CC(\"PATC\"):\n    if (data_len >= 6) {\n      res = (data[4] == 'H' && data[5] == ' ') ? TPD_SUCCESS : TPD_FAILED;\n    }\n    break;\n  default:\n    res = TPD_FAILED;\n    break;\n  }\n\n  return res;\n}\n\nvoid ProtocolHandler_UNKNOWN::handle_client_data(\n    u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len)\n{\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"ProtocolHandler_UNKNOWN::handle_client_data(sk={:x}): tstamp={}, offset={}, is_recv={}, data_len={}, data:\\n{}\",\n      tstamp,\n      control_key().sk,\n      offset,\n      stream_type_to_string(stream_type),\n      data_len,\n      std::string_view((const char *)data, data_len));\n\n  // All the client-side protocol detection routines we want to run\n  std::list<std::pair<int, PROTOCOL_DETECT_FUNC>> client_protocols = {\n      {TCPPROTO_HTTP, &ProtocolHandler_UNKNOWN::detect_http},\n      //{TCPPROTO_MYSQL, &ProtocolHandler_UNKNOWN::detect_mysql},\n  };\n\n  // Run the available detection routines\n  for (auto it : client_protocols) {\n    int tcpproto = it.first;\n    PROTOCOL_DETECT_FUNC detect = it.second;\n\n    if (available_protocols_ & TCP_PROTOCOL_BIT(tcpproto)) {\n      // Run the detection if it's still qualified\n      auto res = (this->*detect)(offset, stream_type, data, data_len);\n      if (res == TPD_SUCCESS) {\n        // If we -definitely- detected a particular protocol, upgrade to its handler\n        ProtocolHandlerBase::ptr_type upgrade = data_handler()->create_protocol_handler(tcpproto, control_key(), pid());\n        set_upgrade(upgrade);\n        return;\n      } else if (res == TPD_FAILED) {\n        // Remove any that are disqualified\n        available_protocols_ &= ~TCP_PROTOCOL_BIT(tcpproto);\n      }\n      // If res==TPD_UNKNOWN, then continue checking with the next protocol\n    }\n  }\n\n  // When all protocols are done just stop listening completely\n  if (available_protocols_ == 0) {\n    data_handler()->enable_stream(control_key(), false);\n  }\n}\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_handler_unknown.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n#include \"../tcp_data_handler.h\"\n#include \"protocol_handler_base.h\"\n\nclass ProtocolHandler_UNKNOWN : public ProtocolHandlerBase {\npublic:\n  ProtocolHandler_UNKNOWN(TCPDataHandler *data_handler, const tcp_control_key_t &key, u32 pid);\n  virtual ~ProtocolHandler_UNKNOWN();\n\n  virtual void handle_server_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) override;\n  virtual void handle_client_data(u64 tstamp, u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len) override;\n\nprotected:\n  typedef TCP_PROTOCOL_DETECT_RESULT (ProtocolHandler_UNKNOWN::*PROTOCOL_DETECT_FUNC)(\n      u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len);\n\n  TCP_PROTOCOL_DETECT_RESULT detect_http(u64 offset, STREAM_TYPE stream_type, const u8 *data, size_t data_len);\n\nprivate:\n  u32 available_protocols_;\n};\n"
  },
  {
    "path": "collector/kernel/protocols/protocol_tools.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\ninline bool prefix_check(const void *haystack, const size_t haystack_len, const char *needle)\n{\n  size_t needle_len = strlen(needle);\n  return (haystack_len >= needle_len && memcmp(haystack, needle, needle_len) == 0);\n}\n\ninline int char_to_number(char x)\n{\n  if (x < '0' || x > '9')\n    return -1;\n  return (int)(x - '0');\n}\n\n#define U32IDX(X, N) (absl::little_endian::FromHost32(((u32 *)(X))[(N)]))\n#define _U32CH(X, N) (((u32)((X)[(N)])) << (8 * (N)))\n#define U32CC(X) (_U32CH(X, 0) | _U32CH(X, 1) | _U32CH(X, 2) | _U32CH(X, 3))\n"
  },
  {
    "path": "collector/kernel/socket_prober.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/agent_log.h>\n#include <collector/kernel/fd_reader.h>\n#include <collector/kernel/probe_handler.h>\n#include <collector/kernel/proc_net_reader.h>\n#include <collector/kernel/proc_reader.h>\n#include <collector/kernel/socket_prober.h>\n#include <config.h>\n#include <iostream>\n#include <set>\n#include <util/log.h>\n\nstatic constexpr u32 periodic_cb_mask = 0x3f;\n\nSocketProber::SocketProber(\n    ProbeHandler &probe_handler,\n    struct render_bpf_bpf *skel,\n    std::function<void(void)> periodic_cb,\n    std::function<void(std::string)> check_cb,\n    logging::Logger &log)\n    : log_(log)\n{\n  // END\n  // NOTE: Covers all protocols\n  probe_handler.start_probe(skel, \"on_security_sk_free\", \"security_sk_free\");\n\n  // inet END\n  probe_handler.start_probe(skel, \"on_inet_release\", \"inet_release\");\n  probe_handler.start_kretprobe(skel, \"onret_inet_release\", \"inet_release\");\n\n  // CHANGE OF STATE\n  probe_handler.start_probe(skel, \"on_tcp_connect\", \"tcp_connect\");\n  probe_handler.start_probe(skel, \"on_inet_csk_listen_start\", \"inet_csk_listen_start\");\n\n  // START\n  probe_handler.start_probe(skel, \"on_tcp_init_sock\", \"tcp_init_sock\");\n  // NOTE: these probes also sends out state information\n  probe_handler.start_kretprobe(skel, \"onret_inet_csk_accept\", \"inet_csk_accept\");\n  probe_handler.start_probe(skel, \"on_inet_csk_accept\", \"inet_csk_accept\");\n  // UDP START\n  probe_handler.start_kretprobe(skel, \"onret_udp_v4_get_port\", \"udp_v4_get_port\");\n  probe_handler.start_kretprobe(skel, \"onret_udp_v6_get_port\", \"udp_v6_get_port\");\n  probe_handler.start_probe(skel, \"on_udp_v4_get_port\", \"udp_v4_get_port\");\n  probe_handler.start_probe(skel, \"on_udp_v6_get_port\", \"udp_v6_get_port\");\n\n  // EXISTING\n  probe_handler.start_probe(skel, \"on_tcp4_seq_show\", \"tcp4_seq_show\");\n  probe_handler.start_probe(skel, \"on_tcp6_seq_show\", \"tcp6_seq_show\");\n  probe_handler.start_probe(skel, \"on_udp4_seq_show\", \"udp4_seq_show\");\n  probe_handler.start_probe(skel, \"on_udp6_seq_show\", \"udp6_seq_show\");\n\n  periodic_cb();\n  check_cb(\"socket prober startup\");\n\n  /* First step: fill up the \"seen_inodes\" bpf hashmap: inode -> pid */\n  struct bpf_map *seen_inodes_map = probe_handler.get_bpf_map(skel, \"seen_inodes\");\n  int map_fd = bpf_map__fd(seen_inodes_map);\n\n  // Clear the map\n  u32 key, next_key;\n  while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) {\n    bpf_map_delete_elem(map_fd, &next_key);\n    key = next_key;\n  }\n  periodic_cb();\n  check_cb(\"clear inode table\");\n\n  fill_inode_to_pid_map(map_fd, periodic_cb);\n  check_cb(\"fill_inode_to_pid_map()\");\n\n  // now iterate over processes again but look through network namespaces\n  // for new ns, read tcp and tcp6. this will trigger tcp46_seq_show\n  trigger_seq_show(periodic_cb);\n  check_cb(\"trigger_seq_show()\");\n\n  /* can remove existing now */\n  probe_handler.cleanup_probe(\"tcp4_seq_show\");\n  periodic_cb();\n  check_cb(\"socket prober cleanup (1)\");\n  probe_handler.cleanup_probe(\"tcp6_seq_show\");\n  periodic_cb();\n  check_cb(\"socket prober cleanup (2)\");\n  probe_handler.cleanup_probe(\"udp4_seq_show\");\n  periodic_cb();\n  check_cb(\"socket prober cleanup (3)\");\n  probe_handler.cleanup_probe(\"udp6_seq_show\");\n  periodic_cb();\n  check_cb(\"socket prober cleanup (4)\");\n}\n\nvoid SocketProber::fill_inode_to_pid_map(int map_fd, std::function<void(void)> periodic_cb)\n{\n  // iterate over /proc/\n  ProcReader proc_reader;\n  u32 proc_count = 0;\n  u32 n_update_failures = 0;\n  while (proc_reader.next()) {\n    // every few procs, call periodic_cb, in case a lot of them are skipped\n    if (((++proc_count) & periodic_cb_mask) == 0)\n      periodic_cb();\n\n    if (!proc_reader.is_pid())\n      continue; // skip this entry if this wasn't a pid directory\n\n    int pid = proc_reader.get_pid();\n    FDReader fd_reader(pid);\n    int status = fd_reader.open_task_dir();\n    if (status) {\n      LOG::trace_in(AgentLogKind::SOCKET, \"skipping entry because task_dir couldn't be opened pid={}\", pid);\n      continue; // skip this entry because task_dir couldn't be opened\n    }\n\n    // for each fd of this pid.\n    status = fd_reader.open_fd_dir();\n    if (status) {\n      LOG::trace_in(AgentLogKind::SOCKET, \"skipping entry because fd_dir couldn't be opened pid={}\", pid);\n      continue; // skip this pid if fd_dir couldn't be opened\n    }\n\n    u32 inode_count = 0;\n    while (!fd_reader.next_fd()) {\n      int ino = fd_reader.get_inode();\n      if (ino > 0) {\n        u32 key = (u32)ino;\n        u32 lookup_pid = 0;\n        int lookup_result = bpf_map_lookup_elem(map_fd, &key, &lookup_pid);\n        if (lookup_result == 0) {\n          LOG::trace_in(\n              AgentLogKind::SOCKET, \"Duplicate file descriptor for pid={}, ino={} (lookup_pid={})\", pid, ino, lookup_pid);\n          continue;\n        }\n        int update_result = bpf_map_update_elem(map_fd, &key, &pid, 0);\n        if (update_result != 0) {\n          // log at most 10 times\n          if (++n_update_failures < 10) {\n            LOG::debug(\"Error updating hash_map: {} - {}\", update_result, strerror(errno));\n          }\n          continue;\n        }\n        LOG::trace_in(AgentLogKind::SOCKET, \"Added inode to hash_map: pid={}, ino={}\", pid, ino);\n      }\n\n      // every few inodes, call periodic_cb\n      if (((++inode_count) & periodic_cb_mask) == 0)\n        periodic_cb();\n    }\n\n    // call periodic_cb for every pid\n    periodic_cb();\n  }\n\n  if (n_update_failures != 0) {\n    log_.warn(\"Recovering existing socket inodes got {} total update failures\", n_update_failures);\n  }\n}\n\nvoid SocketProber::trigger_seq_show(std::function<void(void)> periodic_cb)\n{\n  ProcReader proc_reader;\n  std::set<int> done_network_namespaces;\n  // iterate over /proc/\n  while (proc_reader.next()) {\n    periodic_cb();\n\n    if (!proc_reader.is_pid()) {\n      continue; // skip this entry if this wasn't a pid directory\n    }\n\n    int pid = proc_reader.get_pid();\n\n    int network_namespace = get_network_namespace(pid);\n    if (network_namespace == -1)\n      continue; // something went wrong on this pid so skip to the next one\n\n    /* if we've seen this namespace, don't re-process */\n    auto ns_it = done_network_namespaces.find(network_namespace);\n    if (ns_it != done_network_namespaces.end())\n      continue;\n\n    /* new network namespace -- process it */\n    done_network_namespaces.insert(network_namespace);\n\n    read_proc_net_tcp(\"/proc/\" + std::to_string(pid) + \"/net/tcp\", periodic_cb);\n    read_proc_net_tcp(\"/proc/\" + std::to_string(pid) + \"/net/tcp6\", periodic_cb);\n    read_proc_net_udp(\"/proc/\" + std::to_string(pid) + \"/net/udp\", periodic_cb);\n    read_proc_net_udp(\"/proc/\" + std::to_string(pid) + \"/net/udp6\", periodic_cb);\n  }\n\n  periodic_cb();\n}\n\nvoid SocketProber::read_proc_net_tcp(const std::string &filename, std::function<void(void)> periodic_cb)\n{\n\n  ProcNetReader proc_net_reader(filename);\n  u32 sk_count = 0;\n  while (proc_net_reader.next()) {\n    // every few sk's, call periodic_cb\n    if (((++sk_count) & periodic_cb_mask) == 0)\n      periodic_cb();\n\n    // u64 sk_p = proc_net_reader.get_sk();\n\n    // int sk_state = proc_net_reader.get_sk_state();\n    // if ((sk_state != 1) && (sk_state != 10)) {\n    //   LOG::trace_in(AgentLogKind::SOCKET, \"sk {:x} state not listen/established: {}\", sk_p, sk_state);\n    //   continue;\n    // }\n\n    // int sk_ino = proc_net_reader.get_ino();\n    // if (sk_ino == 0) {\n    //  LOG::debug(\"sk {:x} sk_ino was 0: {}\", sk_p, sk_ino);\n    //  continue; // skip if ino=0 (sk will already be closed)\n    //}\n\n    /* TODO: log? */\n    // std::cout << \"successful sk: \" << sk_p << \"\\t sk_ino: \" << sk_ino <<\n    // std::endl;\n  }\n}\n\nvoid SocketProber::read_proc_net_udp(const std::string &filename, std::function<void(void)> periodic_cb)\n{\n\n  ProcNetReader proc_net_reader(filename);\n\n  u32 sk_count = 0;\n  while (proc_net_reader.next()) {\n    /* just iterate to get udp sockets in udp_seq_show */\n\n    // every few sk's, call periodic_cb\n    if (((++sk_count) & periodic_cb_mask) == 0)\n      periodic_cb();\n  }\n}\n\nint SocketProber::get_network_namespace(int pid)\n{\n  char link[64];\n  int network_namespace;\n  snprintf(link, sizeof(link), \"/proc/%d/ns/net\", pid);\n\n  char link_content[32];\n  int info_len = readlink(link, link_content, sizeof(link_content) - 1);\n  if (info_len == -1)\n    // TODO: add logging\n    return -1;\n\n  link_content[info_len] = '\\0';\n\n  if (strncmp(link_content, \"net:[\", strlen(\"net:[\")))\n    // TODO: add logging\n    return -1;\n  //\t\tthrow std::runtime_error(\"get_network_namespace: readlink should\n  // start with net:[\");\n  sscanf(link_content, \"net:[%u]\", &network_namespace);\n\n  return network_namespace;\n}\n"
  },
  {
    "path": "collector/kernel/socket_prober.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <linux/bpf.h>\n\n#include <functional>\n#include <memory>\n\n#include <platform/types.h>\n\n#include <util/logger.h>\n\n/* forward declarations */\nstruct render_bpf_bpf;\nclass ProbeHandler;\n\n/**\n * Adds BPF probes for new and existing sockets, and iterates through existing\n *   sockets, to obtain an up-to-date view of system sockets\n */\nclass SocketProber {\npublic:\n  /**\n   * C'tor\n   *\n   * @param probe_handler: a ProbeHandler where new probes can be registered\n   * @param bpf_module: the module from the bpf source code\n   * @param periodic_cb: a callback to be called every once in a while, to\n   *   allow user to e.g., flush rings\n   */\n  SocketProber(\n      ProbeHandler &probe_handler,\n      struct render_bpf_bpf *skel,\n      std::function<void(void)> periodic_cb,\n      std::function<void(std::string)> check_cb,\n      logging::Logger &log);\n\nprivate:\n  /**\n   * Fills the given map with a mapping of inode->pid of existing sockets\n   *\n   * @param map: the inode->pid map to fill\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void fill_inode_to_pid_map(int map_fd, std::function<void(void)> periodic_cb);\n\n  /**\n   * Iterates through proc, and triggers the corresponding seq_show functions\n   *   for all supported types of existing sockets by reading proc namespaces\n   *\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void trigger_seq_show(std::function<void(void)> periodic_cb);\n\n  /**\n   * Reads a file in /proc/<pid>/net/{tcp,tcp6}\n   *\n   * @param filename: the file to read\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void read_proc_net_tcp(const std::string &filename, std::function<void(void)> periodic_cb);\n\n  /**\n   * Reads a file in /proc/<pid>/net/{udp,udp6}\n   *\n   * @param filename: the file to read\n   * @param periodic_cb: callback to call after doing some work.\n   */\n  void read_proc_net_udp(const std::string &filename, std::function<void(void)> periodic_cb);\n\n  /**\n   * Returns the network namespace the pid lives in, by reading /proc\n   *\n   * @param pid: the pid to check\n   * @returns namespace ID on success, -1 on failure.\n   */\n  int get_network_namespace(int pid);\n\nprivate:\n  logging::Logger &log_;\n};\n"
  },
  {
    "path": "collector/kernel/socket_table.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <array>\n#include <platform/platform.h>\n#include <string.h>\n#include <util/circular_queue_cpp.h>\n#include <util/fixed_hash.h>\n#include <util/histogram.h>\n\n/**\n * Struct used to keep connection statistics to be sent periodically\n */\nstruct tcp_statistics {\n  u64 diff_bytes_acked = 0;\n  u32 diff_delivered = 0;\n  u32 diff_retrans = 0;\n  u32 max_srtt = 0;\n  u32 diff_rcv_holes = 0;\n  u64 diff_bytes_received = 0;\n  u32 diff_rcv_delivered = 0;\n  u32 max_rcv_rtt = 0;\n\n  /* is this statistic valid? since we cannot dequeue ourselves, this enables\n   * code to invalidate this entry so it will be ignored\n   */\n  bool valid = false;\n};\n\nstruct tcp_socket_entry {\n  /* last state observed */\n  u64 bytes_acked = 0;\n  u32 packets_delivered = 0;\n  u32 packets_retrans = 0;\n  u64 bytes_received = 0;\n  u32 rcv_holes = 0;\n  u32 rcv_delivered = 0;\n};\n\nstruct udp_statistics {\n  bool valid = false;\n  u32 packets = 0;\n  u64 bytes = 0;\n  u32 drops = 0;\n};\n\nstruct udp_remote_endpoint {\n  std::array<u32, 4> addr = {0, 0, 0, 0};\n  u16 port = 0;\n\n  /** Address Family if address changed since last report, 0 otherwise */\n  u8 changed_af = 0;\n};\n\nstruct udp_socket_entry {\n  /* local address info */\n  std::array<u32, 4> laddr = {0, 0, 0, 0};\n  u16 lport = 0;\n\n  bool reported = false; /* did we send to backend */\n  u32 pid = 0;\n  u64 sk = 0;\n  struct udp_remote_endpoint addrs[2] = {{}}; /* 0 for TX, 1 for RX */\n};\n"
  },
  {
    "path": "collector/kernel/tcp_data_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/platform.h>\n\n#include \"spdlog/common.h\"\n#include \"spdlog/fmt/bin_to_hex.h\"\n#include <bpf/bpf.h>\n#include <collector/kernel/bpf_src/render_bpf.h>\n#include <collector/kernel/perf_reader.h>\n#include <collector/kernel/probe_handler.h>\n#include <collector/kernel/tcp_data_handler.h>\n#include <iostream>\n#include <stdexcept>\n#include <util/ip_address.h>\n#include <util/log.h>\n#include <util/lookup3.h>\n#include <uv.h>\n\n#include \"protocols/protocol_handler_base.h\"\n#include \"protocols/protocol_handler_http.h\"\n#include \"protocols/protocol_handler_unknown.h\"\n\nbool TCPDataHandler::tcp_control_key_t_comparator::operator()(const tcp_control_key_t &a, const tcp_control_key_t &b) const\n{\n  return a.sk < b.sk;\n}\n\nTCPDataHandler::TCPDataHandler(\n    uv_loop_t &loop,\n    ProbeHandler &probe_handler,\n    struct render_bpf_bpf *skel,\n    ::ebpf_net::ingest::Writer &writer,\n    PerfContainer &container,\n    logging::Logger &log)\n    : loop_(loop), skel_(skel), writer_(writer), container_(container), log_(log)\n{\n  // Get tcp control hash table map file descriptor\n  struct bpf_map *tcp_control_map = probe_handler.get_bpf_map(skel_, \"_tcp_control\");\n  tcp_control_map_fd_ = bpf_map__fd(tcp_control_map);\n}\n\nTCPDataHandler::~TCPDataHandler() {}\n\nstd::shared_ptr<ProtocolHandlerBase>\nTCPDataHandler::create_protocol_handler(int protocol, const tcp_control_key_t &key, u32 pid)\n{\n  switch (protocol) {\n  case TCPPROTO_HTTP:\n    return std::make_shared<ProtocolHandler_HTTP>(this, key, pid);\n  case TCPPROTO_UNKNOWN:\n    return std::make_shared<ProtocolHandler_UNKNOWN>(this, key, pid);\n  default:\n    throw std::runtime_error(\"invalid protocol\");\n  }\n  return nullptr;\n}\n\nvoid TCPDataHandler::upgrade_protocol_handler(\n    std::shared_ptr<ProtocolHandlerBase> new_handler, std::shared_ptr<ProtocolHandlerBase> original_handler)\n{\n  tcp_control_key_t key = original_handler->control_key();\n#ifndef NDEBUG\n  tcp_control_key_t newkey = new_handler->control_key();\n  assert(key.sk == newkey.sk);\n#endif\n\n  auto it = protocol_handlers_.find(key);\n  if (it == protocol_handlers_.end()) {\n    throw std::runtime_error(\"update from invalid protocol\");\n  }\n\n  it->second = new_handler;\n}\n\n// toggle enabling one side of a stream\nvoid TCPDataHandler::enable_stream(const tcp_control_key_t &key, STREAM_TYPE stream_type, bool enable)\n{\n  // Get the file descriptor for the tcp control map\n  int fd = tcp_control_map_fd_;\n\n  // Get the key pointer in a form that bpf can accept\n  void *keyptr = const_cast<void *>(static_cast<const void *>(&key));\n\n  // Get the previous value\n  tcp_control_value_t value;\n  int err;\n  if ((err = bpf_map_lookup_elem(fd, keyptr, &value)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, lookup failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n\n  // Change the desired values\n  value.streams[stream_type].enable = enable;\n\n  // Update the value if it is still in the table\n  if ((err = bpf_map_update_elem(fd, keyptr, &value, BPF_EXIST)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, update failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"enable_stream: sk={}, stream_type={}, enable={}\",\n      key.sk,\n      stream_type_to_string(stream_type),\n      enable ? \"true\" : \"false\");\n}\n\n// toggle enabling both sides of a stream\nvoid TCPDataHandler::enable_stream(const tcp_control_key_t &key, bool enable)\n{\n  // Get the file descriptor for the tcp control map\n  int fd = tcp_control_map_fd_;\n\n  // Get the key pointer in a form that bpf can accept\n  void *keyptr = const_cast<void *>(static_cast<const void *>(&key));\n\n  // Get the previous value\n  tcp_control_value_t value;\n  int err;\n  if ((err = bpf_map_lookup_elem(fd, keyptr, &value)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, lookup failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n\n  // Change the desired values\n  value.streams[ST_SEND].enable = enable;\n  value.streams[ST_RECV].enable = enable;\n\n  // Update the value if it is still in the table\n  if ((err = bpf_map_update_elem(fd, keyptr, &value, BPF_EXIST)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, update failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL, \"enable_stream: sk={:x}, stream_type=BOTH, enable={}\", key.sk, enable ? \"true\" : \"false\");\n}\n\nvoid TCPDataHandler::update_stream_start(const tcp_control_key_t &key, STREAM_TYPE stream_type, u64 start)\n{\n  // Get the file descriptor for the tcp control map\n  int fd = tcp_control_map_fd_;\n\n  // Get the key pointer in a form that bpf can accept\n  void *keyptr = const_cast<void *>(static_cast<const void *>(&key));\n\n  // Get the previous value\n  tcp_control_value_t value;\n  int err;\n  if ((err = bpf_map_lookup_elem(fd, keyptr, &value)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, lookup failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n\n  // Change the desired values\n  value.streams[stream_type].start = start;\n\n  // Update the value if it is still in the table\n  if ((err = bpf_map_update_elem(fd, keyptr, &value, BPF_EXIST)) < 0) {\n    // It's okay for this to fail, it means that BPF deleted the tcp connection record\n    // before this got called, which means we should do nothing in this case\n    LOG::debug_in(\n        AgentLogKind::PROTOCOL, \"enable_stream called on non-existing key, update failed, sk={:x}, err={}\", key.sk, err);\n    return;\n  }\n\n  LOG::debug_in(\n      AgentLogKind::PROTOCOL,\n      \"update_stream_start: sk={:x}, stream_type={}, start={}\",\n      key.sk,\n      stream_type_to_string(stream_type),\n      start);\n}\n\nvoid TCPDataHandler::process(\n    size_t idx, u64 tstamp, u64 sk, u32 pid, u32 length, u64 offset, STREAM_TYPE stream_type, CLIENT_SERVER_TYPE client_server)\n{\n  // Send to the appropriate protocol handler\n  tcp_control_key_t key{.sk = sk};\n\n  // find the appropriate protocol handler\n  auto phiter = protocol_handlers_.find(key);\n  if (phiter == protocol_handlers_.end()) {\n    // data for new socket, so create a protocol handler for it\n    auto ret = protocol_handlers_.insert(std::make_pair(key, create_protocol_handler(TCPPROTO_UNKNOWN, key, pid)));\n    phiter = ret.first;\n  }\n\n  // Get the data ring for the same cpu as the control ring\n  // we're on to ensure we get the right data\n  PerfRing &ring = container_.data_ring(idx);\n\n  u32 length_remaining = length;\n  u64 current_offset = offset;\n\n  while (length_remaining > 0) {\n    auto ring_size = ring.peek_size();\n\n    if (ring_size == -ENOENT) {\n      LOG::debug_in(AgentLogKind::PROTOCOL, \"TCPDataHandler::process: ring buffer is empty\");\n      break;\n    }\n\n    // If we have an sample, process it\n    // peek_type assumes ring is non-empty\n    int type = ring.peek_type();\n\n    if (type == PERF_RECORD_SAMPLE) {\n\n      u32 padded_chunk_length = ring_size;\n\n      // PERF_SAMPLE_RAW adds 32 bits of length per documentation of perf_event_open\n      const unsigned int min_length = sizeof(u32);\n      // round up max_length to a multiple of 8 bytes for padding\n      const unsigned int max_length = ((sizeof(u32) + DATA_CHANNEL_CHUNK_MAX) + 7) & ~7;\n\n      // Ensure we don't overflow\n      if (padded_chunk_length < min_length) {\n        log_.error(\"got message < sizeof header: padded_chunk_length({}) < min_length({})\", padded_chunk_length, min_length);\n        return;\n      }\n      if (padded_chunk_length > max_length) {\n        log_.error(\"got message > sizeof message: padded_chunk_length({}) > max_length({})\", padded_chunk_length, max_length);\n        return;\n      }\n\n      // process tcp data\n      LOG::debug_in(AgentLogKind::PROTOCOL, \"tcp_data_handler: processing data (padded_chunk_length={})\", padded_chunk_length);\n\n      char buf[max_length];\n\n      /* copy into buffer */\n      ring.peek_copy(buf, 0, padded_chunk_length);\n      /* release the element */\n      ring.pop();\n\n      // get pointer to data and length of data\n      data_channel_header_t *header = (data_channel_header_t *)(buf + sizeof(u32));\n      const u8 *data = (const u8 *)(buf + sizeof(u32) + sizeof(data_channel_header_t));\n      u32 data_len = header->length;\n\n      // make sure we don't read past end\n      const unsigned int chunk_length = data_len + sizeof(u32) + sizeof(data_channel_header_t);\n      if (chunk_length > padded_chunk_length) {\n        log_.error(\n            \"got chunk_length > padded_chunk_length: chunk_length({}) > padded_chunk_length({})\",\n            chunk_length,\n            padded_chunk_length);\n        return;\n      }\n\n      // make sure our read won't take us past the length remaining on this data\n      if (data_len > length_remaining) {\n        // there is more data in the buffer than we need\n        // just process the remaining length\n        data_len = length_remaining;\n      }\n\n      // process the data with the protocol handler\n      // possibly upgrading the handler\n      ProtocolHandlerBase::ptr_type phb = phiter->second;\n\n      bool do_upgrade;\n      do {\n        do_upgrade = false;\n\n        if (client_server == SC_SERVER) {\n          phb->handle_server_data(tstamp, current_offset, stream_type, data, data_len);\n        } else {\n          phb->handle_client_data(tstamp, current_offset, stream_type, data, data_len);\n        }\n\n        ProtocolHandlerBase::ptr_type upgrade = phb->get_upgrade();\n        if (upgrade) {\n          upgrade_protocol_handler(upgrade, phb);\n          phb = upgrade;\n          do_upgrade = true;\n        }\n      } while (do_upgrade);\n\n      // offset for the next chunk\n      current_offset += data_len;\n      length_remaining -= data_len;\n\n    } else if (type == PERF_RECORD_LOST) {\n\n      u64 n_lost = ring.peek_aligned_u64(sizeof(u64));\n      ring.pop();\n\n      lost_record_total_count_ = lost_record_total_count_ + n_lost;\n\n      log_.warn(\"tcp_data_handler: lost {} records ({} total)\", n_lost, lost_record_total_count_);\n    } else {\n      log_.error(\"tcp_data_handler: unexpected record type {}\", type);\n      // skip this one\n      ring.pop();\n    }\n  }\n}\n\nvoid TCPDataHandler::handle_close_socket(u64 sk)\n{\n  // Send to the appropriate protocol handler\n  tcp_control_key_t key{\n      .sk = sk,\n  };\n\n  // end-of-socket\n  auto phiter = protocol_handlers_.find(key);\n  if (phiter == protocol_handlers_.end()) {\n    // This is okay since sockets that send no data won't have a protocol handler\n    // LOG::debug_in(AgentLogKind::PROTOCOL, \"missing protocol handler for key(sk={:x})\", key.sk);\n  } else {\n    // remove protocol handler at end of socket\n    protocol_handlers_.erase(phiter);\n  }\n}\n"
  },
  {
    "path": "collector/kernel/tcp_data_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <linux/bpf.h>\n\n#include <map>\n#include <memory>\n\n#include <generated/ebpf_net/ingest/writer.h>\n#include <platform/platform.h>\n#include <util/logger.h>\n#include <uv.h>\n\n#include \"collector/agent_log.h\"\n#include \"collector/kernel/bpf_src/tcp-processor/tcp_processor.h\"\n#include \"collector/kernel/perf_reader.h\"\n#include \"protocols/protocol_handler_base.h\"\n\n/* forward declarations */\nstruct render_bpf_bpf;\nclass ProbeHandler;\n\nclass TCPDataHandler {\npublic:\n  /**\n   * c'tor\n   */\n  TCPDataHandler(\n      uv_loop_t &loop,\n      ProbeHandler &probe_handler,\n      struct render_bpf_bpf *skel,\n      ::ebpf_net::ingest::Writer &writer,\n      PerfContainer &container,\n      logging::Logger &log);\n\n  /**\n   * d'tor\n   */\n  virtual ~TCPDataHandler();\n\n  // Data processing entrypoint\n  void process(\n      size_t idx,\n      u64 tstamp,\n      u64 sk,\n      u32 pid,\n      u32 length,\n      u64 offset,\n      STREAM_TYPE stream_type,\n      CLIENT_SERVER_TYPE client_server);\n\n  void handle_close_socket(u64 sk);\n\n  // Output\n  inline ::ebpf_net::ingest::Writer &writer() { return writer_; }\n\n  // tcp kernel->userland throttling control backchannel\n  void enable_stream(const tcp_control_key_t &key, STREAM_TYPE stream_type, bool enable);\n  void enable_stream(const tcp_control_key_t &key, bool enable);\n  void update_stream_start(const tcp_control_key_t &key, STREAM_TYPE stream_type, u64 start);\n\n  // create a new protocol handler, possibly for upgrade\n  ProtocolHandlerBase::ptr_type create_protocol_handler(int protocol, const tcp_control_key_t &key, u32 pid);\n\nprotected:\n  void upgrade_protocol_handler(ProtocolHandlerBase::ptr_type new_handler, ProtocolHandlerBase::ptr_type original_handler);\n\n  struct tcp_control_key_t_comparator {\n    bool operator()(const tcp_control_key_t &a, const tcp_control_key_t &b) const;\n  };\n\nprotected:\n  uv_loop_t &loop_;\n  struct render_bpf_bpf *skel_;\n  ::ebpf_net::ingest::Writer &writer_;\n  PerfContainer &container_;\n  u64 lost_record_total_count_ = 0;\n  std::map<tcp_control_key_t, std::shared_ptr<ProtocolHandlerBase>, tcp_control_key_t_comparator> protocol_handlers_;\n  int tcp_control_map_fd_;\n  logging::Logger &log_;\n};\n"
  },
  {
    "path": "collector/kernel/troubleshoot_item.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME TroubleshootItem\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(bpf_load_probes_failed, 1, \"\")                                                                                             \\\n  X(operation_not_permitted, 2, \"\")                                                                                            \\\n  X(permission_denied, 3, \"\")                                                                                                  \\\n  X(unexpected_exception, 4, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "collector/kernel/troubleshooting.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <collector/kernel/troubleshooting.h>\n\n#include <common/linux_distro.h>\n#include <util/log_formatters.h>\n\n#include <spdlog/fmt/fmt.h>\n#include <spdlog/fmt/ostr.h>\n\n#include <iostream>\n#include <string_view>\n#include <thread>\n\n#include <cstdlib>\n\nstatic constexpr auto EXIT_SLEEP_GRACE_PERIOD_DEFAULT = 60s;\nstatic constexpr auto EXIT_SLEEP_FOREVER = std::chrono::seconds::max();\n\nvoid close_agent(int exit_code, std::function<void()> flush_and_close, std::chrono::seconds exit_sleep_sec)\n{\n  if (flush_and_close) {\n    flush_and_close();\n  }\n#if NDEBUG // delay exit for release builds but not for debug builds to prevent test failures from blocking\n  std::this_thread::sleep_for(exit_sleep_sec);\n#endif\n  exit(exit_code);\n}\n\nvoid print_troubleshooting_message_and_exit(\n    HostInfo const &info,\n    TroubleshootItem item,\n    std::exception const &e,\n    std::optional<std::reference_wrapper<logging::Logger>> log,\n    std::function<void()> flush_and_close)\n{\n  if (item == TroubleshootItem::none) {\n    return;\n  }\n\n  auto const item_name = to_string(item);\n  auto const os_name = to_string(info.os);\n  auto const distro_name = to_string(static_cast<LinuxDistro>(info.os_flavor));\n  auto const headers_source = to_string(info.kernel_headers_source);\n  auto const exception_message = std::string(e.what());\n\n  auto const item_fmt = fmt::format(\n      \"troubleshoot item {} (os={},flavor={},headers_src={},kernel={}): {}\",\n      item_name,\n      os_name,\n      distro_name,\n      headers_source,\n      info.kernel_version,\n      exception_message);\n\n  if (log) {\n    log->get().error(item_fmt);\n  } else {\n    LOG::error(item_fmt);\n  }\n\n  auto exit_sleep_sec = EXIT_SLEEP_GRACE_PERIOD_DEFAULT;\n  switch (item) {\n\n  case TroubleshootItem::bpf_load_probes_failed: {\n    std::cout << fmt::format(\n        R\"TROUBLESHOOTING(\nFailed to load eBPF probes for the Linux distro '{}' running kernel version {}.\n\n{}\n\n)TROUBLESHOOTING\",\n        distro_name,\n        info.kernel_version,\n        item_fmt);\n    break;\n  }\n\n  case TroubleshootItem::operation_not_permitted: {\n    exit_sleep_sec = EXIT_SLEEP_FOREVER;\n    std::cout << fmt::format(\n        R\"TROUBLESHOOTING(\nInsufficient permissions to perform a priviliged operation.\nPriviliged operations include mounting debugfs, loading eBPF code and probes, etc.\nMake sure to run as privileged user, and/or with --privileged flag or equivalant.\n\n{}\n\nBlocking to avoid failure retry loop.\n\n)TROUBLESHOOTING\",\n        item_fmt);\n    break;\n  }\n\n  case TroubleshootItem::permission_denied: {\n    exit_sleep_sec = EXIT_SLEEP_FOREVER;\n    std::cout << fmt::format(\n        R\"TROUBLESHOOTING(\nOperation failed with permission denied.\nThis can occur due to SELinux policy enforcement.  If SELinux is enabled, make sure to first run\nthe provided script to apply an SELinux policy allowing eBPF operations.\n\n{}\n\nBlocking to avoid failure retry loop.\n\n)TROUBLESHOOTING\",\n        item_fmt);\n    break;\n  }\n\n  case TroubleshootItem::unexpected_exception: {\n    std::cout << fmt::format(\n        R\"TROUBLESHOOTING(\nUnexpected exception.\n\n{}\n\n)TROUBLESHOOTING\",\n        item_fmt);\n    break;\n  }\n\n  default: {\n    std::cout << R\"TROUBLESHOOTING(\nUnknown error happened in the Kernel Collector.\n\n)TROUBLESHOOTING\";\n    break;\n  }\n  }\n\n  std::cout << std::endl;\n  std::cout.flush();\n\n  close_agent(-1, flush_and_close, exit_sleep_sec);\n}\n"
  },
  {
    "path": "collector/kernel/troubleshooting.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <collector/kernel/troubleshoot_item.h>\n#include <common/host_info.h>\n#include <util/logger.h>\n\n/**\n * Prints a troubleshooting message for the given item.\n *\n * In case the item is unrecoverable, calls `exit()` and doesn't return.\n */\nvoid print_troubleshooting_message_and_exit(\n    HostInfo const &info,\n    TroubleshootItem item,\n    std::exception const &e,\n    std::optional<std::reference_wrapper<logging::Logger>> log = std::nullopt,\n    std::function<void()> flush_and_close = nullptr);\n"
  },
  {
    "path": "collector/server_command.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\nenum class ServerCommand : u64 {\n  NONE,\n  DISABLE_SEND = 0xe5c94272c6a3028ful,\n};\n"
  },
  {
    "path": "common/client_server_type.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This enum is used in BPF, Agent, and Server. Must be a normal enum because BPF/BCC is C, not C++.\n\n// Server/client type enum\nenum CLIENT_SERVER_TYPE {\n  SC_CLIENT = 0,\n  SC_SERVER = 1,\n};\n\n#ifndef _PROCESSING_BPF\n\ninline const char *client_server_type_to_string(enum CLIENT_SERVER_TYPE client_server)\n{\n  switch (client_server) {\n  case SC_CLIENT:\n    return \"CLIENT\";\n  case SC_SERVER:\n    return \"SERVER\";\n  default:\n    break;\n  }\n  throw std::runtime_error(\"invalid CLIENT_SERVER_TYPE value\");\n}\n\n#endif\n"
  },
  {
    "path": "common/client_type.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME ClientType\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(kernel, 1, \"\")                                                                                                             \\\n  X(cloud, 2, \"\")                                                                                                              \\\n  X(k8s, 3, \"\")                                                                                                                \\\n  X(ingest, 4, \"\")                                                                                                             \\\n  X(matching, 5, \"\")                                                                                                           \\\n  X(aggregation, 6, \"\")                                                                                                        \\\n  X(liveness_probe, 7, \"\")                                                                                                     \\\n  X(readiness_probe, 8, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/cloud_platform.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME CloudPlatform\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(aws, 1, \"\")                                                                                                                \\\n  X(gcp, 2, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/collected_blob_type.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME CollectedBlobType\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(proc_pid_stat, 1, \"\")                                                                                                      \\\n  X(proc_pid_io, 2, \"\")                                                                                                        \\\n  X(proc_pid_status, 3, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/collector_status.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME CollectorStatus\n#define ENUM_NAMESPACE collector\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(healthy, 0, \"\")                                                                                                            \\\n  X(unknown, 1, \"\")                                                                                                            \\\n  X(aws_describe_regions_error, 2, \"\")                                                                                         \\\n  X(aws_describe_network_interfaces_error, 3, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/component.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE common\n#define ENUM_NAME Component\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(debug_data, 1, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/constants.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config.h>\n\n#include <util/preprocessor.h>\n#include <util/version.h>\n\n#include <string>\n\nnamespace kernel {\nconstexpr u16 MIN_CGROUP_CPU_SHARES = 2;\nconstexpr u16 MAX_CGROUP_CPU_SHARES = 1024;\nconstexpr u32 DEFAULT_CGROUP_QUOTA = 100'000;\n} // namespace kernel\n\nnamespace versions {\n\nextern const VersionInfo release;\n\n} // namespace versions\n"
  },
  {
    "path": "common/host_info.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/kernel_headers_source.h>\n#include <common/linux_distro.h>\n#include <common/operating_system.h>\n\n#include <cstdint>\n\nstruct HostInfo {\n  OperatingSystem const os;\n  std::uint8_t const os_flavor;\n  std::string const os_version;\n  KernelHeadersSource const kernel_headers_source;\n  std::string const kernel_version;\n  std::string const hostname;\n};\n"
  },
  {
    "path": "common/http_status_code.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#include <type_traits>\n\n#include <cstdint>\n\n#define ENUM_NAME HttpStatusCode\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n                                                                                                                               \\\n  /* informational */                                                                                                          \\\n  X(Continue, 100, \"\")                                                                                                         \\\n  X(SwitchingProtocols, 101, \"\")                                                                                               \\\n  X(Processing, 102, \"\")                                                                                                       \\\n  X(EarlyHints, 103, \"\")                                                                                                       \\\n                                                                                                                               \\\n  /* success */                                                                                                                \\\n  X(OK, 200, \"\")                                                                                                               \\\n  X(Created, 201, \"\")                                                                                                          \\\n  X(Accepted, 202, \"\")                                                                                                         \\\n  X(NonAuthoritativeInformation, 203, \"\")                                                                                      \\\n  X(NoContent, 204, \"\")                                                                                                        \\\n  X(ResetContent, 205, \"\")                                                                                                     \\\n  X(PartialContent, 206, \"\")                                                                                                   \\\n  X(MultiStatus, 207, \"\")                                                                                                      \\\n  X(AlreadyReported, 208, \"\")                                                                                                  \\\n  X(InstanceManipulationUsed, 226, \"\")                                                                                         \\\n                                                                                                                               \\\n  /* redirection */                                                                                                            \\\n  X(MultipleChoices, 300, \"\")                                                                                                  \\\n  X(MovedPermanently, 301, \"\")                                                                                                 \\\n  X(Found, 302, \"\")                                                                                                            \\\n  X(SeeOther, 303, \"\")                                                                                                         \\\n  X(NotModified, 304, \"\")                                                                                                      \\\n  X(UseProxy, 305, \"\")                                                                                                         \\\n  X(SwitchProxy, 306, \"\")                                                                                                      \\\n  X(TemporaryRedirect, 307, \"\")                                                                                                \\\n  X(PermanentRedirect, 308, \"\")                                                                                                \\\n                                                                                                                               \\\n  /* client error */                                                                                                           \\\n  X(BadRequest, 400, \"\")                                                                                                       \\\n  X(Unauthorized, 401, \"\")                                                                                                     \\\n  X(PaymentRequired, 402, \"\")                                                                                                  \\\n  X(Forbidden, 403, \"\")                                                                                                        \\\n  X(NotFound, 404, \"\")                                                                                                         \\\n  X(MethodNotAllowed, 405, \"\")                                                                                                 \\\n  X(NotAcceptable, 406, \"\")                                                                                                    \\\n  X(ProxyAuthenticationRequired, 407, \"\")                                                                                      \\\n  X(RequestTimeout, 408, \"\")                                                                                                   \\\n  X(Conflict, 409, \"\")                                                                                                         \\\n  X(Gone, 410, \"\")                                                                                                             \\\n  X(LengthRequired, 411, \"\")                                                                                                   \\\n  X(PreconditionFailed, 412, \"\")                                                                                               \\\n  X(PayloadTooLarge, 413, \"\")                                                                                                  \\\n  X(URITooLong, 414, \"\")                                                                                                       \\\n  X(UnsupportedMediaType, 415, \"\")                                                                                             \\\n  X(RangeNotSatisfiable, 416, \"\")                                                                                              \\\n  X(ExpectationFailed, 417, \"\")                                                                                                \\\n  X(ImATeapot, 418, \"\")                                                                                                        \\\n  X(MisdirectedRequest, 421, \"\")                                                                                               \\\n  X(UnprocessableEntity, 422, \"\")                                                                                              \\\n  X(Locked, 423, \"\")                                                                                                           \\\n  X(FailedDependency, 424, \"\")                                                                                                 \\\n  X(TooEarly, 425, \"\")                                                                                                         \\\n  X(UpgradeRequired, 426, \"\")                                                                                                  \\\n  X(PreconditionRequired, 428, \"\")                                                                                             \\\n  X(TooManyRequests, 429, \"\")                                                                                                  \\\n  X(RequestHeaderFieldsTooLarge, 431, \"\")                                                                                      \\\n  X(UnavailableForLegalReasons, 451, \"\")                                                                                       \\\n                                                                                                                               \\\n  /* server error */                                                                                                           \\\n  X(InternalServerError, 500, \"\")                                                                                              \\\n  X(NotImplemented, 501, \"\")                                                                                                   \\\n  X(BadGateway, 502, \"\")                                                                                                       \\\n  X(ServiceUnavailable, 503, \"\")                                                                                               \\\n  X(GatewayTimeout, 504, \"\")                                                                                                   \\\n  X(HTTPVersionNotSupported, 505, \"\")                                                                                          \\\n  X(VariantAlsoNegotiates, 506, \"\")                                                                                            \\\n  X(InsufficientStorage, 507, \"\")                                                                                              \\\n  X(LoopDetected, 508, \"\")                                                                                                     \\\n  X(NotExtended, 510, \"\")                                                                                                      \\\n  X(NetworkAuthenticationRequired, 511, \"\")\n#include <util/enum_operators.inl>\n\nconstexpr bool is_informational_class(HttpStatusCode code)\n{\n  auto const value = static_cast<std::underlying_type_t<HttpStatusCode>>(code);\n  return value >= 100 && value < 200;\n}\n\nconstexpr bool is_success_class(HttpStatusCode code)\n{\n  auto const value = static_cast<std::underlying_type_t<HttpStatusCode>>(code);\n  return value >= 200 && value < 300;\n}\n\nconstexpr bool is_redirection_class(HttpStatusCode code)\n{\n  auto const value = static_cast<std::underlying_type_t<HttpStatusCode>>(code);\n  return value >= 300 && value < 400;\n}\n\nconstexpr bool is_client_error_class(HttpStatusCode code)\n{\n  auto const value = static_cast<std::underlying_type_t<HttpStatusCode>>(code);\n  return value >= 400 && value < 500;\n}\n\nconstexpr bool is_server_error_class(HttpStatusCode code)\n{\n  auto const value = static_cast<std::underlying_type_t<HttpStatusCode>>(code);\n  return value >= 500 && value < 600;\n}\n"
  },
  {
    "path": "common/intake_encoder.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME IntakeEncoder\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X) X(binary, 0, \"\")\n#define ENUM_DEFAULT binary\n#include <util/enum_operators.inl>\n\ninline std::string_view format_as(IntakeEncoder v)\n{\n  return to_string(v);\n}\n"
  },
  {
    "path": "common/kernel_headers_source.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME KernelHeadersSource\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(pre_installed, 1, \"\")                                                                                                      \\\n  X(pre_fetched, 2, \"\")                                                                                                        \\\n  X(dont_fetch, 3, \"\")                                                                                                         \\\n  X(fetched, 4, \"\")                                                                                                            \\\n  X(libbpf, 5, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/linux_distro.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME LinuxDistro\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(debian, 1, \"\")                                                                                                             \\\n  X(ubuntu, 2, \"\")                                                                                                             \\\n  X(rhel, 3, \"\")                                                                                                               \\\n  X(centos, 4, \"\")                                                                                                             \\\n  X(amazon, 5, \"\")                                                                                                             \\\n  X(gcp_cos, 6, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/operating_system.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME OperatingSystem\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(Linux, 1, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "common/port_protocol.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME PortProtocol\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(TCP, 1, \"\")                                                                                                                \\\n  X(UDP, 2, \"\")                                                                                                                \\\n  X(HTTP, 3, \"\")                                                                                                               \\\n  X(PROXY, 4, \"\")                                                                                                              \\\n  X(SCTP, 5, \"\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "config/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_library(\n  config_file\n  STATIC\n    config_file.cc\n)\ntarget_link_libraries(\n  config_file\n    intake_config\n    yamlcpp\n    spdlog\n)\n\nadd_library(\n  intake_config\n  STATIC\n    intake_config.cc\n)\ntarget_link_libraries(\n  intake_config\n    render_ebpf_net_ingest_writer\n    tcp_channel\n    libuv-interface\n    args_parser\n    file_ops\n    environment_variables\n)\n"
  },
  {
    "path": "config/config_file.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config/config_file.h>\n\n#include <util/log.h>\n#include <yaml-cpp/yaml.h>\n\nnamespace config {\n\nConfigFile::ConfigFile(YamlFormat, std::string const &path, FailMode fail) : intake_config_(IntakeConfig::DEFAULT_CONFIG)\n{\n  if (path.empty()) {\n    return;\n  }\n\n  try {\n    YAML::Node yaml = YAML::LoadFile(path);\n\n    if (auto labels = yaml[\"labels\"]) {\n      if (!labels.IsMap() && !labels.IsNull()) {\n        LOG::warn(\"Ignoring 'labels' in config file: 'labels' should be a map.\");\n      } else {\n        for (auto const &i : labels) {\n          auto key = i.first.as<std::string>();\n          auto value = i.second.as<std::string>();\n\n          if ((key.length() > 20) || (value.length() > 40)) {\n            LOG::warn(\n                \"Ignoring label '{}': '{}'. \"\n                \"key and value lengths must be max 20, 40 chars.\",\n                key,\n                value);\n            continue;\n          }\n\n          LOG::info(\"Node label has been set in config: '{}':'{}'\", key, value);\n          labels_[std::move(key)] = std::move(value);\n        }\n      }\n    } else {\n      LOG::info(\"No \\\"labels\\\" were specified.\");\n    }\n\n    if (auto intake = yaml[\"intake\"]) {\n      if (!intake.IsMap() && !intake.IsNull()) {\n        LOG::warn(\"Ignoring 'intake' in config file: 'intake' should be a map.\");\n      } else {\n        auto host = intake[\"host\"].as<std::string>();\n        auto port = intake[\"port\"].as<std::string>();\n\n        intake_config_.host(host);\n        intake_config_.port(port);\n      }\n    }\n  } catch (std::exception const &e) {\n    LOG::error(\"Config file at {} could not be loaded.\", path);\n\n    if (fail == FailMode::exception) {\n      throw e;\n    }\n  }\n}\n\n} // namespace config\n"
  },
  {
    "path": "config/config_file.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config/intake_config.h>\n\n#include <map>\n#include <string>\n\nnamespace config {\n\nclass ConfigFile {\npublic:\n  struct YamlFormat {};\n  enum class FailMode { silent, exception };\n\n  ConfigFile(YamlFormat, std::string const &path, FailMode fail = FailMode::exception);\n\n  using LabelsMap = std::map<std::string, std::string>;\n  LabelsMap const &labels() const { return labels_; }\n  LabelsMap &labels() { return labels_; }\n\n  IntakeConfig const &intake_config() const { return intake_config_; }\n\nprivate:\n  LabelsMap labels_;\n  IntakeConfig intake_config_;\n};\n\n} // namespace config\n"
  },
  {
    "path": "config/intake_config.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config/intake_config.h>\n\n#include <channel/tcp_channel.h>\n#include <util/environment_variables.h>\n#include <util/log.h>\n\n#include <util/utility.h>\n\n#include <cstdlib>\n\nnamespace config {\n\nconst IntakeConfig IntakeConfig::DEFAULT_CONFIG = IntakeConfig(\n    \"127.0.0.1\",          // host\n    \"8000\",               // port\n    \"\",                   // record_output_path\n    IntakeEncoder::binary // encoder\n);\n\nFileDescriptor IntakeConfig::create_output_record_file() const\n{\n  FileDescriptor fd;\n\n  LOG::debug(\"intake record file: `{}`\", record_path_);\n  if (!record_path_.empty()) {\n    if (auto const error = fd.create(record_path_.c_str(), FileDescriptor::Access::write_only)) {\n      LOG::error(\"failed to create intake record file at `{}`\", record_path_);\n    } else {\n      LOG::debug(\"created intake record file at `{}`\", record_path_);\n    }\n  }\n\n  return fd;\n}\n\nstd::unique_ptr<channel::NetworkChannel> IntakeConfig::make_channel(uv_loop_t &loop) const\n{\n  if (host_.empty()) {\n    throw std::invalid_argument(\"missing intake host value\");\n  }\n\n  if (port_.empty()) {\n    throw std::invalid_argument(\"missing intake port value\");\n  }\n\n  return std::make_unique<channel::TCPChannel>(loop, host_, port_);\n}\n\nvoid IntakeConfig::read_from_env(IntakeConfig &config)\n{\n  if (std::string_view value = try_get_env_var(INTAKE_HOST_VAR); !value.empty()) {\n    config.host_ = value;\n  }\n\n  if (std::string_view value = try_get_env_var(INTAKE_PORT_VAR); !value.empty()) {\n    config.port_ = value;\n  }\n\n  if (std::string_view value = try_get_env_var(INTAKE_RECORD_OUTPUT_PATH_VAR); !value.empty()) {\n    config.record_path_ = value;\n  }\n\n  if (std::string_view value = try_get_env_var(INTAKE_INTAKE_ENCODER_VAR); !value.empty()) {\n    config.encoder_ = try_enum_from_string(value, IntakeEncoder::binary);\n  }\n}\n\nIntakeConfig::ArgsHandler::ArgsHandler(cli::ArgsParser &parser)\n    : host_(parser.add_arg<std::string>(\n          \"intake-host\", \"IP address or host name of the reducer to which telemetry is to be sent\")),\n      port_(parser.add_arg<std::string>(\n          \"intake-port\", \"TCP port number on which the reducer is listening for collector connections\")),\n      encoder_(parser.add_arg<IntakeEncoder>(\n          \"intake-encoder\",\n          \"Chooses the intake encoder to use\"\n          \" - this relates to the sink used to dump collected telemetry to\"))\n{}\n\nvoid IntakeConfig::ArgsHandler::read_config(IntakeConfig &config)\n{\n  IntakeConfig::read_from_env(config);\n\n  if (host_) {\n    config.host(*host_);\n  }\n\n  if (port_) {\n    config.port(*port_);\n  }\n\n  if (encoder_) {\n    config.encoder(*encoder_);\n  }\n}\n\n} // namespace config\n"
  },
  {
    "path": "config/intake_config.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/network_channel.h>\n#include <common/intake_encoder.h>\n#include <util/args_parser.h>\n#include <util/file_ops.h>\n\n#include <generated/ebpf_net/ingest/encoder.h>\n\n#include <spdlog/fmt/ostr.h>\n\n#include <uv.h>\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\nnamespace config {\n\nclass IntakeConfig {\n  // environment variable names used by `read_from_env()`\n  static constexpr auto INTAKE_HOST_VAR = \"EBPF_NET_INTAKE_HOST\";\n  static constexpr auto INTAKE_PORT_VAR = \"EBPF_NET_INTAKE_PORT\";\n  static constexpr auto INTAKE_INTAKE_ENCODER_VAR = \"EBPF_NET_INTAKE_ENCODER\";\n  static constexpr auto INTAKE_RECORD_OUTPUT_PATH_VAR = \"EBPF_NET_RECORD_INTAKE_OUTPUT_PATH\";\n\npublic:\n  static const IntakeConfig DEFAULT_CONFIG;\n\n  IntakeConfig() {}\n\n  /**\n   * Constructs an intake config object.\n   *\n   * host: the host to connect to for intake\n   * port: the port to connect to for intake\n   * secondary_output: when given, path to a file into which to record all traffic sent upstream\n   */\n  IntakeConfig(\n      std::string host,\n      std::string port,\n      std::string record_output_path = {},\n      IntakeEncoder encoder = IntakeEncoder::binary)\n      : host_(std::move(host)),\n        port_(std::move(port)),\n        record_path_(std::move(record_output_path)),\n        encoder_(encoder)\n  {}\n\n  std::string const &host() const { return host_; }\n  std::string const &port() const { return port_; }\n\n  void host(std::string const &host) { host_ = host; }\n  void port(std::string const &port) { port_ = port; }\n\n  /**\n   * If a secondary output has been set, opens or creates the output file and\n   * returns its file descriptor.\n   *\n   * Otherwise, returns an invalid file descriptor.\n   */\n  FileDescriptor create_output_record_file() const;\n\n  void encoder(IntakeEncoder encoder) { encoder_ = encoder; }\n  IntakeEncoder encoder() const { return encoder_; }\n\n  virtual bool allow_compression() const { return encoder_ == IntakeEncoder::binary; }\n\n  virtual std::unique_ptr<channel::NetworkChannel> make_channel(uv_loop_t &loop) const;\n\n  std::unique_ptr<::ebpf_net::ingest::Encoder> make_encoder() const\n  {\n    switch (encoder_) {\n    default:\n      // Use default encoder.\n      return nullptr;\n    }\n  }\n\n  /**\n   * Reads intake configuration from existing environment variables.\n   *\n   * NOTE: this function reads environment variables so it's advisable to call it\n   * before any thread is created, given that reading/writing to environment\n   * variables is not thread safe and we can't control 3rd party libraries.\n   */\n  static void read_from_env(IntakeConfig &config);\n\n  template <typename Out> friend Out &&operator<<(Out &&out, IntakeConfig const &config)\n  {\n    out << config.host_ << ':' << config.port_ << \" (\" << config.encoder_ << ')';\n\n    return std::forward<Out>(out);\n  }\n\n  struct ArgsHandler;\n\nprivate:\n  std::string host_;\n  std::string port_;\n  std::string record_path_;\n  IntakeEncoder encoder_ = IntakeEncoder::binary;\n};\n\nstruct IntakeConfig::ArgsHandler : cli::ArgsParser::Handler {\n  ArgsHandler(cli::ArgsParser &parser);\n\n  void read_config(IntakeConfig &config);\n\nprivate:\n  cli::ArgsParser::ArgProxy<std::string> host_;\n  cli::ArgsParser::ArgProxy<std::string> port_;\n  cli::ArgsParser::ArgProxy<IntakeEncoder> encoder_;\n};\n\n} // namespace config\n\nnamespace fmt {\ntemplate <> struct formatter<config::IntakeConfig> : fmt::ostream_formatter {};\n} // namespace fmt\n"
  },
  {
    "path": "config.h.cmake_in",
    "content": "#cmakedefine01 CONFIGURABLE_BPF\n#cmakedefine01 DEBUG_LOG\n#cmakedefine01 ENABLE_CODE_TIMING\n#cmakedefine01 USE_ADDRESS_SANITIZER\n\n"
  },
  {
    "path": "crates/build/otn_link_build.rs",
    "content": "pub fn run() {\n    use std::env;\n\n    // Rebuild if any of the link env vars change\n    println!(\"cargo:rerun-if-env-changed=OTN_LINK_SEARCH\");\n    println!(\"cargo:rerun-if-env-changed=OTN_LINK_LIBS\");\n    println!(\"cargo:rerun-if-env-changed=OTN_LINK_ARGS\");\n\n    if let Ok(search) = env::var(\"OTN_LINK_SEARCH\") {\n        for p in search.split(':').filter(|s| !s.is_empty()) {\n            println!(\"cargo:rustc-link-search=native={}\", p);\n        }\n    }\n\n    if let Ok(libs) = env::var(\"OTN_LINK_LIBS\") {\n        for spec in libs.split(';').filter(|s| !s.is_empty()) {\n            // spec format: kind=name (e.g., static=agentlib or dylib=stdc++)\n            if let Some((kind, name)) = spec.split_once('=') {\n                println!(\"cargo:rustc-link-lib={}={}\", kind, name);\n            } else {\n                // default to \"dylib\" if kind omitted (not expected)\n                println!(\"cargo:rustc-link-lib={}\", spec);\n            }\n        }\n    }\n\n    if let Ok(args) = env::var(\"OTN_LINK_ARGS\") {\n        for arg in args.split(';').filter(|s| !s.is_empty()) {\n            println!(\"cargo:rustc-link-arg={}\", arg);\n        }\n    }\n}\n\n"
  },
  {
    "path": "crates/cloud-collector-bin/Cargo.toml",
    "content": "[package]\nname = \"cloud-collector-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ncloud-collector-sys = { path = \"../cloud-collector-sys\" }\n\n[[bin]]\nname = \"cloud-collector\"\npath = \"src/main.rs\"\n\n"
  },
  {
    "path": "crates/cloud-collector-bin/src/main.rs",
    "content": "fn main() {\n    let code = cloud_collector_sys::run_with_args(std::env::args_os());\n    std::process::exit(code);\n}\n"
  },
  {
    "path": "crates/cloud-collector-sys/Cargo.toml",
    "content": "[package]\nname = \"cloud-collector-sys\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"cloud_collector_sys\"\npath = \"src/lib.rs\"\n\n[dependencies]\nencoder_ebpf_net_ingest = { path = \"../render/ebpf_net/ingest\" }\nencoder_ebpf_net_cloud_collector = { path = \"../render/ebpf_net/cloud_collector\" }\n\n"
  },
  {
    "path": "crates/cloud-collector-sys/build.rs",
    "content": "include!(\"../build/otn_link_build.rs\");\n\nfn main() {\n    run();\n}\n"
  },
  {
    "path": "crates/cloud-collector-sys/src/lib.rs",
    "content": "use std::os::raw::{c_char, c_int};\n\n// Ensure encoder crates are linked so C++ static libs can resolve their\n// extern \"C\" symbols via our dependency graph.\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_cloud_collector;\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_ingest;\n\nextern \"C\" {\n    pub fn otn_cloud_collector_main(argc: c_int, argv: *const *const c_char) -> c_int;\n}\n\npub fn run_with_args<I, S>(args: I) -> i32\nwhere\n    I: IntoIterator<Item = S>,\n    S: Into<std::ffi::OsString>,\n{\n    use std::ffi::CString;\n\n    let mut c_strings: Vec<CString> = Vec::new();\n    for arg in args {\n        let s: std::ffi::OsString = arg.into();\n        let bytes = std::os::unix::ffi::OsStringExt::into_vec(s);\n        c_strings.push(CString::new(bytes).expect(\"argv contains NUL byte\"));\n    }\n\n    let ptrs: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();\n    unsafe { otn_cloud_collector_main(ptrs.len() as c_int, ptrs.as_ptr()) }\n}\n"
  },
  {
    "path": "crates/element-queue/Cargo.toml",
    "content": "[package]\nname = \"element-queue\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"Apache-2.0\"\n\n[lib]\nname = \"element_queue\"\npath = \"src/lib.rs\"\n\n"
  },
  {
    "path": "crates/element-queue/src/layout.rs",
    "content": "use core::ptr;\nuse core::ptr::{addr_of, addr_of_mut};\n\n/// C-compatible shared indices for ElementQueue contiguous layout.\n#[repr(C)]\n#[derive(Debug, Copy, Clone)]\npub struct ElementQueueShared {\n    pub elem_head: u32,\n    pub buf_head: u32,\n    pub elem_tail: u32,\n    pub buf_tail: u32,\n}\n\nimpl ElementQueueShared {\n    /// Initialize shared indices to zero using volatile writes to match C semantics.\n    pub unsafe fn init(shared: *mut ElementQueueShared) {\n        shared.init_zero()\n    }\n}\n\n/// Trait providing volatile read/write accessors on raw pointers to the\n/// shared header. Enables pointer-style calls like `self.shared.set_buf_head(v)`.\npub(crate) trait ElementQueueSharedOps {\n    fn init_zero(self);\n\n    // Volatile getters (READ_ONCE semantics)\n    fn get_elem_head(self) -> u32;\n    fn get_buf_head(self) -> u32;\n    fn get_elem_tail(self) -> u32;\n    fn get_buf_tail(self) -> u32;\n\n    // Volatile setters (WRITE_ONCE semantics)\n    fn set_elem_head(self, v: u32);\n    fn set_buf_head(self, v: u32);\n    fn set_elem_tail(self, v: u32);\n    fn set_buf_tail(self, v: u32);\n}\n\nimpl ElementQueueSharedOps for *mut ElementQueueShared {\n    #[inline]\n    fn init_zero(self) {\n        unsafe {\n            ptr::write_volatile(addr_of_mut!((*self).elem_head), 0);\n            ptr::write_volatile(addr_of_mut!((*self).buf_head), 0);\n            ptr::write_volatile(addr_of_mut!((*self).elem_tail), 0);\n            ptr::write_volatile(addr_of_mut!((*self).buf_tail), 0);\n        }\n    }\n\n    #[inline]\n    fn get_elem_head(self) -> u32 {\n        unsafe { ptr::read_volatile(addr_of!((*self).elem_head)) }\n    }\n    #[inline]\n    fn get_buf_head(self) -> u32 {\n        unsafe { ptr::read_volatile(addr_of!((*self).buf_head)) }\n    }\n    #[inline]\n    fn get_elem_tail(self) -> u32 {\n        unsafe { ptr::read_volatile(addr_of!((*self).elem_tail)) }\n    }\n    #[inline]\n    fn get_buf_tail(self) -> u32 {\n        unsafe { ptr::read_volatile(addr_of!((*self).buf_tail)) }\n    }\n\n    #[inline]\n    fn set_elem_head(self, v: u32) {\n        unsafe { ptr::write_volatile(addr_of_mut!((*self).elem_head), v) }\n    }\n    #[inline]\n    fn set_buf_head(self, v: u32) {\n        unsafe { ptr::write_volatile(addr_of_mut!((*self).buf_head), v) }\n    }\n    #[inline]\n    fn set_elem_tail(self, v: u32) {\n        unsafe { ptr::write_volatile(addr_of_mut!((*self).elem_tail), v) }\n    }\n    #[inline]\n    fn set_buf_tail(self, v: u32) {\n        unsafe { ptr::write_volatile(addr_of_mut!((*self).buf_tail), v) }\n    }\n}\n\n/// Compute the size of a contiguous element queue buffer in bytes.\npub fn contig_size(n_elems: u32, buf_len: u32) -> usize {\n    core::mem::size_of::<ElementQueueShared>()\n        + (n_elems as usize) * core::mem::size_of::<u32>()\n        + (buf_len as usize)\n}\n"
  },
  {
    "path": "crates/element-queue/src/lib.rs",
    "content": "#![forbid(unsafe_op_in_unsafe_fn)]\n\n/// Linux-style negative error codes preserved for parity with C implementation.\nuse crate::layout::ElementQueueSharedOps;\npub mod errno {\n    pub const EINVAL: i32 = -22;\n    pub const ENOSPC: i32 = -28;\n    pub const ENOENT: i32 = -2;\n    pub const EAGAIN: i32 = -11;\n    pub const ENOSYS: i32 = -38;\n}\n\npub mod layout;\npub mod raw;\n\n// Re-export for backwards-compatibility with the previous single-module layout.\npub use layout::{contig_size, ElementQueueShared};\npub use raw::{ElementQueue, EqError, ReadBatch, WriteBatch};\n\n/// Owned contiguous storage for an element queue, similar to MemElementQueueStorage.\npub struct MemElementQueueStorage {\n    buf: Vec<u64>,\n    n_elems: u32,\n    buf_len: u32,\n}\n\nimpl MemElementQueueStorage {\n    pub fn new(n_elems: u32, buf_len: u32) -> Self {\n        let size = contig_size(n_elems, buf_len);\n        // Allocate with u64 alignment and round up size to u64 words\n        let words = (size + 7) / 8;\n        let mut buf = vec![0u64; words];\n        // Initialize shared header\n        let ptr = buf.as_mut_ptr() as *mut u8;\n        (ptr as *mut ElementQueueShared).init_zero();\n        Self {\n            buf,\n            n_elems,\n            buf_len,\n        }\n    }\n\n    pub fn n_elems(&self) -> u32 {\n        self.n_elems\n    }\n    pub fn buf_len(&self) -> u32 {\n        self.buf_len\n    }\n    pub fn data_ptr(&self) -> *mut u8 {\n        self.buf.as_ptr() as *mut u8\n    }\n\n    pub fn make_queue(&self) -> Result<ElementQueue, EqError> {\n        // Safety: `self.buf` is contiguous and has the expected layout\n        unsafe { ElementQueue::new_from_contiguous(self.n_elems, self.buf_len, self.data_ptr()) }\n    }\n\n    /// Construct a queue over an external contiguous buffer.\n    /// Safety: `data` must have the expected layout and size.\n    pub unsafe fn queue_from_ptr(\n        data: *mut u8,\n        n_elems: u32,\n        buf_len: u32,\n    ) -> Result<ElementQueue, EqError> {\n        unsafe { ElementQueue::new_from_contiguous(n_elems, buf_len, data) }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn layout_size() {\n        let n_elems = 8u32;\n        let buf_len = 64u32;\n        let size = contig_size(n_elems, buf_len);\n        assert_eq!(\n            size,\n            core::mem::size_of::<ElementQueueShared>() + (n_elems as usize) * 4 + buf_len as usize\n        );\n    }\n\n    #[test]\n    fn write_read_basic() {\n        let storage = MemElementQueueStorage::new(8, 128);\n        let mut q = storage.make_queue().unwrap();\n\n        // Writer via guard\n        let mut wb = q.start_write();\n        wb.write(5).unwrap().copy_from_slice(b\"hello\");\n        wb.write(8).unwrap().copy_from_slice(b\"world!!!\");\n        let _ = wb.finish();\n\n        // Reader via guard\n        let rb = q.start_read();\n        // peek first\n        assert_eq!(rb.peek_len().unwrap(), 5);\n        let v1 = rb.read().unwrap();\n        assert_eq!(v1, b\"hello\");\n        // second\n        let v2 = rb.read().unwrap();\n        assert_eq!(v2, b\"world!!!\");\n        let _ = rb.finish();\n    }\n\n    #[test]\n    fn wrap_around_alignment() {\n        let storage = MemElementQueueStorage::new(8, 64);\n        let mut q = storage.make_queue().unwrap();\n\n        // Fill close to end to force wrap\n        let mut wb = q.start_write();\n        wb.write(30).unwrap().copy_from_slice(&vec![0u8; 30]);\n        wb.write(5).unwrap().copy_from_slice(&vec![1u8; 5]); // 5 -> aligned to 8\n        let _ = wb.finish();\n\n        let rb = q.start_read();\n        let _ = rb.read().unwrap();\n        let second = rb.read().unwrap();\n        assert_eq!(second.len(), 5);\n        assert!(second.iter().all(|&b| b == 1));\n        let _ = rb.finish();\n    }\n\n    #[test]\n    fn peek_value_u64() {\n        let storage = MemElementQueueStorage::new(8, 128);\n        let mut q = storage.make_queue().unwrap();\n\n        let ts: u64 = 0x1122334455667788;\n        let mut wb = q.start_write();\n        // write timestamp as 8 bytes\n        wb.write(8).unwrap().copy_from_slice(&ts.to_le_bytes());\n        let _ = wb.finish();\n\n        let rb = q.start_read();\n        let v: u64 = rb.peek_value::<u64>().unwrap();\n        assert_eq!(v, ts.to_le());\n        let got = rb.read().unwrap();\n        assert_eq!(got.len(), 8);\n        let _ = rb.finish();\n    }\n}\n"
  },
  {
    "path": "crates/element-queue/src/raw.rs",
    "content": "use core::cell::Cell;\nuse core::ptr::{self, NonNull};\nuse core::sync::atomic::{fence, Ordering};\n\nuse crate::errno;\nuse crate::layout::{ElementQueueShared, ElementQueueSharedOps};\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub enum EqError {\n    InvalidArg,\n    NoSpace,\n    NoEntry,\n    TryAgain,\n    Unexpected,\n}\n\nimpl EqError {\n    pub fn code(self) -> i32 {\n        use EqError::*;\n        match self {\n            InvalidArg => errno::EINVAL,\n            NoSpace => errno::ENOSPC,\n            NoEntry => errno::ENOENT,\n            TryAgain => errno::EAGAIN,\n            Unexpected => errno::ENOSYS,\n        }\n    }\n}\n\n/// Unsafe, low-level queue core. Mirrors the C implementation closely.\n///\n/// Safety: Methods that dereference raw pointers encapsulate the same\n/// assumptions as the original C code. Public, higher-level wrappers should\n/// be preferred when possible.\npub struct ElementQueue {\n    // Masks (size - 1), must be power-of-two sizes.\n    elem_mask: u32,\n    buf_mask: u32,\n\n    // Local cached indices, following batching protocol.\n    elem_head: u32,\n    buf_head: u32,\n    elem_tail: u32,\n    buf_tail: u32,\n\n    // Raw pointers into contiguous storage.\n    shared: *mut ElementQueueShared,\n    elems: NonNull<[u32]>,\n    data: NonNull<[u8]>,\n}\n\nimpl core::fmt::Debug for ElementQueue {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"ElementQueue\")\n            .field(\"elem_mask\", &self.elem_mask)\n            .field(\"buf_mask\", &self.buf_mask)\n            .field(\"elem_head\", &self.elem_head)\n            .field(\"buf_head\", &self.buf_head)\n            .field(\"elem_tail\", &self.elem_tail)\n            .field(\"buf_tail\", &self.buf_tail)\n            .finish()\n    }\n}\n\nimpl ElementQueue {\n    /// Create a queue over contiguous memory ([shared][elems][data]).\n    ///\n    /// Safety: `data` must be a valid, writable pointer to at least\n    /// `contig_size(n_elems, buf_len)` bytes with the expected layout.\n    pub unsafe fn new_from_contiguous(\n        n_elems: u32,\n        buf_len: u32,\n        data: *mut u8,\n    ) -> Result<Self, EqError> {\n        if data.is_null() {\n            return Err(EqError::InvalidArg);\n        }\n        if n_elems == 0 || n_elems & (n_elems - 1) != 0 {\n            return Err(EqError::InvalidArg);\n        }\n        if buf_len == 0 || buf_len & (buf_len - 1) != 0 {\n            return Err(EqError::InvalidArg);\n        }\n\n        // Layout: shared, then elem ring, then data buffer.\n        let shared = data as *mut ElementQueueShared;\n        // SAFETY: pointer arithmetic within the contiguous buffer layout\n        let elems = unsafe { shared.add(1) } as *mut u32;\n        let data_ptr = unsafe { elems.add(n_elems as usize) } as *mut u8;\n\n        // Create raw slice pointers for elems and data (unsafe boundary here only).\n        let elems_slice: *mut [u32] = ptr::slice_from_raw_parts_mut(elems, n_elems as usize);\n        let data_slice: *mut [u8] = ptr::slice_from_raw_parts_mut(data_ptr, buf_len as usize);\n\n        // Load current shared indices using volatile reads (per-field like C).\n        let elem_head = shared.get_elem_head();\n        let buf_head = shared.get_buf_head();\n        let elem_tail = shared.get_elem_tail();\n        let buf_tail = shared.get_buf_tail();\n\n        Ok(ElementQueue {\n            elem_mask: n_elems - 1,\n            buf_mask: buf_len - 1,\n            elem_head,\n            buf_head,\n            elem_tail,\n            buf_tail,\n            shared,\n            elems: unsafe { NonNull::new_unchecked(elems_slice) },\n            data: unsafe { NonNull::new_unchecked(data_slice) },\n        })\n    }\n}\n\n/// Write batch guard with discard-on-drop semantics.\n///\n/// - Holds local copies of tails which are advanced during the batch.\n/// - `finish(self)` commits local tails to the queue and publishes to shared.\n/// - Dropping without `finish()` discards local progress (no commit/publish).\npub struct WriteBatch<'q> {\n    q: &'q mut ElementQueue,\n    elem_tail: Cell<u32>,\n    buf_tail: Cell<u32>,\n}\n\nimpl ElementQueue {\n    /// Start a write batch (producer) and return a guard.\n    /// Loads consumer-published heads to check space.\n    pub fn start_write<'q>(&'q mut self) -> WriteBatch<'q> {\n        // Read the heads published by consumer (volatile per-field to mirror ACCESS_ONCE).\n        self.elem_head = self.shared.get_elem_head();\n        self.buf_head = self.shared.get_buf_head();\n\n        debug_assert!((self.buf_tail as i64 - self.buf_head as i64) >= 0);\n        debug_assert!((self.elem_tail as i64 - self.elem_head as i64) >= 0);\n        WriteBatch {\n            elem_tail: Cell::new(self.elem_tail),\n            buf_tail: Cell::new(self.buf_tail),\n            q: self,\n        }\n    }\n}\n\nimpl<'q> WriteBatch<'q> {\n    pub fn write<'a>(&'a mut self, len: u32) -> Result<&'a mut [u8], EqError> {\n        let aligned_len = (len + 7) & !7;\n        if aligned_len > self.q.buf_mask {\n            return Err(EqError::InvalidArg);\n        }\n        // Element ring full?\n        if self.elem_tail.get().wrapping_sub(self.q.elem_head) >= (self.q.elem_mask + 1) {\n            return Err(EqError::NoSpace);\n        }\n\n        let buf_mask = self.q.buf_mask;\n        let buf_tail =\n            ElementQueue::__next_offset_by_len(self.buf_tail.get(), buf_mask, aligned_len);\n\n        // Enough space in data buffer? Use wrapping arithmetic to mirror C semantics.\n        let used = buf_tail\n            .wrapping_add(aligned_len)\n            .wrapping_sub(self.q.buf_head);\n        if used > buf_mask + 1 {\n            return Err(EqError::NoSpace);\n        }\n\n        // Reserve locally: update tails\n        self.buf_tail.set(buf_tail.wrapping_add(aligned_len));\n        let idx = (self.elem_tail.get() & self.q.elem_mask) as usize;\n        // record element length in ring (visible to reader after publish)\n        self.q.elems_mut()[idx] = len;\n        self.elem_tail.set(self.elem_tail.get().wrapping_add(1));\n\n        let start = (buf_tail & buf_mask) as usize;\n        let end = start + len as usize;\n        Ok(&mut self.q.data_mut()[start..end])\n    }\n\n    pub fn finish(self) -> &'q mut ElementQueue {\n        // Commit local tails and publish to shared with release ordering\n        self.q.elem_tail = self.elem_tail.get();\n        self.q.buf_tail = self.buf_tail.get();\n        fence(Ordering::Release);\n        self.q.shared.set_buf_tail(self.q.buf_tail);\n        self.q.shared.set_elem_tail(self.q.elem_tail);\n        self.q\n    }\n\n    /// Move one element from `reader` to this writer within their active batches.\n    pub fn move_from(&mut self, reader: &ReadBatch<'_>) -> Result<usize, EqError> {\n        let src_len = reader.peek_len()? as usize;\n        let dst = self.write(src_len as u32)?;\n        let src = reader.read()?;\n        debug_assert_eq!(src.len(), src_len);\n        dst.copy_from_slice(src);\n        Ok(src_len)\n    }\n}\n\n/// Read batch guard with discard-on-drop semantics.\n///\n/// - Holds local copies of heads which are advanced during the batch.\n/// - `finish(self)` commits local heads to the queue and publishes to shared.\n/// - Dropping without `finish()` discards local progress (no commit/publish).\npub struct ReadBatch<'q> {\n    q: &'q mut ElementQueue,\n    elem_head: Cell<u32>,\n    buf_head: Cell<u32>,\n}\n\nimpl ElementQueue {\n    /// Start a read batch (consumer) and return a guard.\n    /// Loads writer-published tail and establishes acquire ordering.\n    pub fn start_read<'q>(&'q mut self) -> ReadBatch<'q> {\n        // We trust sizes in elem ring; only need elem_tail here.\n        self.elem_tail = self.shared.get_elem_tail();\n        // Acquire barrier: subsequent reads of elems/data happen-after.\n        fence(Ordering::Acquire);\n        ReadBatch {\n            elem_head: Cell::new(self.elem_head),\n            buf_head: Cell::new(self.buf_head),\n            q: self,\n        }\n    }\n}\n\nimpl<'q> ReadBatch<'q> {\n    pub fn peek_len(&self) -> Result<u32, EqError> {\n        if self.q.elem_tail == self.elem_head.get() {\n            return Err(EqError::NoEntry);\n        }\n        let idx = (self.elem_head.get() & self.q.elem_mask) as usize;\n        Ok(self.q.elems_ref()[idx])\n    }\n\n    pub fn peek(&self) -> Result<&[u8], EqError> {\n        let len = self.peek_len()?;\n\n        let aligned_len = (len + 7) & !7;\n        let offset =\n            ElementQueue::__next_offset_by_len(self.buf_head.get(), self.q.buf_mask, aligned_len)\n                & self.q.buf_mask;\n        let start = offset as usize;\n        let end = start + len as usize;\n        Ok(&self.q.data_ref()[start..end])\n    }\n\n    /// Peek a typed value from the next element without advancing the queue.\n    /// Returns Err(NoEntry) if empty or Err(InvalidArg) if the element is too small.\n    pub fn peek_value<T: Copy>(&self) -> Result<T, EqError> {\n        let bytes = self.peek()?;\n        let need = core::mem::size_of::<T>();\n        if bytes.len() < need {\n            return Err(EqError::InvalidArg);\n        }\n        // Copy bytes into an uninitialized T and assume init.\n        let mut tmp = core::mem::MaybeUninit::<T>::uninit();\n        unsafe {\n            ptr::copy_nonoverlapping(bytes.as_ptr(), tmp.as_mut_ptr() as *mut u8, need);\n            Ok(tmp.assume_init())\n        }\n    }\n\n    /// Read next element, returning a slice into the data buffer.\n    pub fn read(&self) -> Result<&[u8], EqError> {\n        let len = self.peek_len()?;\n        let aligned_len = (len + 7) & !7;\n        let offset =\n            ElementQueue::__next_offset_by_len(self.buf_head.get(), self.q.buf_mask, aligned_len);\n        let start = (offset & self.q.buf_mask) as usize;\n        let end = start + len as usize;\n        // advance local heads\n        self.elem_head.set(self.elem_head.get().wrapping_add(1));\n        self.buf_head.set(offset.wrapping_add(aligned_len));\n        Ok(&self.q.data_ref()[start..end])\n    }\n\n    pub fn finish(self) -> &'q mut ElementQueue {\n        // Commit local heads and publish to shared with release ordering\n        self.q.elem_head = self.elem_head.get();\n        self.q.buf_head = self.buf_head.get();\n        fence(Ordering::Release);\n        self.q.shared.set_buf_head(self.q.buf_head);\n        self.q.shared.set_elem_head(self.q.elem_head);\n        self.q\n    }\n}\n\nimpl ElementQueue {\n    #[inline]\n    pub fn elem_count(&self) -> u32 {\n        self.elem_tail.wrapping_sub(self.elem_head)\n    }\n\n    #[inline]\n    pub fn elem_capacity(&self) -> u32 {\n        self.elem_mask + 1\n    }\n\n    #[inline]\n    pub fn buf_used(&self) -> u32 {\n        self.buf_tail.wrapping_sub(self.buf_head)\n    }\n\n    #[inline]\n    pub fn buf_capacity(&self) -> u32 {\n        self.buf_mask + 1\n    }\n\n    #[inline]\n    fn __next_offset_by_len(buf_offset: u32, buf_mask: u32, len: u32) -> u32 {\n        // If this write would cross the end of buffer page, wrap to the start.\n        let end = buf_offset.wrapping_add(len).wrapping_sub(1);\n        let page_end = end & !buf_mask;\n        let page_start = buf_offset & !buf_mask;\n        if ((page_end.wrapping_sub(page_start)) as i32) > 0 {\n            page_end\n        } else {\n            buf_offset\n        }\n    }\n\n    #[inline]\n    fn elems_ref(&self) -> &[u32] {\n        // SAFETY: elems points to valid initialized slice for the life of self\n        unsafe { self.elems.as_ref() }\n    }\n\n    #[inline]\n    fn elems_mut(&mut self) -> &mut [u32] {\n        // SAFETY: unique &mut self borrow guarantees exclusive access\n        unsafe { self.elems.as_mut() }\n    }\n\n    #[inline]\n    fn data_ref(&self) -> &[u8] {\n        // SAFETY: data points to valid initialized slice for the life of self\n        unsafe { self.data.as_ref() }\n    }\n\n    #[inline]\n    fn data_mut(&mut self) -> &mut [u8] {\n        // SAFETY: unique &mut self borrow guarantees exclusive access\n        unsafe { self.data.as_mut() }\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/Cargo.toml",
    "content": "[package]\nname = \"k8s-collector\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nclap = { version = \"4\", features = [\"derive\"] }\ntokio = { version = \"1\", features = [\"sync\", \"macros\", \"rt-multi-thread\", \"time\", \"net\", \"io-util\"], default-features = false }\nfutures-util = { version = \"0.3\" }\nthiserror = \"1\"\nlog = \"0.4\"\n# K8s watcher stack\nkube = { version = \"2.0.1\", features = [\"runtime\", \"client\", \"rustls-tls\"] }\nk8s-openapi = { version = \"0.26\" }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nencoder_ebpf_net_ingest = { path = \"../render/ebpf_net/ingest\" }\nlz4_flex = { version = \"0.11\", features = [\"frame\"] }\ntokio-util = { version = \"0.7\", features = [\"io-util\"] }\nhostname = \"0.4\"\n\n[dev-dependencies]\nk8s-openapi = { version = \"0.26\", features = [\"v1_30\"] }\nproptest = \"1\"\nanyhow = \"1\"\nenv_logger = \"0.11\"\nserde_json = \"1\"\n"
  },
  {
    "path": "crates/k8s-collector/src/collector.rs",
    "content": "//! Orchestrates the layered pipeline: kube watchers → tombstone adapters →\n//! matcher → encoder → writer.\n//!\n//! The function [`run`] connects to the Kubernetes API, wires the streams, and\n//! drives the end-to-end loop including reducer connection management.\n\nuse std::pin::Pin;\n\nuse futures_util::{future, Stream, StreamExt};\nuse log::{error, info, warn};\nuse tokio::time::{interval, sleep, Duration, Instant};\n\nuse crate::config::Config;\nuse crate::convert_to_meta::{job_to_owner, pod_to_meta, rs_to_owner};\nuse crate::encode;\nuse crate::matcher::Matcher;\nuse crate::output::RenderEvent;\nuse crate::tombstone_adapter::TombstoneAdapter;\nuse crate::types::{OwnerMeta, PodMeta};\nuse crate::writer::Writer;\nuse kube::runtime::watcher::Event;\n\nuse kube::{\n    runtime::{\n        reflector::{self},\n        watcher::{self, Event as KEvent},\n    },\n    Api, Client,\n};\n\nuse k8s_openapi::api::apps::v1::ReplicaSet;\nuse k8s_openapi::api::batch::v1::Job;\nuse k8s_openapi::api::core::v1::Pod;\n\ntype PodStream =\n    Pin<Box<dyn Stream<Item = Result<Vec<Event<PodMeta>>, kube::runtime::watcher::Error>> + Send>>;\n\ntype OwnerStream = Pin<\n    Box<dyn Stream<Item = Result<Vec<Event<OwnerMeta>>, kube::runtime::watcher::Error>> + Send>,\n>;\n\n/// High-level collector pipeline used by both the binary and tests.\n///\n/// Owns kube watcher streams, Stores, tombstone adapters, and the Matcher,\n/// exposing a `next_messages` API that yields batches of `RenderEvent`s.\npub struct Collector {\n    pod_stream: PodStream,\n    rs_owner_stream: OwnerStream,\n    job_owner_stream: OwnerStream,\n\n    rs_owners_writer: reflector::store::Writer<OwnerMeta>,\n    job_owners_writer: reflector::store::Writer<OwnerMeta>,\n    pods_writer: reflector::store::Writer<PodMeta>,\n\n    matcher: Matcher,\n    rs_owner_tomb: TombstoneAdapter<OwnerMeta>,\n    job_owner_tomb: TombstoneAdapter<OwnerMeta>,\n    pod_tomb: TombstoneAdapter<PodMeta>,\n}\n\nimpl Collector {\n    /// Construct a new collector pipeline using the default Kubernetes client.\n    pub async fn new(cfg: Config) -> Result<Self, crate::Error> {\n        let client = Client::try_default()\n            .await\n            .map_err(|e| crate::Error::KubeInit(e.to_string()))?;\n        Ok(Self::with_client(cfg, client))\n    }\n\n    /// Construct a new collector pipeline from an existing Kubernetes client.\n    pub fn with_client(cfg: Config, client: Client) -> Self {\n        let pods_api: Api<Pod> = Api::all(client.clone());\n        let rs_api: Api<ReplicaSet> = Api::all(client.clone());\n        let job_api: Api<Job> = Api::all(client);\n\n        let wc = watcher::Config::default();\n\n        // Watcher streams mapped to our Event<T> protocol\n        let pod_stream =\n            watcher::watcher(pods_api, wc.clone()).map(|e| map_kube_event(e, pod_to_meta));\n        let rs_stream =\n            watcher::watcher(rs_api, wc.clone()).map(|e| map_kube_event(e, rs_to_owner));\n        let job_stream = watcher::watcher(job_api, wc).map(|e| map_kube_event(e, job_to_owner));\n\n        let (rs_owners_store, rs_owners_writer) = reflector::store::store::<OwnerMeta>();\n        let (job_owners_store, job_owners_writer) = reflector::store::store::<OwnerMeta>();\n        let (pods_store, pods_writer) = reflector::store::store::<PodMeta>();\n        let matcher = Matcher::new(rs_owners_store, job_owners_store, pods_store);\n\n        let rs_owner_tomb = TombstoneAdapter::new(cfg.delete_ttl, cfg.delete_capacity);\n        let job_owner_tomb = TombstoneAdapter::new(cfg.delete_ttl, cfg.delete_capacity);\n        let pod_tomb = TombstoneAdapter::new(cfg.delete_ttl, cfg.delete_capacity);\n\n        Self {\n            pod_stream: Box::pin(pod_stream),\n            rs_owner_stream: Box::pin(rs_stream),\n            job_owner_stream: Box::pin(job_stream),\n            rs_owners_writer,\n            job_owners_writer,\n            pods_writer,\n            matcher,\n            rs_owner_tomb,\n            job_owner_tomb,\n            pod_tomb,\n        }\n    }\n\n    /// Fetch the next batch of render events from either the pod or owner\n    /// watcher stream.\n    ///\n    /// Returns:\n    /// - `Ok(events)` with zero or more `RenderEvent`s; the caller can skip\n    ///   empty batches and continue calling to wait for more data.\n    /// - `Err` on watcher failure.\n    pub async fn next_messages(&mut self) -> Result<Vec<RenderEvent>, crate::Error> {\n        loop {\n            let mut out = Vec::new();\n            tokio::select! {\n                ev = self.pod_stream.next() => {\n                    match ev {\n                        Some(Ok(events)) => {\n                            for e in events {\n                                for fwd in self.pod_tomb.handle(e) {\n                                    self.pods_writer.apply_watcher_event(&fwd);\n                                    out.extend(self.matcher.handle_pod(fwd));\n                                }\n                            }\n                        }\n                        Some(Err(err)) => {\n                            warn!(\"pod watcher error: {err:?}; will retry on next tick\");\n                            continue;\n                        }\n                        None => {\n                            error!(\"pod watcher stream terminated unexpectedly\");\n                            return Err(crate::Error::Stopped);\n                        }\n                    }\n                }\n                ev = self.rs_owner_stream.next() => {\n                    match ev {\n                        Some(Ok(events)) => {\n                            for e in events {\n                                for fwd in self.rs_owner_tomb.handle(e) {\n                                    self.rs_owners_writer.apply_watcher_event(&fwd);\n                                    out.extend(self.matcher.handle_owner(fwd));\n                                }\n                            }\n                        }\n                        Some(Err(err)) => {\n                            warn!(\"owner watcher error: {err:?}; will retry on next tick\");\n                            continue;\n                        }\n                        None => {\n                            error!(\"owner watcher stream terminated unexpectedly\");\n                            return Err(crate::Error::Stopped);\n                        }\n                    }\n                }\n                ev = self.job_owner_stream.next() => {\n                    match ev {\n                        Some(Ok(events)) => {\n                            for e in events {\n                                for fwd in self.job_owner_tomb.handle(e) {\n                                    self.job_owners_writer.apply_watcher_event(&fwd);\n                                    out.extend(self.matcher.handle_owner(fwd));\n                                }\n                            }\n                        }\n                        Some(Err(err)) => {\n                            warn!(\"owner watcher error: {err:?}; will retry on next tick\");\n                            continue;\n                        }\n                        None => {\n                            error!(\"owner watcher stream terminated unexpectedly\");\n                            return Err(crate::Error::Stopped);\n                        }\n                    }\n                }\n            }\n\n            if !out.is_empty() {\n                return Ok(out);\n            }\n        }\n    }\n\n    /// Start a new resync epoch using the current store snapshots.\n    pub fn start_new_epoch(&mut self) -> Vec<RenderEvent> {\n        self.matcher.start_new_epoch()\n    }\n\n    /// Produce a human-readable snapshot of the internal matcher/stores.\n    ///\n    /// This is primarily intended for tests and debugging to inspect the\n    /// Stores and live pod bookkeeping when assertions fail.\n    pub fn debug_snapshot(&self) -> String {\n        self.matcher.debug_snapshot()\n    }\n}\n\n/// Run the collector to completion.\n///\n/// This function:\n/// - Starts kube watchers for Pods, ReplicaSets, and Jobs\n/// - Applies tombstone adapters to both owner and pod streams\n/// - Matches and encodes events, writing them to the reducer\n/// - Reconnects aggressively on socket errors and starts a new epoch per connect\npub async fn run(cfg: Config) -> Result<(), crate::Error> {\n    use tokio::net::TcpStream;\n\n    let (major, minor, patch) = collector_version();\n    info!(\n        \"Starting k8s-collector version {}.{}.{}\",\n        major, minor, patch\n    );\n\n    let mut pipeline = Collector::new(cfg.clone()).await?;\n\n    let hostname = collector_hostname();\n    info!(\"Hostname: {}\", hostname);\n\n    let addr = std::net::SocketAddr::new(\n        cfg.intake_host\n            .parse()\n            .unwrap_or(std::net::Ipv4Addr::LOCALHOST.into()),\n        cfg.intake_port,\n    );\n\n    'reconnect: loop {\n        // Connect\n        info!(\"k8s-collector: connecting to reducer at {}\", addr);\n        let stream = match TcpStream::connect(addr).await {\n            Ok(s) => {\n                info!(\"k8s-collector: connected to reducer at {}\", addr);\n                s\n            }\n            Err(err) => {\n                warn!(\n                    \"k8s-collector: failed to connect to reducer at {}: {err}; retrying in 1s\",\n                    addr\n                );\n                sleep(Duration::from_secs(1)).await;\n                continue;\n            }\n        };\n        let mut writer = Writer::new(stream);\n        if let Err(err) = perform_handshake(&mut writer, &hostname).await {\n            warn!(\"k8s-collector: handshake with reducer failed: {err}; reconnecting\");\n            sleep(Duration::from_secs(1)).await;\n            continue 'reconnect;\n        }\n\n        // New epoch on connect\n        for ev in pipeline.start_new_epoch() {\n            let buf = encode::encode(&ev, timestamp());\n            if let Err(err) = writer.send(&buf).await {\n                // Connection failed while sending epoch; restart outer loop\n                warn!(\"k8s-collector: failed to send epoch event to reducer: {err}; reconnecting\");\n                sleep(Duration::from_secs(1)).await;\n                continue 'reconnect;\n            }\n        }\n        if let Err(err) = writer.flush().await {\n            warn!(\"k8s-collector: flush after epoch send failed: {err}; reconnecting\");\n            sleep(Duration::from_secs(1)).await;\n            continue 'reconnect;\n        }\n\n        let mut heartbeat = interval(Duration::from_secs(5));\n        let mut flush_deadline: Option<Instant> = None;\n\n        // Inner loop: forward events until connection drops or the pipeline fails\n        'connection: loop {\n            let flush_deadline_opt = flush_deadline;\n            tokio::select! {\n                res = pipeline.next_messages() => {\n                    match res {\n                        Ok(events) => {\n                            let had_events = !events.is_empty();\n                            for ev in events {\n                                let buf = encode::encode(&ev, timestamp());\n                                if let Err(err) = writer.send(&buf).await {\n                                    // Connection dropped; break to reconnect.\n                                    warn!(\n                                        \"k8s-collector: send to reducer failed during event forwarding: {err}; reconnecting\"\n                                    );\n                                    break 'connection;\n                                }\n                            }\n                            if had_events && flush_deadline.is_none() {\n                                flush_deadline = Some(Instant::now() + Duration::from_millis(100));\n                            }\n                        }\n                        Err(e) => {\n                            // Fatal pipeline error (e.g., watcher stream terminated): propagate up.\n                            error!(\"collector pipeline failed: {e}\");\n                            return Err(e);\n                        }\n                    }\n                }\n                _ = heartbeat.tick() => {\n                    let buf = encode::encode_heartbeat(timestamp());\n                    if let Err(err) = writer.send(&buf).await {\n                        warn!(\n                            \"k8s-collector: send to reducer failed during heartbeat: {err}; reconnecting\"\n                        );\n                        break 'connection;\n                    }\n                    if let Err(err) = writer.flush().await {\n                        warn!(\n                            \"k8s-collector: flush after heartbeat failed: {err}; reconnecting\"\n                        );\n                        break 'connection;\n                    }\n                    flush_deadline = None;\n                }\n                _ = async {\n                    if let Some(deadline) = flush_deadline_opt {\n                        tokio::time::sleep_until(deadline).await;\n                    } else {\n                        future::pending::<()>().await;\n                    }\n                } => {\n                    if let Err(err) = writer.flush().await {\n                        warn!(\n                            \"k8s-collector: flush after flush-deadline failed: {err}; reconnecting\"\n                        );\n                        break 'connection;\n                    }\n                    flush_deadline = None;\n                }\n            }\n        }\n\n        // Drop connection, wait and reconnect\n        sleep(Duration::from_secs(1)).await;\n    }\n}\n\n/// Map a kube watcher event to one or more internal events by applying a\n/// converter function.\nfn map_kube_event<K, T>(\n    ev: Result<KEvent<K>, kube::runtime::watcher::Error>,\n    f: fn(K) -> T,\n) -> Result<Vec<Event<T>>, kube::runtime::watcher::Error> {\n    Ok(match ev? {\n        KEvent::Apply(obj) => vec![Event::Apply(f(obj))],\n        KEvent::Delete(obj) => vec![Event::Delete(f(obj))],\n        KEvent::Init => vec![Event::Init],\n        KEvent::InitApply(obj) => vec![Event::InitApply(f(obj))],\n        KEvent::InitDone => vec![Event::InitDone],\n    })\n}\n\nfn collector_version() -> (u32, u32, u32) {\n    fn parse(name: &str) -> Option<u32> {\n        std::env::var(name).ok()?.parse().ok()\n    }\n\n    let major = parse(\"EBPF_NET_MAJOR_VERSION\").unwrap_or(0);\n    let minor = parse(\"EBPF_NET_MINOR_VERSION\").unwrap_or(11);\n    let patch = parse(\"EBPF_NET_PATCH_VERSION\").unwrap_or(0);\n    (major, minor, patch)\n}\n\nfn collector_hostname() -> String {\n    if let Ok(h) = std::env::var(\"K8S_COLLECTOR_HOSTNAME\") {\n        return h;\n    }\n    match hostname::get() {\n        Ok(name) => name.to_string_lossy().into_owned(),\n        Err(_) => \"k8s-collector\".to_string(),\n    }\n}\n\nasync fn perform_handshake<W>(writer: &mut Writer<W>, hostname: &str) -> std::io::Result<()>\nwhere\n    W: tokio::io::AsyncWrite + Unpin + Send + 'static,\n{\n    // First message: uncompressed version_info to negotiate compression and\n    // pass the minimum accepted version check on the reducer.\n    let (major, minor, patch) = collector_version();\n    let ver_buf = encode::encode_version_info(major, minor, patch, timestamp());\n    writer.send(&ver_buf).await?;\n    writer.flush().await?;\n\n    // Second message: connect as a k8s collector with hostname. This\n    // authenticates the connection and enables handlers for k8s pod messages.\n    //\n    // ClientType::k8s = 3 (see common/client_type.h).\n    let connect_buf = encode::encode_connect(3, hostname, timestamp());\n    writer.send(&connect_buf).await?;\n    writer.flush().await?;\n\n    Ok(())\n}\n\n/// Current UNIX time in nanoseconds.\nfn timestamp() -> u64 {\n    use std::time::{SystemTime, UNIX_EPOCH};\n    SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .map(|d| d.as_nanos() as u64)\n        .unwrap_or(0)\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/config.rs",
    "content": "//! Configuration for the Kubernetes metadata collector.\n//!\n//! Settings cover endpoint selection and deletion tombstone policy.\n\nuse std::time::Duration;\n\n#[derive(Clone, Debug)]\npub struct Config {\n    /// Reducer host to connect to (ingest TCP server)\n    pub intake_host: String,\n    /// Reducer port to connect to\n    pub intake_port: u16,\n    /// Max time to retain delete tombstones to satisfy straggling updates.\n    pub delete_ttl: Duration,\n    /// Max number of delete tombstones to retain (older ones evicted).\n    pub delete_capacity: usize,\n}\n\nimpl Default for Config {\n    /// Reasonable defaults:\n    /// - connect to `127.0.0.1:8000`\n    /// - retain tombstones for 60s up to 10k entries\n    fn default() -> Self {\n        Self {\n            intake_host: \"127.0.0.1\".into(),\n            intake_port: 8000,\n            delete_ttl: Duration::from_secs(60),\n            delete_capacity: 10_000,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/convert_to_meta.rs",
    "content": "//! Converters from Kubernetes API objects to compact metadata.\n//!\n//! These functions strip extraneous fields and normalize key properties used by\n//! the matcher and output layers.\n\nuse k8s_openapi::api::apps::v1::ReplicaSet;\nuse k8s_openapi::api::batch::v1::Job;\nuse k8s_openapi::api::core::v1::Pod;\nuse kube::ResourceExt;\n\nuse crate::types::{ContainerMeta, OwnerKind, OwnerMeta, OwnerRef, PodMeta};\n\n/// Convert a Kubernetes `Pod` into a [`PodMeta`].\n///\n/// - Picks the first controller ownerReference as the pod owner (if any)\n/// - Collects container runtime IDs, names, and images from status.container_statuses\n/// - Computes a deterministic version string from container images\npub fn pod_to_meta(pod: Pod) -> PodMeta {\n    let uid = pod.metadata.uid.clone().unwrap_or_default();\n    let name = pod.name_any();\n    let namespace = pod.namespace().unwrap_or_else(|| \"default\".to_string());\n\n    let host_network = pod\n        .spec\n        .as_ref()\n        .and_then(|s| s.host_network)\n        .unwrap_or(false);\n    let ip = pod\n        .status\n        .as_ref()\n        .and_then(|s| s.pod_ip.clone())\n        .unwrap_or_default();\n\n    let containers = pod\n        .status\n        .as_ref()\n        .and_then(|s| s.container_statuses.as_ref())\n        .map(|v| {\n            v.iter()\n                .map(|cs| ContainerMeta {\n                    id: cs.container_id.clone().unwrap_or_default(),\n                    name: cs.name.clone(),\n                    image: cs.image.clone(),\n                })\n                .collect::<Vec<_>>()\n        })\n        .unwrap_or_default();\n\n    let images = pod\n        .status\n        .as_ref()\n        .and_then(|s| s.container_statuses.as_ref())\n        .map(|v| v.iter().map(|cs| cs.image.clone()).collect::<Vec<_>>())\n        .unwrap_or_default();\n    let version = build_version_string(images);\n\n    let owner = pod\n        .metadata\n        .owner_references\n        .unwrap_or_default()\n        .into_iter()\n        .find(|o| o.controller.unwrap_or(false))\n        .map(|o| OwnerRef {\n            kind: o.kind.parse().unwrap_or(OwnerKind::Other),\n            uid: o.uid,\n            name: o.name,\n        });\n\n    PodMeta {\n        uid,\n        ip,\n        name,\n        namespace,\n        owner,\n        host_network,\n        version,\n        containers,\n    }\n}\n\n/// Convert a Kubernetes `ReplicaSet` into an [`OwnerMeta`].\n///\n/// If the ReplicaSet has a controller owner (e.g., Deployment), it is stored in\n/// `controller` for escalation by the matcher.\npub fn rs_to_owner(rs: ReplicaSet) -> OwnerMeta {\n    let uid = rs.metadata.uid.unwrap_or_default();\n    let controller = rs\n        .metadata\n        .owner_references\n        .unwrap_or_default()\n        .into_iter()\n        .find(|o| o.controller.unwrap_or(false))\n        .map(|o| OwnerRef {\n            kind: o.kind.parse().unwrap_or(OwnerKind::Other),\n            uid: o.uid,\n            name: o.name,\n        });\n    OwnerMeta { uid, controller }\n}\n\n/// Convert a Kubernetes `Job` into an [`OwnerMeta`].\n///\n/// If the Job has a controller owner (e.g., CronJob), it is stored in\n/// `controller` for escalation by the matcher.\npub fn job_to_owner(job: Job) -> OwnerMeta {\n    let uid = job.metadata.uid.unwrap_or_default();\n    let controller = job\n        .metadata\n        .owner_references\n        .unwrap_or_default()\n        .into_iter()\n        .find(|o| o.controller.unwrap_or(false))\n        .map(|o| OwnerRef {\n            kind: o.kind.parse().unwrap_or(OwnerKind::Other),\n            uid: o.uid,\n            name: o.name,\n        });\n    OwnerMeta { uid, controller }\n}\n\n/// Build a stable version string from a set of image references.\n///\n/// Images are quoted, sorted, and joined by commas to maintain consistency\n/// across updates and ordering changes.\nfn build_version_string(images: Vec<String>) -> String {\n    let mut imgs = images\n        .into_iter()\n        .filter(|s| !s.is_empty())\n        .map(|i| format!(\"'{}'\", i))\n        .collect::<Vec<_>>();\n    imgs.sort();\n    imgs.join(\",\")\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/encode.rs",
    "content": "//! Encoder for render events into ingestion wire buffers.\n//!\n//! Uses the generated encoder functions from `encoder_ebpf_net_ingest` to\n//! produce exact-sized buffers expected by the reducer. The `encode` function\n//! maps a [`RenderEvent`](crate::output::RenderEvent) into a ready-to-send\n//! buffer.\n\nuse core::ffi::c_char;\n\nuse crate::output::RenderEvent;\nuse crate::types::OwnerKind;\n\n// Ensure encoder crate is linked so its #[no_mangle] symbols are available\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_ingest;\n\n#[repr(C)]\nstruct JbBlob {\n    buf: *const c_char,\n    len: u16,\n}\n\nextern \"C\" {\n    fn ebpf_net_ingest_encode_pod_new_with_name(\n        dest: *mut u8,\n        dest_len: u32,\n        tstamp: u64,\n        uid: JbBlob,\n        ip: u32,\n        owner_name: JbBlob,\n        pod_name: JbBlob,\n        owner_kind: u8,\n        owner_uid: JbBlob,\n        is_host_network: u8,\n        ns: JbBlob,\n        version: JbBlob,\n    );\n    fn ebpf_net_ingest_encode_pod_container(\n        dest: *mut u8,\n        dest_len: u32,\n        tstamp: u64,\n        uid: JbBlob,\n        container_id: JbBlob,\n        container_name: JbBlob,\n        container_image: JbBlob,\n    );\n    fn ebpf_net_ingest_encode_pod_delete(dest: *mut u8, dest_len: u32, tstamp: u64, uid: JbBlob);\n    fn ebpf_net_ingest_encode_pod_resync(\n        dest: *mut u8,\n        dest_len: u32,\n        tstamp: u64,\n        resync_count: u64,\n    );\n}\nextern \"C\" {\n    fn ebpf_net_ingest_encode_version_info(\n        dest: *mut u8,\n        dest_len: u32,\n        tstamp: u64,\n        major: u32,\n        minor: u32,\n        patch: u32,\n    );\n}\nextern \"C\" {\n    fn ebpf_net_ingest_encode_connect(\n        dest: *mut u8,\n        dest_len: u32,\n        tstamp: u64,\n        collector_type: u8,\n        hostname: JbBlob,\n    );\n}\nextern \"C\" {\n    fn ebpf_net_ingest_encode_heartbeat(dest: *mut u8, dest_len: u32, tstamp: u64);\n}\n\n/// Construct a `JbBlob` view from a Rust `&str`.\nfn as_blob(s: &str) -> JbBlob {\n    JbBlob {\n        buf: s.as_ptr() as *const c_char,\n        len: s.len() as u16,\n    }\n}\n\n/// Map [`OwnerKind`] into the reducer's numeric enum codes.\nfn owner_kind_code(k: OwnerKind) -> u8 {\n    match k {\n        OwnerKind::ReplicaSet => 0,\n        OwnerKind::Deployment => 1,\n        OwnerKind::Job => 2,\n        OwnerKind::CronJob => 3,\n        OwnerKind::NoOwner => 254,\n        OwnerKind::Other => 255,\n    }\n}\n\n/// Parse an IPv4 string as a host-order `u32`.\nfn ipv4_to_u32(ip: &str) -> u32 {\n    ip.parse::<std::net::Ipv4Addr>().map(u32::from).unwrap_or(0)\n}\n\n/// Encode a [`RenderEvent`] into a ready-to-send wire buffer.\npub fn encode(event: &RenderEvent, tstamp: u64) -> Vec<u8> {\n    match event {\n        RenderEvent::PodResync { epoch } => {\n            let len = 8 + 16; // timestamp + struct\n            let mut buf = vec![0u8; len];\n            unsafe {\n                ebpf_net_ingest_encode_pod_resync(\n                    buf.as_mut_ptr(),\n                    buf.len() as u32,\n                    tstamp,\n                    *epoch,\n                );\n            }\n            buf\n        }\n        RenderEvent::PodDelete { uid } => {\n            let consumed = 4 + uid.len();\n            let len = 8 + consumed;\n            let mut buf = vec![0u8; len];\n            unsafe {\n                ebpf_net_ingest_encode_pod_delete(\n                    buf.as_mut_ptr(),\n                    buf.len() as u32,\n                    tstamp,\n                    as_blob(uid),\n                );\n            }\n            buf\n        }\n        RenderEvent::PodContainer { uid, container } => {\n            let consumed =\n                10 + uid.len() + container.id.len() + container.name.len() + container.image.len();\n            let len = 8 + consumed;\n            let mut buf = vec![0u8; len];\n            unsafe {\n                ebpf_net_ingest_encode_pod_container(\n                    buf.as_mut_ptr(),\n                    buf.len() as u32,\n                    tstamp,\n                    as_blob(uid),\n                    as_blob(&container.id),\n                    as_blob(&container.name),\n                    as_blob(&container.image),\n                );\n            }\n            buf\n        }\n        RenderEvent::PodNew {\n            uid,\n            ip,\n            pod_name,\n            ns,\n            version,\n            owner_kind,\n            owner_uid,\n            owner_name,\n            is_host_network,\n        } => {\n            let consumed = 20\n                + uid.len()\n                + owner_name.len()\n                + pod_name.len()\n                + owner_uid.len()\n                + ns.len()\n                + version.len();\n            let len = 8 + consumed;\n            let mut buf = vec![0u8; len];\n            unsafe {\n                ebpf_net_ingest_encode_pod_new_with_name(\n                    buf.as_mut_ptr(),\n                    buf.len() as u32,\n                    tstamp,\n                    as_blob(uid),\n                    ipv4_to_u32(ip),\n                    as_blob(owner_name),\n                    as_blob(pod_name),\n                    owner_kind_code(*owner_kind),\n                    as_blob(owner_uid),\n                    if *is_host_network { 1 } else { 0 },\n                    as_blob(ns),\n                    as_blob(version),\n                );\n            }\n            buf\n        }\n    }\n}\n\n/// Encode a `version_info` handshake message.\npub fn encode_version_info(major: u32, minor: u32, patch: u32, tstamp: u64) -> Vec<u8> {\n    let len = 8 + 16;\n    let mut buf = vec![0u8; len];\n    unsafe {\n        ebpf_net_ingest_encode_version_info(\n            buf.as_mut_ptr(),\n            buf.len() as u32,\n            tstamp,\n            major,\n            minor,\n            patch,\n        );\n    }\n    buf\n}\n\n/// Encode a `connect` message identifying this collector.\n///\n/// `collector_type` is the numeric `ClientType` enum value (e.g., 3 for k8s\n/// collectors; see `common/client_type.h`).\npub fn encode_connect(collector_type: u8, hostname: &str, tstamp: u64) -> Vec<u8> {\n    let consumed = 5u32 + hostname.len() as u32;\n    let len = 8 + consumed as usize;\n    let mut buf = vec![0u8; len];\n    unsafe {\n        ebpf_net_ingest_encode_connect(\n            buf.as_mut_ptr(),\n            buf.len() as u32,\n            tstamp,\n            collector_type,\n            as_blob(hostname),\n        );\n    }\n    buf\n}\n\n/// Encode a `heartbeat` message.\npub fn encode_heartbeat(tstamp: u64) -> Vec<u8> {\n    let len = 8 + 2;\n    let mut buf = vec![0u8; len];\n    unsafe {\n        ebpf_net_ingest_encode_heartbeat(buf.as_mut_ptr(), buf.len() as u32, tstamp);\n    }\n    buf\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/lib.rs",
    "content": "//! Kubernetes metadata collector (Rust).\n//!\n//! This crate implements a layered pipeline to watch Kubernetes resources,\n//! correlate Pods with their effective owners, and stream normalized messages\n//! to the reducer. The layers are:\n//! - convert_to_meta: map Kubernetes API objects to compact metadata structs\n//! - tombstone_adapter: retain recent Delete events to bridge race windows\n//! - matcher: match Pods to owners, handle escalation (RS→Deployment, Job→CronJob)\n//! - output/encode: translate matched events into encoded render buffers\n//! - writer: async TCP client with optional LZ4 streaming compression\n//! - collector: orchestrates watchers, adapters, matcher, and writer\n//!\n//! The public entry point is [`run_with_config`].\n\npub mod collector;\npub mod config;\npub mod convert_to_meta;\npub mod encode;\npub mod matcher;\npub mod output;\npub mod tombstone_adapter;\npub mod types;\npub mod writer;\n\nuse crate::config::Config;\n\n#[derive(thiserror::Error, Debug)]\npub enum Error {\n    #[error(\"collector stopped\")]\n    Stopped,\n    #[error(\"failed to initialize Kubernetes client: {0}\")]\n    KubeInit(String),\n    #[error(\"failed to initialize Tokio runtime: {0}\")]\n    RuntimeInit(String),\n}\n\n/// Convenience runner used by the binary: runs the layered pipeline with kube watchers.\n///\n/// Creates a Tokio runtime and executes [`collector::run`].\n///\n/// Errors indicate runtime initialization failure or fatal errors in the\n/// orchestrated tasks.\npub fn run_with_config(cfg: Config) -> Result<(), Error> {\n    let rt = tokio::runtime::Runtime::new().map_err(|e| Error::RuntimeInit(e.to_string()))?;\n    rt.block_on(async move { collector::run(cfg).await })\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/matcher.rs",
    "content": "//! Pod↔Owner matching engine.\n//!\n//! Maintains external kube reflectors Stores for owners (ReplicaSets and Jobs)\n//! and pods, correlates pods with their\n//! effective owners, and produces normalized render events:\n//! - Emit PodNew(+containers) when a pod is resolvable and has an IP\n//! - Emit PodContainer updates for subsequent Apply on live pods\n//! - Emit PodDelete when a live pod is deleted\n//! - Resync epochs on InitDone (double-buffer snapshot becomes active).\n//!\n//! Design notes:\n//! - Stores are provided externally to `Matcher` and are fed by kube reflectors.\n//!   ReplicaSet and Job owners use separate Stores so that independent\n//!   re-initialization (`Init`/`InitApply`/`InitDone`) of their watcher streams\n//!   cannot clobber each other's snapshot state.\n//!   On `InitDone`, `Store::state()` for each owner Store exposes its new\n//!   snapshot; `Matcher` uses the combined view in `start_new_epoch()` to\n//!   re-emit resolvable pods.\n//! - `Lookup` is implemented for `PodMeta` and `OwnerMeta` so `Store` keys by\n//!   UID as a cluster-scoped identifier. We construct `ObjectRef` keys via\n//!   small helpers (`pod_ref`, `owner_ref`) and use `Store::get` for O(1)\n//!   lookups.\n//!\n//! Owner escalation: RS→Deployment, Job→CronJob (when controller owner exists).\n\nuse std::borrow::Cow;\nuse std::collections::{HashMap, HashSet};\n\nuse crate::output::RenderEvent;\nuse crate::types::{OwnerKind, OwnerMeta, PodMeta};\nuse kube::runtime::reflector::{Lookup, ObjectRef, Store};\nuse kube::runtime::watcher::Event;\n\n// Implement kube-runtime's Lookup trait for our compact meta types so that\n// they can be used with reflector Stores. We key by UID as a cluster-scoped\n// identifier to avoid namespace/name collisions and reduce state surface.\nimpl Lookup for PodMeta {\n    type DynamicType = ();\n\n    fn kind(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"PodMeta\")\n    }\n    fn group(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"opentelemetry.network\")\n    }\n    fn version(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"v1\")\n    }\n    fn plural(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"podmeta\")\n    }\n    fn name(&self) -> Option<Cow<'_, str>> {\n        Some(Cow::Borrowed(self.uid.as_str()))\n    }\n    fn namespace(&self) -> Option<Cow<'_, str>> {\n        None\n    }\n    fn resource_version(&self) -> Option<Cow<'_, str>> {\n        None\n    }\n    fn uid(&self) -> Option<Cow<'_, str>> {\n        Some(Cow::Borrowed(self.uid.as_str()))\n    }\n}\n\nimpl Lookup for OwnerMeta {\n    type DynamicType = ();\n\n    fn kind(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"OwnerMeta\")\n    }\n    fn group(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"opentelemetry.network\")\n    }\n    fn version(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"v1\")\n    }\n    fn plural(_: &Self::DynamicType) -> Cow<'_, str> {\n        Cow::Borrowed(\"ownermeta\")\n    }\n    fn name(&self) -> Option<Cow<'_, str>> {\n        Some(Cow::Borrowed(self.uid.as_str()))\n    }\n    fn namespace(&self) -> Option<Cow<'_, str>> {\n        None\n    }\n    fn resource_version(&self) -> Option<Cow<'_, str>> {\n        None\n    }\n    fn uid(&self) -> Option<Cow<'_, str>> {\n        Some(Cow::Borrowed(self.uid.as_str()))\n    }\n}\n\npub struct Matcher {\n    epoch: u64,\n    rs_owners: Store<OwnerMeta>,\n    job_owners: Store<OwnerMeta>,\n    waiting_by_owner: HashMap<String, Vec<String>>, // owner uid -> pod uids\n    pods: Store<PodMeta>,\n    live_pods: HashSet<String>,\n}\n\nimpl Matcher {\n    /// Create a new matcher with the provided Stores.\n    ///\n    /// `rs_owners` and `job_owners` are kept separate to avoid cross-talk\n    /// between independently re-initialized ReplicaSet and Job watcher streams.\n    pub fn new(\n        rs_owners: Store<OwnerMeta>,\n        job_owners: Store<OwnerMeta>,\n        pods: Store<PodMeta>,\n    ) -> Self {\n        Self {\n            epoch: 0,\n            rs_owners,\n            job_owners,\n            waiting_by_owner: HashMap::new(),\n            pods,\n            live_pods: HashSet::new(),\n        }\n    }\n\n    /// Helper: build an ObjectRef for a Pod by UID (cluster-scoped key)\n    fn pod_ref(uid: &str) -> ObjectRef<PodMeta> {\n        ObjectRef::<PodMeta>::new(uid)\n    }\n\n    /// Helper: build an ObjectRef for an Owner by UID (cluster-scoped key)\n    fn owner_ref(uid: &str) -> ObjectRef<OwnerMeta> {\n        ObjectRef::<OwnerMeta>::new(uid)\n    }\n\n    /// Helper: look up an owner meta by UID across ReplicaSet and Job Stores.\n    fn get_owner_meta(&self, uid: &str) -> Option<OwnerMeta> {\n        self.rs_owners\n            .get(&Self::owner_ref(uid))\n            .or_else(|| self.job_owners.get(&Self::owner_ref(uid)))\n            .as_deref()\n            .cloned()\n    }\n\n    /// Helper: check whether an owner UID exists in any owner Store.\n    fn owner_exists(&self, uid: &str) -> bool {\n        self.rs_owners.get(&Self::owner_ref(uid)).is_some()\n            || self.job_owners.get(&Self::owner_ref(uid)).is_some()\n    }\n\n    /// Handle a single Owner event and return any resulting render events.\n    pub fn handle_owner(&mut self, ev: Event<OwnerMeta>) -> Vec<RenderEvent> {\n        match ev {\n            Event::Init => Vec::new(),\n            Event::InitApply(_m) => Vec::new(),\n            Event::Apply(m) => {\n                let uid = m.uid.clone();\n                // Drain waiters for this owner if any\n                let mut out = Vec::new();\n                if let Some(pods) = self.waiting_by_owner.remove(&uid) {\n                    for puid in pods {\n                        if let Some(p) = self.pods.get(&Self::pod_ref(&puid)).as_deref().cloned() {\n                            out.extend(self.try_emit_pod(p));\n                        }\n                    }\n                }\n                out\n            }\n            Event::Delete(_m) => Vec::new(),\n            Event::InitDone => {\n                // Scan waiting pods against the now-initialized owner store\n                let mut out = Vec::new();\n                let keys: Vec<String> = self.waiting_by_owner.keys().cloned().collect();\n                for owner_uid in keys {\n                    if self.owner_exists(&owner_uid) {\n                        if let Some(pods) = self.waiting_by_owner.remove(&owner_uid) {\n                            for puid in pods {\n                                if let Some(p) =\n                                    self.pods.get(&Self::pod_ref(&puid)).as_deref().cloned()\n                                {\n                                    out.extend(self.try_emit_pod(p));\n                                }\n                            }\n                        }\n                    }\n                }\n                out\n            }\n        }\n    }\n\n    /// Handle a single Pod event and return any resulting render events.\n    pub fn handle_pod(&mut self, ev: Event<PodMeta>) -> Vec<RenderEvent> {\n        match ev {\n            Event::Init => Vec::new(),\n            Event::InitApply(_p) => Vec::new(),\n            Event::Apply(p) => {\n                if self.live_pods.contains(&p.uid) {\n                    // Live pod: containers only\n                    return p\n                        .containers\n                        .into_iter()\n                        .map(|c| RenderEvent::PodContainer {\n                            uid: p.uid.clone(),\n                            container: c,\n                        })\n                        .collect();\n                }\n                self.try_emit_pod(p)\n            }\n            Event::Delete(p) => {\n                let mut out = Vec::new();\n                if self.live_pods.remove(&p.uid) {\n                    out.push(RenderEvent::PodDelete { uid: p.uid.clone() });\n                }\n                for waiters in self.waiting_by_owner.values_mut() {\n                    waiters.retain(|w| w != &p.uid);\n                }\n                out\n            }\n            Event::InitDone => self.start_new_epoch(),\n        }\n    }\n\n    /// Start a new resync epoch, clearing waiting queues and emitting a\n    /// `PodResync` event followed by any pods now resolvable from the current\n    /// store contents.\n    pub fn start_new_epoch(&mut self) -> Vec<RenderEvent> {\n        self.epoch = self.epoch.wrapping_add(1);\n        self.waiting_by_owner.clear();\n        let mut out = vec![RenderEvent::PodResync { epoch: self.epoch }];\n        // Iterate pod store snapshot and emit resolvable ones\n        for p in self.pods.state().into_iter().map(|a| (*a).clone()) {\n            out.extend(self.try_emit_pod(p));\n        }\n        out\n    }\n\n    /// Produce a human-readable snapshot of the current matcher state.\n    ///\n    /// Intended for use in tests and debugging to help understand mismatches\n    /// between the pod/owner Stores and emitted events during resync.\n    pub fn debug_snapshot(&self) -> String {\n        use std::fmt::Write;\n\n        let mut out = String::new();\n\n        let _ = writeln!(out, \"Matcher debug snapshot:\");\n        let _ = writeln!(out, \"  epoch={}\", self.epoch);\n        let _ = writeln!(out, \"  live_pods={:?}\", self.live_pods);\n        let _ = writeln!(\n            out,\n            \"  waiting_by_owner keys={:?}\",\n            self.waiting_by_owner.keys()\n        );\n\n        let _ = writeln!(out, \"  rs_owners_store:\");\n        for owner in self.rs_owners.state().into_iter().map(|a| (*a).clone()) {\n            let _ = writeln!(out, \"    {:?}\", owner);\n        }\n\n        let _ = writeln!(out, \"  job_owners_store:\");\n        for owner in self.job_owners.state().into_iter().map(|a| (*a).clone()) {\n            let _ = writeln!(out, \"    {:?}\", owner);\n        }\n\n        let _ = writeln!(out, \"  pods_store:\");\n        for pod in self.pods.state().into_iter().map(|a| (*a).clone()) {\n            let _ = writeln!(out, \"    {:?}\", pod);\n        }\n\n        out\n    }\n\n    /// Attempt to emit `PodNew` (+containers) for the given pod when resolvable.\n    ///\n    fn try_emit_pod(&mut self, p: PodMeta) -> Vec<RenderEvent> {\n        if !p.has_ip() {\n            return Vec::new();\n        }\n\n        // No owner\n        let owner = match &p.owner {\n            None => {\n                self.live_pods.insert(p.uid.clone());\n                return crate::output::RenderEvent::from_pod_new(&p, None);\n            }\n            Some(o) => o.clone(),\n        };\n\n        if !owner.kind.is_rs_or_job() {\n            self.live_pods.insert(p.uid.clone());\n            return crate::output::RenderEvent::from_pod_new(&p, Some(&owner));\n        }\n\n        // Owner is RS/Job: check if owner info is known and possibly escalate\n        if let Some(om) = self.get_owner_meta(&owner.uid) {\n            let escalated = match &om.controller {\n                Some(ctrl) if matches!(ctrl.kind, OwnerKind::Deployment | OwnerKind::CronJob) => {\n                    ctrl.clone()\n                }\n                _ => owner,\n            };\n            self.live_pods.insert(p.uid.clone());\n            return crate::output::RenderEvent::from_pod_new(&p, Some(&escalated));\n        }\n\n        // Not known yet: queue\n        let w = self.waiting_by_owner.entry(owner.uid.clone()).or_default();\n        if !w.iter().any(|u| u == &p.uid) {\n            w.push(p.uid.clone());\n        }\n        Vec::new()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::types::{ContainerMeta, OwnerRef};\n    use kube::runtime::reflector::store;\n\n    struct Harness {\n        m: Matcher,\n        rs_owners_w: kube::runtime::reflector::store::Writer<OwnerMeta>,\n        job_owners_w: kube::runtime::reflector::store::Writer<OwnerMeta>,\n        pods_w: kube::runtime::reflector::store::Writer<PodMeta>,\n    }\n\n    impl Harness {\n        fn new() -> Self {\n            let (rs_owners_store, rs_owners_w) = store::store::<OwnerMeta>();\n            let (job_owners_store, job_owners_w) = store::store::<OwnerMeta>();\n            let (pods_store, pods_w) = store::store::<PodMeta>();\n            let m = Matcher::new(rs_owners_store, job_owners_store, pods_store);\n            Self {\n                m,\n                rs_owners_w,\n                job_owners_w,\n                pods_w,\n            }\n        }\n        fn handle_rs_owner(&mut self, ev: Event<OwnerMeta>) -> Vec<RenderEvent> {\n            self.rs_owners_w.apply_watcher_event(&ev);\n            self.m.handle_owner(ev)\n        }\n        fn handle_job_owner(&mut self, ev: Event<OwnerMeta>) -> Vec<RenderEvent> {\n            self.job_owners_w.apply_watcher_event(&ev);\n            self.m.handle_owner(ev)\n        }\n        fn handle_pod(&mut self, ev: Event<PodMeta>) -> Vec<RenderEvent> {\n            self.pods_w.apply_watcher_event(&ev);\n            self.m.handle_pod(ev)\n        }\n    }\n\n    fn pod(uid: &str, ip: &str, owner: Option<OwnerRef>) -> PodMeta {\n        PodMeta {\n            uid: uid.into(),\n            ip: ip.into(),\n            name: format!(\"pod-{}\", uid),\n            namespace: \"default\".into(),\n            owner,\n            host_network: false,\n            version: \"'img:1'\".into(),\n            containers: vec![ContainerMeta {\n                id: format!(\"c-{}\", uid),\n                name: \"c\".into(),\n                image: \"img:1\".into(),\n            }],\n        }\n    }\n\n    fn rs_owner(uid: &str, controller: Option<OwnerRef>) -> OwnerMeta {\n        OwnerMeta {\n            uid: uid.into(),\n            controller,\n        }\n    }\n\n    fn ref_kind(kind: OwnerKind, uid: &str, name: &str) -> OwnerRef {\n        OwnerRef {\n            kind,\n            uid: uid.into(),\n            name: name.into(),\n        }\n    }\n\n    #[test]\n    // Pod arrives first, then owner Apply arrives later.\n    // The pod should be emitted once owner information is available.\n    fn pod_first_then_owner_emits_on_owner_apply() {\n        let mut h = Harness::new();\n        let events = h.handle_pod(Event::Apply(pod(\n            \"p1\",\n            \"10.0.0.1\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"r1\", \"rs\")),\n        )));\n        assert!(events.is_empty());\n        let out = h.handle_rs_owner(Event::Apply(rs_owner(\n            \"r1\",\n            Some(ref_kind(OwnerKind::Deployment, \"d1\", \"dep\")),\n        )));\n        assert_eq!(out.len(), 2);\n        match &out[0] {\n            RenderEvent::PodNew {\n                owner_kind,\n                owner_uid,\n                ..\n            } => {\n                assert_eq!(*owner_kind, OwnerKind::Deployment);\n                assert_eq!(owner_uid, \"d1\");\n            }\n            _ => panic!(\"expected PodNew\"),\n        }\n        match &out[1] {\n            RenderEvent::PodContainer { uid, .. } => assert_eq!(uid, \"p1\"),\n            _ => panic!(\"expected PodContainer\"),\n        }\n    }\n\n    #[test]\n    // Owner arrives first, then pod Apply.\n    // The pod should be emitted immediately using escalated owner when applicable.\n    fn owner_first_then_pod_emits_immediately() {\n        let mut h = Harness::new();\n        let _ = h.handle_rs_owner(Event::Apply(rs_owner(\n            \"r2\",\n            Some(ref_kind(OwnerKind::Deployment, \"d2\", \"dep\")),\n        )));\n        let out = h.handle_pod(Event::Apply(pod(\n            \"p2\",\n            \"10.0.0.2\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"r2\", \"rs\")),\n        )));\n        assert_eq!(out.len(), 2);\n        match &out[0] {\n            RenderEvent::PodNew {\n                owner_kind,\n                owner_uid,\n                ..\n            } => {\n                assert_eq!(*owner_kind, OwnerKind::Deployment);\n                assert_eq!(owner_uid, \"d2\");\n            }\n            _ => panic!(\"expected PodNew\"),\n        }\n        match &out[1] {\n            RenderEvent::PodContainer { uid, .. } => assert_eq!(uid, \"p2\"),\n            _ => panic!(\"expected PodContainer\"),\n        }\n    }\n\n    #[test]\n    fn initdone_starts_epoch_and_emits_resolvable() {\n        let mut h = Harness::new();\n        let _ = h.handle_rs_owner(Event::InitApply(rs_owner(\n            \"r3\",\n            Some(ref_kind(OwnerKind::Deployment, \"d3\", \"dep\")),\n        )));\n        // Complete owner init to swap snapshot into the owner Store\n        let _ = h.handle_rs_owner(Event::InitDone);\n        let _ = h.handle_pod(Event::InitApply(pod(\n            \"p3\",\n            \"10.0.0.3\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"r3\", \"rs\")),\n        )));\n        // Complete pod init to swap snapshot into the pod Store and start new epoch\n        let out = h.handle_pod(Event::InitDone);\n        assert!(matches!(out.first(), Some(RenderEvent::PodResync { .. })));\n        assert!(out.iter().any(|e| matches!(e, RenderEvent::PodNew { .. })));\n    }\n\n    #[test]\n    // When a pod has no owner, emit PodNew with NoOwner and containers.\n    fn ownerless_pod_emits_with_no_owner() {\n        let mut h = Harness::new();\n        let out = h.handle_pod(Event::Apply(pod(\"p0\", \"10.0.0.10\", None)));\n        assert_eq!(out.len(), 2);\n        match &out[0] {\n            RenderEvent::PodNew {\n                owner_kind,\n                owner_uid,\n                ..\n            } => {\n                assert_eq!(*owner_kind, OwnerKind::NoOwner);\n                assert!(owner_uid.is_empty());\n            }\n            _ => panic!(\"expected PodNew\"),\n        }\n        match &out[1] {\n            RenderEvent::PodContainer { uid, .. } => assert_eq!(uid, \"p0\"),\n            _ => panic!(\"expected PodContainer\"),\n        }\n    }\n\n    #[test]\n    // If the pod is owned by a non-RS/Job owner, emit directly with that owner.\n    fn non_rs_job_owner_emits_directly() {\n        let mut h = Harness::new();\n        let owner = ref_kind(OwnerKind::Other, \"x1\", \"owner\");\n        let out = h.handle_pod(Event::Apply(pod(\"p9\", \"10.0.0.9\", Some(owner.clone()))));\n        assert_eq!(out.len(), 2);\n        match &out[0] {\n            RenderEvent::PodNew {\n                owner_kind,\n                owner_uid,\n                owner_name,\n                ..\n            } => {\n                assert_eq!(*owner_kind, OwnerKind::Other);\n                assert_eq!(owner_uid, \"x1\");\n                assert_eq!(owner_name, \"owner\");\n            }\n            _ => panic!(\"expected PodNew\"),\n        }\n        match &out[1] {\n            RenderEvent::PodContainer { uid, .. } => assert_eq!(uid, \"p9\"),\n            _ => panic!(\"expected PodContainer\"),\n        }\n    }\n\n    #[test]\n    // Job→CronJob escalation: if the Job's controller is a CronJob,\n    // emit the pod with the CronJob as the effective owner.\n    fn job_cronjob_escalation() {\n        let mut h = Harness::new();\n        // pod owned by Job j1 but owner meta has controller CronJob c1\n        let _ = h.handle_job_owner(Event::Apply(rs_owner(\n            \"j1\",\n            Some(ref_kind(OwnerKind::CronJob, \"c1\", \"cj\")),\n        )));\n        // Actually RS owner meta function used for RS/Job both - here using rs_owner to avoid boilerplate as both use OwnerMeta\n        let out = h.handle_pod(Event::Apply(pod(\n            \"pj\",\n            \"10.0.0.20\",\n            Some(ref_kind(OwnerKind::Job, \"j1\", \"job\")),\n        )));\n        assert_eq!(out.len(), 2);\n        match &out[0] {\n            RenderEvent::PodNew {\n                owner_kind,\n                owner_uid,\n                owner_name,\n                ..\n            } => {\n                assert_eq!(*owner_kind, OwnerKind::CronJob);\n                assert_eq!(owner_uid, \"c1\");\n                assert_eq!(owner_name, \"cj\");\n            }\n            _ => panic!(\"expected PodNew\"),\n        }\n    }\n\n    #[test]\n    // Deleting a live pod should produce a PodDelete event and clear its state.\n    fn delete_emits_pod_delete_when_live() {\n        let mut h = Harness::new();\n        let _ = h.handle_rs_owner(Event::Apply(rs_owner(\n            \"r4\",\n            Some(ref_kind(OwnerKind::Deployment, \"d4\", \"dep\")),\n        )));\n        let _ = h.handle_pod(Event::Apply(pod(\n            \"pd\",\n            \"10.0.0.44\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"r4\", \"rs\")),\n        )));\n        let out = h.handle_pod(Event::Delete(pod(\n            \"pd\",\n            \"10.0.0.44\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"r4\", \"rs\")),\n        )));\n        assert_eq!(out.len(), 1);\n        match &out[0] {\n            RenderEvent::PodDelete { uid } => assert_eq!(uid, \"pd\"),\n            _ => panic!(\"expected PodDelete\"),\n        }\n    }\n\n    #[test]\n    // Owner flaps Apply+Delete: the delete is retained in the tombstone adapter.\n    // While retained, the store still reflects the owner Apply, so a pod Apply\n    // that depends on this owner should still emit.\n    fn owner_flaps_apply_delete_then_pod_apply_emits() {\n        let mut h = Harness::new();\n        let mut owner_tomb = crate::tombstone_adapter::TombstoneAdapter::new(\n            std::time::Duration::from_secs(60),\n            100,\n        );\n        // Owner Apply forwarded\n        for e in owner_tomb.handle(Event::Apply(rs_owner(\n            \"ro\",\n            Some(ref_kind(OwnerKind::Deployment, \"dep\", \"dep\")),\n        ))) {\n            let _ = h.handle_rs_owner(e);\n        }\n        // Owner Delete retained (no events forwarded)\n        assert!(owner_tomb\n            .handle(Event::Delete(rs_owner(\n                \"ro\",\n                Some(ref_kind(OwnerKind::Deployment, \"dep\", \"dep\"))\n            )))\n            .next()\n            .is_none());\n        // Pod Apply should emit now because owner is still present in store\n        let out = h.handle_pod(Event::Apply(pod(\n            \"pp\",\n            \"10.0.0.60\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"ro\", \"rs\")),\n        )));\n        assert!(out.iter().any(|e| matches!(e, RenderEvent::PodNew { .. })));\n    }\n\n    #[test]\n    // Pod flaps Apply+Delete: the delete is retained in the pod tombstone adapter.\n    // The pod Apply is forwarded (no emit because owner missing); on a later owner Apply,\n    // the pod should still emit despite the retained delete.\n    fn pod_flaps_apply_delete_then_owner_apply_emits() {\n        let mut h = Harness::new();\n        let mut pod_tomb = crate::tombstone_adapter::TombstoneAdapter::new(\n            std::time::Duration::from_secs(60),\n            100,\n        );\n        // Pod Apply forwarded to matcher (no owner yet -> no output)\n        for e in pod_tomb.handle(Event::Apply(pod(\n            \"pp2\",\n            \"10.0.0.61\",\n            Some(ref_kind(OwnerKind::ReplicaSet, \"ro2\", \"rs\")),\n        ))) {\n            let out = h.handle_pod(e);\n            assert!(out.is_empty());\n        }\n        // Pod Delete retained by tombstone adapter\n        assert!(pod_tomb\n            .handle(Event::Delete(pod(\n                \"pp2\",\n                \"10.0.0.61\",\n                Some(ref_kind(OwnerKind::ReplicaSet, \"ro2\", \"rs\"))\n            )))\n            .next()\n            .is_none());\n        // Owner Apply arrives -> should emit pod despite pending delete\n        let out = h.handle_rs_owner(Event::Apply(rs_owner(\n            \"ro2\",\n            Some(ref_kind(OwnerKind::Deployment, \"dep2\", \"dep\")),\n        )));\n        assert!(out.iter().any(|e| matches!(e, RenderEvent::PodNew { .. })));\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/output.rs",
    "content": "//! Normalized events produced by the matcher and consumed by the encoder.\n//!\n//! Events mirror the messages the reducer expects. `PodNew` is accompanied by\n//! one or more `PodContainer` events. `PodResync` indicates a new epoch.\n\nuse crate::types::{ContainerMeta, OwnerKind, OwnerRef, PodMeta};\n\n#[derive(Clone, Debug)]\npub enum RenderEvent {\n    /// Begin a new resync epoch with a monotonically increasing counter.\n    PodResync { epoch: u64 },\n    PodNew {\n        /// Pod UID\n        uid: String,\n        /// Pod IPv4 string\n        ip: String,\n        /// Pod name\n        pod_name: String,\n        /// Namespace\n        ns: String,\n        /// Deterministic version from images\n        version: String,\n        /// Effective owner kind\n        owner_kind: OwnerKind,\n        /// Effective owner UID (empty for NoOwner)\n        owner_uid: String,\n        /// Effective owner name (pod name for NoOwner)\n        owner_name: String,\n        /// Host network flag\n        is_host_network: bool,\n    },\n    PodContainer {\n        /// Pod UID for which the container is reported\n        uid: String,\n        /// Container metadata\n        container: ContainerMeta,\n    },\n    /// Delete a previously live pod\n    PodDelete { uid: String },\n}\n\nimpl RenderEvent {\n    /// Expand a `PodNew` event into the full set of render events; includes\n    /// `PodNew` itself and one `PodContainer` per container.\n    pub fn from_pod_new(pod: &PodMeta, owner: Option<&OwnerRef>) -> Vec<RenderEvent> {\n        let mut out = Vec::new();\n        let (owner_kind, owner_uid, owner_name) = match owner {\n            Some(o) => (o.kind, o.uid.clone(), o.name.clone()),\n            None => (OwnerKind::NoOwner, String::new(), pod.name.clone()),\n        };\n        out.push(RenderEvent::PodNew {\n            uid: pod.uid.clone(),\n            ip: pod.ip.clone(),\n            pod_name: pod.name.clone(),\n            ns: pod.namespace.clone(),\n            version: pod.version.clone(),\n            owner_kind,\n            owner_uid,\n            owner_name,\n            is_host_network: pod.host_network,\n        });\n        for c in &pod.containers {\n            out.push(RenderEvent::PodContainer {\n                uid: pod.uid.clone(),\n                container: c.clone(),\n            });\n        }\n        out\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/tombstone_adapter.rs",
    "content": "use std::collections::VecDeque;\nuse std::time::{Duration, Instant};\n\nuse kube::runtime::watcher::Event;\n\n/// Stream-level adapter that retains Delete events temporarily.\n///\n/// - For Apply/InitApply/Init events: emit expired deletes first, then forward the event unchanged.\n/// - For Delete events: retain internally; if capacity exceeded or entries have expired, emit those deletes.\n/// - For InitDone: clear the retained deletes (drop them) and forward InitDone.\npub struct TombstoneAdapter<T> {\n    ttl: Duration,\n    cap: usize,\n    q: VecDeque<(Instant, T)>,\n}\n\nimpl<T: Clone> TombstoneAdapter<T> {\n    /// Create a new adapter with the given time-to-live and capacity.\n    pub fn new(ttl: Duration, cap: usize) -> Self {\n        Self {\n            ttl,\n            cap,\n            q: VecDeque::new(),\n        }\n    }\n\n    /// Handle a new event and return an iterator over forwarded events:\n    /// - Delete: retained; may emit evicted/expired deletes\n    /// - Apply/Init/InitApply: emit expired deletes first, then the event\n    /// - InitDone: clear retention and forward InitDone\n    pub fn handle(&mut self, ev: Event<T>) -> impl Iterator<Item = Event<T>> {\n        let mut out: Vec<Event<T>> = Vec::new();\n        let now = Instant::now();\n\n        let mut drain_expired = |out: &mut Vec<Event<T>>| {\n            // Only inspect the timestamp by reference; clone `T` only when expired\n            loop {\n                match self.q.front() {\n                    Some((ts, _)) if now.duration_since(*ts) >= self.ttl => {\n                        if let Some((_, t)) = self.q.pop_front() {\n                            out.push(Event::Delete(t));\n                        }\n                    }\n                    _ => break,\n                }\n            }\n        };\n\n        match ev {\n            Event::Init => {\n                // Forward init after flushing expired deletes\n                drain_expired(&mut out);\n                out.push(Event::Init);\n            }\n            Event::InitApply(t) => {\n                drain_expired(&mut out);\n                out.push(Event::InitApply(t));\n            }\n            Event::Apply(t) => {\n                drain_expired(&mut out);\n                out.push(Event::Apply(t));\n            }\n            Event::Delete(t) => {\n                // retain; drop expired first\n                drain_expired(&mut out);\n                self.q.push_back((now, t));\n                while self.q.len() > self.cap {\n                    if let Some((_, t)) = self.q.pop_front() {\n                        out.push(Event::Delete(t));\n                    }\n                }\n            }\n            Event::InitDone => {\n                // Drop retained deletes on init-done; forward the signal\n                self.q.clear();\n                out.push(Event::InitDone);\n            }\n        }\n\n        out.into_iter()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    // Capacity eviction: when capacity is 1, inserting a second Delete\n    // should evict (and thus emit) the oldest retained Delete event.\n    fn capacity_evicts_oldest_delete() {\n        let mut t = TombstoneAdapter::new(Duration::from_secs(60), 1);\n        // First delete retained, nothing emitted\n        assert!(t.handle(Event::Delete(1)).next().is_none());\n        // Second delete should evict the first\n        let mut evs = t.handle(Event::Delete(2));\n        match evs.next() {\n            Some(Event::Delete(x)) => assert_eq!(x, 1),\n            _ => panic!(\"expected Delete(1)\"),\n        }\n        assert!(evs.next().is_none());\n    }\n\n    #[test]\n    // InitDone clears the retention queue, so no previously retained\n    // deletes should be surfaced after it. The InitDone marker itself\n    // is forwarded.\n    fn initdone_clears_queue() {\n        let mut t = TombstoneAdapter::new(Duration::from_secs(60), 2);\n        // Retain two deletes\n        assert!(t.handle(Event::Delete(10)).next().is_none());\n        assert!(t.handle(Event::Delete(20)).next().is_none());\n        // InitDone should drop queue and forward InitDone\n        let mut evs = t.handle(Event::InitDone);\n        matches!(evs.next(), Some(Event::InitDone));\n        assert!(evs.next().is_none());\n        // Another delete should not surface the old deletes anymore, they are dropped\n        let mut evs2 = t.handle(Event::Delete(30));\n        if evs2.next().is_some() {\n            panic!(\"expected event - queue should be empty\");\n        }\n        assert!(evs2.next().is_none());\n    }\n\n    #[test]\n    // TTL expiration: with ttl=0, a retained Delete is expired immediately\n    // on the next handle() call and emitted before forwarding the new event.\n    fn ttl_expires_before_apply() {\n        // ttl=0 forces immediate expiration on next handle\n        let mut t = TombstoneAdapter::new(Duration::from_secs(0), 10);\n        assert!(t.handle(Event::Delete(42)).next().is_none());\n        // Next non-delete should surface the expired delete first\n        let mut it = t.handle(Event::Apply(7));\n        match it.next() {\n            Some(Event::Delete(x)) => assert_eq!(x, 42),\n            _ => panic!(\"expected Delete(42)\"),\n        }\n        match it.next() {\n            Some(Event::Apply(x)) => assert_eq!(x, 7),\n            _ => panic!(\"expected Apply(7)\"),\n        }\n        assert!(it.next().is_none());\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/types.rs",
    "content": "//! Compact metadata types used by the collector.\n//!\n//! These types are derived from Kubernetes objects and used internally by the\n//! matcher and output layers.\n\nuse std::fmt;\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum OwnerKind {\n    /// apps/v1 ReplicaSet\n    ReplicaSet,\n    /// apps/v1 Deployment\n    Deployment,\n    /// batch/v1 Job\n    Job,\n    /// batch/v1 CronJob\n    CronJob,\n    /// Explicitly indicates no owner\n    NoOwner,\n    /// Any other kind; treated as opaque\n    Other,\n}\n\nimpl OwnerKind {\n    pub fn is_rs_or_job(self) -> bool {\n        matches!(self, OwnerKind::ReplicaSet | OwnerKind::Job)\n    }\n}\n\nimpl std::str::FromStr for OwnerKind {\n    type Err = ();\n\n    /// Parse a human-readable Kubernetes kind string into an [`OwnerKind`].\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(match s {\n            \"ReplicaSet\" => OwnerKind::ReplicaSet,\n            \"Deployment\" => OwnerKind::Deployment,\n            \"Job\" => OwnerKind::Job,\n            \"CronJob\" => OwnerKind::CronJob,\n            \"NoOwner\" => OwnerKind::NoOwner,\n            _ => OwnerKind::Other,\n        })\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct OwnerRef {\n    /// Kubernetes owner kind\n    pub kind: OwnerKind,\n    /// Owner UID\n    pub uid: String,\n    /// Owner name\n    pub name: String,\n}\n\nimpl fmt::Display for OwnerRef {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(\n            f,\n            \"{}:{}:{}\",\n            match self.kind {\n                OwnerKind::ReplicaSet => \"ReplicaSet\",\n                OwnerKind::Deployment => \"Deployment\",\n                OwnerKind::Job => \"Job\",\n                OwnerKind::CronJob => \"CronJob\",\n                OwnerKind::NoOwner => \"NoOwner\",\n                OwnerKind::Other => \"Other\",\n            },\n            self.name,\n            self.uid\n        )\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ContainerMeta {\n    /// Container runtime ID (e.g., docker://... or containerd://...)\n    pub id: String,\n    /// Container name\n    pub name: String,\n    /// Container image reference\n    pub image: String,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct PodMeta {\n    /// Pod UID\n    pub uid: String,\n    /// Pod IP address (empty if not yet assigned)\n    pub ip: String,\n    /// Pod name\n    pub name: String,\n    /// Namespace name\n    pub namespace: String,\n    /// Controller owner (if any)\n    pub owner: Option<OwnerRef>,\n    /// Whether the Pod uses host networking\n    pub host_network: bool,\n    /// Deterministic version built from container images\n    pub version: String,\n    /// Containers observed for this Pod\n    pub containers: Vec<ContainerMeta>,\n}\n\nimpl PodMeta {\n    pub fn has_ip(&self) -> bool {\n        !self.ip.is_empty()\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct OwnerMeta {\n    /// UID of the owner (ReplicaSet or Job)\n    pub uid: String,\n    /// Its owner reference, if any (e.g. Deployment for RS, CronJob for Job)\n    pub controller: Option<OwnerRef>,\n}\n"
  },
  {
    "path": "crates/k8s-collector/src/writer.rs",
    "content": "//! Async TCP writer to the reducer with LZ4 streaming compression.\n//!\n//! Semantics:\n//! - On connect, the first message is sent uncompressed to negotiate\n//!   decompression on the reducer side.\n//! - Subsequent messages are appended to a single persistent LZ4 frame.\n//! - `flush()` calls the encoder's `flush()` and drains any newly produced bytes\n//!   to the socket, then flushes the socket.\n//!\n//! The writer only writes the newly produced compressed tail per call; the\n//! underlying frame buffer is retained between calls and only reset on\n//! reconnect.\n\nuse std::io;\nuse tokio::io::{AsyncWrite, AsyncWriteExt};\nuse tokio_util::io::SyncIoBridge;\n\n/// Internal sink state: uncompressed passthrough initially, then a persistent\n/// LZ4 frame once the first message has been sent.\nenum Sink<W: AsyncWrite + Unpin + Send + 'static> {\n    Uncompressed(W),\n    Compressed {\n        encoder: Box<lz4_flex::frame::FrameEncoder<SyncIoBridge<W>>>,\n    },\n}\n\npub struct Writer<W: AsyncWrite + Unpin + Send + 'static> {\n    // Invariant: always Some; None is a temporary state during transitions\n    sink: Option<Sink<W>>,\n}\n\nimpl<W: AsyncWrite + Unpin + Send + 'static> Writer<W> {\n    /// Create a new writer. The first message is sent uncompressed; subsequent\n    /// messages are appended to a persistent LZ4 frame.\n    pub fn new(sink: W) -> Self {\n        Self {\n            sink: Some(Sink::Uncompressed(sink)),\n        }\n    }\n\n    /// Send a single message, applying the protocol described above.\n    pub async fn send(&mut self, buf: &[u8]) -> io::Result<()> {\n        // Take ownership of the sink enum to allow moving `W` between variants\n        let sink = self.sink.take().expect(\"unreachable: sink must be set\");\n        match sink {\n            Sink::Uncompressed(mut w) => {\n                // First message: send uncompressed, then switch to compressed mode\n                w.write_all(buf).await?;\n                // Switch to compressed mode where the encoder writes directly to the inner sink\n                let bridge = SyncIoBridge::new(w);\n                let encoder = lz4_flex::frame::FrameEncoder::new(bridge);\n                self.sink = Some(Sink::Compressed {\n                    encoder: Box::new(encoder),\n                });\n                Ok(())\n            }\n            Sink::Compressed { mut encoder } => {\n                use std::io::Write;\n                // Perform synchronous compression in a blocking task; the bridge\n                // will block on the async writer without panicking.\n                let data = buf.to_vec();\n                let res = tokio::task::spawn_blocking(move || {\n                    encoder.write_all(&data)?;\n                    Ok::<_, io::Error>(encoder)\n                })\n                .await\n                .map_err(|e| io::Error::other(format!(\"join error: {}\", e)))??;\n                self.sink = Some(Sink::Compressed { encoder: res });\n                Ok(())\n            }\n        }\n    }\n\n    /// Flush any pending compressed bytes and the underlying socket.\n    pub async fn flush(&mut self) -> io::Result<()> {\n        // We need ownership of the encoder to run flush inside spawn_blocking,\n        // so we temporarily take the enum and put it back after flushing.\n        let sink = self.sink.take().expect(\"unreachable: sink must be set\");\n        match sink {\n            Sink::Uncompressed(mut w) => {\n                w.flush().await?;\n                self.sink = Some(Sink::Uncompressed(w));\n                Ok(())\n            }\n            Sink::Compressed { encoder } => {\n                // Flush the encoder which flushes the underlying sink via the bridge\n                use std::io::Write;\n                let res = tokio::task::spawn_blocking(move || {\n                    let mut enc = encoder;\n                    enc.flush()?;\n                    Ok::<_, io::Error>(enc)\n                })\n                .await\n                .map_err(|e| io::Error::other(format!(\"join error: {}\", e)))??;\n                self.sink = Some(Sink::Compressed { encoder: res });\n                Ok(())\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use lz4_flex::frame::FrameDecoder;\n    use std::io::Read;\n    use std::pin::Pin;\n    use std::sync::{Arc, Mutex};\n    use tokio::io::AsyncWrite;\n\n    /// Simple in-memory AsyncWrite sink used for tests.\n    /// Bytes written are appended to the shared buffer.\n    struct TestSink(pub Arc<Mutex<Vec<u8>>>);\n\n    impl AsyncWrite for TestSink {\n        fn poll_write(\n            self: Pin<&mut Self>,\n            _cx: &mut std::task::Context<'_>,\n            buf: &[u8],\n        ) -> std::task::Poll<std::io::Result<usize>> {\n            let mut g = self.0.lock().unwrap();\n            g.extend_from_slice(buf);\n            std::task::Poll::Ready(Ok(buf.len()))\n        }\n\n        fn poll_flush(\n            self: Pin<&mut Self>,\n            _cx: &mut std::task::Context<'_>,\n        ) -> std::task::Poll<std::io::Result<()>> {\n            std::task::Poll::Ready(Ok(()))\n        }\n\n        fn poll_shutdown(\n            self: Pin<&mut Self>,\n            _cx: &mut std::task::Context<'_>,\n        ) -> std::task::Poll<std::io::Result<()>> {\n            std::task::Poll::Ready(Ok(()))\n        }\n    }\n\n    #[test]\n    // Property-based test: with compression enabled, the first flush exposes the\n    // uncompressed handshake bytes; subsequent flushes append to the persistent\n    // LZ4 frame. Decoding the combined compressed bytes after both flushes must\n    // equal the concatenation of the payloads.\n    fn flush_visibility_compressed_prop() {\n        // Property-based: first flush exposes the uncompressed handshake (a),\n        // subsequent flushes append to the persistent LZ4 frame (b then b+c).\n        proptest::proptest!(|(a in \"[ -~]{0,64}\", b in \"[ -~]{0,64}\", c in \"[ -~]{0,64}\")| {\n            let buf = Arc::new(Mutex::new(Vec::new()));\n            let sink = TestSink(buf.clone());\n            let rt = tokio::runtime::Runtime::new().unwrap();\n            let a2 = a.clone(); let b2 = b.clone(); let c2 = c.clone();\n            rt.block_on(async move {\n                let mut w = super::Writer::new(sink);\n                // First write is uncompressed handshake\n                w.send(a2.as_bytes()).await.unwrap();\n                w.flush().await.unwrap();\n                // Verify handshake visible\n                assert_eq!(&buf.lock().unwrap()[..], a2.as_bytes());\n\n                // Second write goes into persistent frame\n                w.send(b2.as_bytes()).await.unwrap();\n                w.flush().await.unwrap();\n                let snap = buf.lock().unwrap().clone();\n                let (first, rest) = snap.split_at(a2.len());\n                assert_eq!(first, a2.as_bytes());\n                let mut dec = FrameDecoder::new(rest);\n                let mut out = Vec::new();\n                dec.read_to_end(&mut out).unwrap();\n                assert_eq!(out, b2.as_bytes());\n\n                // Third write appends to same frame\n                w.send(c2.as_bytes()).await.unwrap();\n                w.flush().await.unwrap();\n                let snap2 = buf.lock().unwrap().clone();\n                let (_first2, rest2) = snap2.split_at(a2.len());\n                let mut dec2 = FrameDecoder::new(rest2);\n                let mut out2 = Vec::new();\n                dec2.read_to_end(&mut out2).unwrap();\n                let mut expected = Vec::new();\n                expected.extend_from_slice(b2.as_bytes());\n                expected.extend_from_slice(c2.as_bytes());\n                assert_eq!(out2, expected);\n            });\n        });\n    }\n}\n"
  },
  {
    "path": "crates/k8s-collector/tests/collector_prop.rs",
    "content": "use std::collections::{BTreeMap, BTreeSet, HashMap};\nuse std::time::Duration;\n\nuse k8s_collector::matcher::Matcher;\nuse k8s_collector::output::RenderEvent;\nuse k8s_collector::tombstone_adapter::TombstoneAdapter;\nuse k8s_collector::types::{ContainerMeta, OwnerKind, OwnerMeta, OwnerRef, PodMeta};\nuse kube::runtime::reflector::store;\nuse kube::runtime::watcher::Event;\nuse proptest::prelude::*;\n\n/// Minimal, synchronous harness that mirrors the production Collector pipeline:\n/// - tombstone adapters in front of\n/// - reflector writers feeding Stores used by\n/// - Matcher, which produces RenderEvents.\nstruct PipelineHarness {\n    matcher: Matcher,\n    rs_owners_writer: store::Writer<OwnerMeta>,\n    job_owners_writer: store::Writer<OwnerMeta>,\n    pods_writer: store::Writer<PodMeta>,\n    rs_owner_tomb: TombstoneAdapter<OwnerMeta>,\n    job_owner_tomb: TombstoneAdapter<OwnerMeta>,\n    pod_tomb: TombstoneAdapter<PodMeta>,\n}\n\nimpl PipelineHarness {\n    fn new() -> Self {\n        let (rs_owners_store, rs_owners_writer) = store::store::<OwnerMeta>();\n        let (job_owners_store, job_owners_writer) = store::store::<OwnerMeta>();\n        let (pods_store, pods_writer) = store::store::<PodMeta>();\n        let matcher = Matcher::new(rs_owners_store, job_owners_store, pods_store);\n        let rs_owner_tomb = TombstoneAdapter::new(Duration::from_secs(0), 1024);\n        let job_owner_tomb = TombstoneAdapter::new(Duration::from_secs(0), 1024);\n        let pod_tomb = TombstoneAdapter::new(Duration::from_secs(0), 1024);\n        Self {\n            matcher,\n            rs_owners_writer,\n            job_owners_writer,\n            pods_writer,\n            rs_owner_tomb,\n            job_owner_tomb,\n            pod_tomb,\n        }\n    }\n\n    fn apply_rs_owner(&mut self, ev: Event<OwnerMeta>) -> Vec<RenderEvent> {\n        let mut out = Vec::new();\n        for fwd in self.rs_owner_tomb.handle(ev) {\n            self.rs_owners_writer.apply_watcher_event(&fwd);\n            out.extend(self.matcher.handle_owner(fwd));\n        }\n        out\n    }\n\n    fn apply_job_owner(&mut self, ev: Event<OwnerMeta>) -> Vec<RenderEvent> {\n        let mut out = Vec::new();\n        for fwd in self.job_owner_tomb.handle(ev) {\n            self.job_owners_writer.apply_watcher_event(&fwd);\n            out.extend(self.matcher.handle_owner(fwd));\n        }\n        out\n    }\n\n    fn apply_pod(&mut self, ev: Event<PodMeta>) -> Vec<RenderEvent> {\n        let mut out = Vec::new();\n        for fwd in self.pod_tomb.handle(ev) {\n            self.pods_writer.apply_watcher_event(&fwd);\n            out.extend(self.matcher.handle_pod(fwd));\n        }\n        out\n    }\n}\n\n/// How a pod is owned in Kubernetes, restricted to the shapes\n/// the collector actually supports (RS/Job with optional controller).\n#[derive(Clone, Debug)]\nenum OwnerScenario {\n    NoOwner,\n    ReplicaSetOnly,\n    ReplicaSetWithDeployment,\n    JobOnly,\n    JobWithCronJob,\n}\n\nimpl OwnerScenario {\n    fn from_tag(tag: u8) -> Self {\n        match tag % 5 {\n            0 => OwnerScenario::NoOwner,\n            1 => OwnerScenario::ReplicaSetOnly,\n            2 => OwnerScenario::ReplicaSetWithDeployment,\n            3 => OwnerScenario::JobOnly,\n            _ => OwnerScenario::JobWithCronJob,\n        }\n    }\n\n    /// Build the k8s meta objects for this scenario:\n    /// - OwnerRef embedded into PodMeta (if any)\n    /// - OwnerMeta objects for ReplicaSet/Job owners\n    /// - The effective owner after escalation (Deployment/CronJob) for the model.\n    fn build_for_pod(&self, pod_id: u8) -> (Option<OwnerRef>, Vec<OwnerMeta>, Option<OwnerRef>) {\n        let rs_uid = format!(\"rs-{pod_id}\");\n        let rs_name = format!(\"rs-{pod_id}\");\n        let dep_uid = format!(\"dep-{pod_id}\");\n        let dep_name = format!(\"dep-{pod_id}\");\n        let job_uid = format!(\"job-{pod_id}\");\n        let job_name = format!(\"job-{pod_id}\");\n        let cron_uid = format!(\"cron-{pod_id}\");\n        let cron_name = format!(\"cron-{pod_id}\");\n\n        match self {\n            OwnerScenario::NoOwner => (None, Vec::new(), None),\n            OwnerScenario::ReplicaSetOnly => {\n                let pod_owner = OwnerRef {\n                    kind: OwnerKind::ReplicaSet,\n                    uid: rs_uid.clone(),\n                    name: rs_name.clone(),\n                };\n                let owner_meta = OwnerMeta {\n                    uid: rs_uid,\n                    controller: None,\n                };\n                (Some(pod_owner.clone()), vec![owner_meta], Some(pod_owner))\n            }\n            OwnerScenario::ReplicaSetWithDeployment => {\n                let pod_owner = OwnerRef {\n                    kind: OwnerKind::ReplicaSet,\n                    uid: rs_uid.clone(),\n                    name: rs_name.clone(),\n                };\n                let controller = OwnerRef {\n                    kind: OwnerKind::Deployment,\n                    uid: dep_uid.clone(),\n                    name: dep_name.clone(),\n                };\n                let owner_meta = OwnerMeta {\n                    uid: rs_uid,\n                    controller: Some(controller.clone()),\n                };\n                (Some(pod_owner), vec![owner_meta], Some(controller))\n            }\n            OwnerScenario::JobOnly => {\n                let pod_owner = OwnerRef {\n                    kind: OwnerKind::Job,\n                    uid: job_uid.clone(),\n                    name: job_name.clone(),\n                };\n                let owner_meta = OwnerMeta {\n                    uid: job_uid,\n                    controller: None,\n                };\n                (Some(pod_owner.clone()), vec![owner_meta], Some(pod_owner))\n            }\n            OwnerScenario::JobWithCronJob => {\n                let pod_owner = OwnerRef {\n                    kind: OwnerKind::Job,\n                    uid: job_uid.clone(),\n                    name: job_name.clone(),\n                };\n                let controller = OwnerRef {\n                    kind: OwnerKind::CronJob,\n                    uid: cron_uid.clone(),\n                    name: cron_name.clone(),\n                };\n                let owner_meta = OwnerMeta {\n                    uid: job_uid,\n                    controller: Some(controller.clone()),\n                };\n                (Some(pod_owner), vec![owner_meta], Some(controller))\n            }\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct ModelPod {\n    pod_id: u8,\n    uid: String,\n    scenario: OwnerScenario,\n    present: bool,\n    has_ip: bool,\n    effective_owner: Option<OwnerRef>,\n}\n\n#[derive(Clone, Debug, Default)]\nstruct ModelState {\n    pods: BTreeMap<u8, ModelPod>,\n    epoch: u64,\n}\n\nimpl ModelState {\n    fn is_live(&self, pod_id: u8) -> bool {\n        self.pods\n            .get(&pod_id)\n            .map(|p| p.present && p.has_ip)\n            .unwrap_or(false)\n    }\n\n    fn live_uids(&self) -> BTreeSet<String> {\n        self.pods\n            .values()\n            .filter(|p| p.present && p.has_ip)\n            .map(|p| p.uid.clone())\n            .collect()\n    }\n}\n\n/// Helper to construct a simple PodMeta (single container, fixed image)\n/// with a stable UID/IP derived from pod_id.\nfn build_pod_meta(pod_id: u8, owner: Option<OwnerRef>) -> PodMeta {\n    let uid = format!(\"pod-{pod_id}\");\n    PodMeta {\n        uid,\n        ip: format!(\"10.0.0.{pod_id}\"),\n        name: format!(\"pod-{pod_id}\"),\n        namespace: \"default\".into(),\n        owner,\n        host_network: false,\n        version: \"'img:1'\".into(),\n        containers: vec![ContainerMeta {\n            id: format!(\"c-{pod_id}\"),\n            name: \"c\".into(),\n            image: \"img:1\".into(),\n        }],\n    }\n}\n\n#[derive(Clone, Debug)]\nenum Op {\n    AddPod { pod_id: u8, scenario: OwnerScenario },\n    DeletePod { pod_id: u8 },\n    Reinit,\n}\n\n#[test]\nfn prop_collector_matches_pod_model() {\n    let config = ProptestConfig {\n        cases: 64,\n        ..ProptestConfig::default()\n    };\n\n    // Each step carries:\n    // - tag: selects op kind and also flips arrival order in AddPod\n    // - pod_id: small identifier mapped to a UID\n    // - owner_tag: mapped into an OwnerScenario\n    proptest!(config, |(ops in proptest::collection::vec((any::<u8>(), any::<u8>(), any::<u8>()), 0..128))| {\n        let mut harness = PipelineHarness::new();\n        let mut model = ModelState::default();\n        let mut first_seen: HashMap<String, &'static str> = HashMap::new();\n        let mut last_epoch: u64 = 0;\n\n        for (tag, pod_id, owner_tag) in ops {\n            let scenario = OwnerScenario::from_tag(owner_tag);\n            let op = match tag % 3 {\n                0 => Op::AddPod { pod_id, scenario },\n                1 => Op::DeletePod { pod_id },\n                _ => Op::Reinit,\n            };\n\n            let mut step_events: Vec<RenderEvent> = Vec::new();\n\n            match op {\n                Op::AddPod { pod_id, scenario } => {\n                    // Add or update a pod with a particular ownership scenario.\n                    // We randomize whether owners or pod arrive first and assert:\n                    // - exactly one PodNew when the pod becomes live\n                    // - PodNew comes from the second arrival\n                    // - PodNew owner fields match the scenario.\n                    let was_live = model.is_live(pod_id);\n                    let (pod_owner_ref, owners_meta, effective_owner) = scenario.build_for_pod(pod_id);\n                    let uid = format!(\"pod-{pod_id}\");\n                    let pod_meta = build_pod_meta(pod_id, pod_owner_ref.clone());\n\n                    let owners_first = (tag & 0x80) != 0;\n                    let mut owner_events = Vec::new();\n                    let mut pod_events = Vec::new();\n\n                    if owners_first {\n                        for owner in owners_meta.iter().cloned() {\n                            match scenario {\n                                OwnerScenario::ReplicaSetOnly | OwnerScenario::ReplicaSetWithDeployment => {\n                                    owner_events.extend(harness.apply_rs_owner(Event::Apply(owner)));\n                                }\n                                OwnerScenario::JobOnly | OwnerScenario::JobWithCronJob => {\n                                    owner_events.extend(harness.apply_job_owner(Event::Apply(owner)));\n                                }\n                                OwnerScenario::NoOwner => {}\n                            }\n                        }\n                        pod_events.extend(harness.apply_pod(Event::Apply(pod_meta)));\n                    } else {\n                        pod_events.extend(harness.apply_pod(Event::Apply(pod_meta)));\n                        for owner in owners_meta.iter().cloned() {\n                            match scenario {\n                                OwnerScenario::ReplicaSetOnly | OwnerScenario::ReplicaSetWithDeployment => {\n                                    owner_events.extend(harness.apply_rs_owner(Event::Apply(owner)));\n                                }\n                                OwnerScenario::JobOnly | OwnerScenario::JobWithCronJob => {\n                                    owner_events.extend(harness.apply_job_owner(Event::Apply(owner)));\n                                }\n                                OwnerScenario::NoOwner => {}\n                            }\n                        }\n                    }\n\n                    model.pods.insert(\n                        pod_id,\n                        ModelPod {\n                            pod_id,\n                            uid: uid.clone(),\n                            scenario,\n                            present: true,\n                            has_ip: true,\n                            effective_owner,\n                        },\n                    );\n                    let now_live = model.is_live(pod_id);\n\n                    if !was_live && now_live {\n                        let num_podnew_owner = owner_events\n                            .iter()\n                            .filter(|ev| matches!(ev, RenderEvent::PodNew { uid: ev_uid, .. } if ev_uid == &uid))\n                            .count();\n                        let num_podnew_pod = pod_events\n                            .iter()\n                            .filter(|ev| matches!(ev, RenderEvent::PodNew { uid: ev_uid, .. } if ev_uid == &uid))\n                            .count();\n                        let total_podnew = num_podnew_owner + num_podnew_pod;\n                        prop_assert_eq!(total_podnew, 1, \"expected exactly one PodNew for uid {}\", uid);\n\n                        let mp = model.pods.get(&pod_id).expect(\"model should contain pod after AddPod\");\n\n                        match &mp.scenario {\n                            OwnerScenario::NoOwner => {\n                                prop_assert_eq!(num_podnew_pod, 1, \"NoOwner PodNew should come from pod_events\");\n                                prop_assert_eq!(num_podnew_owner, 0, \"NoOwner PodNew should not come from owner_events\");\n                            }\n                            _ => {\n                                if owners_first {\n                                    prop_assert_eq!(num_podnew_pod, 1, \"owners_first: PodNew should come from pod_events\");\n                                    prop_assert_eq!(num_podnew_owner, 0, \"owners_first: PodNew should not come from owner_events\");\n                                } else {\n                                    prop_assert_eq!(num_podnew_owner, 1, \"pod_first: PodNew should come from owner_events\");\n                                    prop_assert_eq!(num_podnew_pod, 0, \"pod_first: PodNew should not come from pod_events\");\n                                }\n                            }\n                        }\n\n                        let podnew_ev = owner_events\n                            .iter()\n                            .chain(pod_events.iter())\n                            .find(|ev| matches!(ev, RenderEvent::PodNew { uid: ev_uid, .. } if ev_uid == &uid))\n                            .expect(\"PodNew must be present for live transition\");\n\n                        if let RenderEvent::PodNew {\n                            owner_kind,\n                            owner_uid,\n                            owner_name,\n                            pod_name,\n                            ..\n                        } = podnew_ev\n                        {\n                            match &mp.effective_owner {\n                                None => {\n                                    prop_assert_eq!(\n                                        *owner_kind,\n                                        OwnerKind::NoOwner,\n                                        \"AddPod: expected NoOwner for uid {}\",\n                                        uid\n                                    );\n                                    prop_assert!(\n                                        owner_uid.is_empty(),\n                                        \"AddPod: owner_uid should be empty for NoOwner uid {}\",\n                                        uid\n                                    );\n                                    prop_assert_eq!(\n                                        owner_name,\n                                        pod_name,\n                                        \"AddPod: owner_name should equal pod_name for NoOwner uid {}\",\n                                        uid\n                                    );\n                                }\n                                Some(o) => {\n                                    prop_assert_eq!(\n                                        *owner_kind,\n                                        o.kind,\n                                        \"AddPod: owner_kind mismatch for uid {}\",\n                                        uid\n                                    );\n                                    prop_assert_eq!(\n                                        owner_uid,\n                                        &o.uid,\n                                        \"AddPod: owner_uid mismatch for uid {}\",\n                                        uid\n                                    );\n                                    prop_assert_eq!(\n                                        owner_name,\n                                        &o.name,\n                                        \"AddPod: owner_name mismatch for uid {}\",\n                                        uid\n                                    );\n                                }\n                            }\n                        }\n                    }\n\n                    step_events.extend(owner_events);\n                    step_events.extend(pod_events);\n                }\n                Op::DeletePod { pod_id } => {\n                    // Delete a pod if present; we only assert indirectly via\n                    // subsequent resync snapshots (PodNew should no longer appear).\n                    if let Some(mp) = model.pods.get_mut(&pod_id) {\n                        mp.present = false;\n                        let (owner_ref, _owners, _effective_owner) = mp.scenario.build_for_pod(pod_id);\n                        let pod_meta = build_pod_meta(pod_id, owner_ref);\n                        let events = harness.apply_pod(Event::Delete(pod_meta));\n                        step_events.extend(events);\n                    }\n                }\n                Op::Reinit => {\n                    // Simulate a watcher re-init: Init/InitApply/InitDone for\n                    // owners then pods. Matcher should emit:\n                    // - a new PodResync{epoch}\n                    // - PodNew for exactly the live pods in the model.\n                    model.epoch = model.epoch.wrapping_add(1);\n\n                    let mut rs_owners_snapshot: Vec<OwnerMeta> = Vec::new();\n                    let mut job_owners_snapshot: Vec<OwnerMeta> = Vec::new();\n                    for mp in model.pods.values() {\n                        if !mp.present {\n                            continue;\n                        }\n                        let (_owner_ref, owners_meta, _effective_owner) = mp.scenario.build_for_pod(mp.pod_id);\n                        match mp.scenario {\n                            OwnerScenario::ReplicaSetOnly | OwnerScenario::ReplicaSetWithDeployment => {\n                                rs_owners_snapshot.extend(owners_meta);\n                            }\n                            OwnerScenario::JobOnly | OwnerScenario::JobWithCronJob => {\n                                job_owners_snapshot.extend(owners_meta);\n                            }\n                            OwnerScenario::NoOwner => {}\n                        }\n                    }\n\n                    let mut pods_snapshot: Vec<PodMeta> = Vec::new();\n                    for mp in model.pods.values() {\n                        if !mp.present {\n                            continue;\n                        }\n                        let (owner_ref, _owners_meta, _effective_owner) = mp.scenario.build_for_pod(mp.pod_id);\n                        let pod_meta = build_pod_meta(mp.pod_id, owner_ref);\n                        pods_snapshot.push(pod_meta);\n                    }\n\n                    let mut epoch_events: Vec<RenderEvent> = Vec::new();\n                    // RS owners re-init\n                    epoch_events.extend(harness.apply_rs_owner(Event::Init));\n                    for owner in rs_owners_snapshot.into_iter() {\n                        epoch_events.extend(harness.apply_rs_owner(Event::InitApply(owner)));\n                    }\n                    epoch_events.extend(harness.apply_rs_owner(Event::InitDone));\n                    // Job owners re-init\n                    epoch_events.extend(harness.apply_job_owner(Event::Init));\n                    for owner in job_owners_snapshot.into_iter() {\n                        epoch_events.extend(harness.apply_job_owner(Event::InitApply(owner)));\n                    }\n                    epoch_events.extend(harness.apply_job_owner(Event::InitDone));\n                    epoch_events.extend(harness.apply_pod(Event::Init));\n                    for pod_meta in pods_snapshot.into_iter() {\n                        epoch_events.extend(harness.apply_pod(Event::InitApply(pod_meta)));\n                    }\n                    epoch_events.extend(harness.apply_pod(Event::InitDone));\n\n                    let mut resync_epoch: Option<u64> = None;\n                    let mut seen_resync = false;\n                    let mut snapshot_uids: BTreeSet<String> = BTreeSet::new();\n                    for ev in &epoch_events {\n                        match ev {\n                            RenderEvent::PodResync { epoch } => {\n                                if !seen_resync {\n                                    resync_epoch = Some(*epoch);\n                                    seen_resync = true;\n                                }\n                            }\n                            RenderEvent::PodNew { uid, .. } => {\n                                if seen_resync {\n                                    snapshot_uids.insert(uid.clone());\n                                }\n                            }\n                            _ => {}\n                        }\n                    }\n\n                    prop_assert!(seen_resync, \"expected PodResync event on Reinit\");\n                    let epoch = resync_epoch.unwrap();\n                    prop_assert!(epoch > last_epoch, \"epoch should increase monotonically\");\n                    last_epoch = epoch;\n\n                    let live = model.live_uids();\n                    prop_assert_eq!(snapshot_uids, live, \"snapshot PodNew uids must equal live set\");\n\n                    step_events.extend(epoch_events);\n                }\n            }\n\n            for ev in &step_events {\n                match ev {\n                    RenderEvent::PodNew {\n                        uid,\n                        owner_kind,\n                        owner_uid,\n                        owner_name,\n                        pod_name,\n                        ..\n                    } => {\n                        // First event for a uid must be PodNew.\n                        if !first_seen.contains_key(uid) {\n                            first_seen.insert(uid.clone(), \"PodNew\");\n                        }\n\n                        let mp_opt = model\n                            .pods\n                            .values()\n                            .find(|p| &p.uid == uid);\n                        prop_assert!(mp_opt.is_some(), \"PodNew for unknown uid {}\", uid);\n                        let mp = mp_opt.unwrap();\n                        match &mp.effective_owner {\n                            None => {\n                                prop_assert_eq!(*owner_kind, OwnerKind::NoOwner, \"expected NoOwner for uid {}\", uid);\n                                prop_assert!(owner_uid.is_empty(), \"owner_uid should be empty for NoOwner uid {}\", uid);\n                                prop_assert_eq!(owner_name, pod_name, \"owner_name should equal pod_name for NoOwner uid {}\", uid);\n                            }\n                            Some(o) => {\n                                prop_assert_eq!(*owner_kind, o.kind, \"owner_kind mismatch for uid {}\", uid);\n                                prop_assert_eq!(owner_uid, &o.uid, \"owner_uid mismatch for uid {}\", uid);\n                                prop_assert_eq!(owner_name, &o.name, \"owner_name mismatch for uid {}\", uid);\n                            }\n                        }\n                    }\n                    RenderEvent::PodContainer { uid, .. } => {\n                        // PodContainer should only follow PodNew for a uid.\n                        if !first_seen.contains_key(uid) {\n                            prop_assert!(false, \"first event for uid {} was PodContainer, not PodNew\", uid);\n                        }\n                    }\n                    RenderEvent::PodDelete { uid } => {\n                        // PodDelete should only follow PodNew for a uid.\n                        if !first_seen.contains_key(uid) {\n                            prop_assert!(false, \"first event for uid {} was PodDelete, not PodNew\", uid);\n                        }\n                    }\n                    _ => {}\n                }\n            }\n        }\n    });\n}\n"
  },
  {
    "path": "crates/k8s-collector/tests/integration_test.rs",
    "content": "use std::path::PathBuf;\nuse std::time::Duration;\n\nuse anyhow::{bail, Context};\nuse k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec, ReplicaSet, ReplicaSetSpec};\nuse k8s_openapi::api::batch::v1::{CronJob, CronJobSpec, Job, JobSpec};\nuse k8s_openapi::api::core::v1::{Container, Namespace, Pod, PodSpec, PodStatus, PodTemplateSpec};\nuse k8s_openapi::apimachinery::pkg::apis::meta::v1::{LabelSelector, ObjectMeta, OwnerReference};\nuse kube::{\n    api::{Api, DeleteParams, ListParams, PostParams, ResourceExt},\n    Client,\n};\nuse serde_json::json;\nuse tokio::time::Instant;\n\nuse k8s_collector::collector::Collector;\nuse k8s_collector::config::Config;\nuse k8s_collector::output::RenderEvent;\nuse k8s_collector::types::OwnerKind;\nuse log::info;\n\nfn init_test_logger() {\n    if std::env::var_os(\"RUST_BACKTRACE\").is_none() {\n        std::env::set_var(\"RUST_BACKTRACE\", \"1\");\n    }\n    if std::env::var_os(\"RUST_LIB_BACKTRACE\").is_none() {\n        std::env::set_var(\"RUST_LIB_BACKTRACE\", \"1\");\n    }\n    let _ = env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(\"info\"))\n        .is_test(true)\n        .try_init();\n}\n\nasync fn ensure_namespace(client: &Client, name: &str) -> anyhow::Result<()> {\n    let namespaces: Api<Namespace> = Api::all(client.clone());\n    match namespaces.get(name).await {\n        Ok(_) => Ok(()),\n        Err(kube::Error::Api(ae)) if ae.code == 404 => {\n            let ns: Namespace = serde_json::from_value(json!({\n                \"apiVersion\": \"v1\",\n                \"kind\": \"Namespace\",\n                \"metadata\": { \"name\": name },\n            }))?;\n            namespaces.create(&PostParams::default(), &ns).await?;\n            Ok(())\n        }\n        Err(e) => Err(e.into()),\n    }\n}\n\nasync fn delete_if_exists<T>(api: &Api<T>, name: &str) -> anyhow::Result<()>\nwhere\n    T: k8s_openapi::Metadata<Ty = ObjectMeta>\n        + Clone\n        + serde::de::DeserializeOwned\n        + serde::Serialize\n        + std::fmt::Debug,\n{\n    let dp = DeleteParams {\n        grace_period_seconds: Some(0),\n        ..DeleteParams::default()\n    };\n    match api.delete(name, &dp).await {\n        Ok(_o) => Ok(()),\n        Err(kube::Error::Api(ae)) if ae.code == 404 => Ok(()),\n        Err(e) => Err(e.into()),\n    }\n}\n\nasync fn wait_for_pod_running_by_label(\n    pods: &Api<Pod>,\n    label_selector: &str,\n    timeout: Duration,\n) -> anyhow::Result<Pod> {\n    let deadline = Instant::now() + timeout;\n    loop {\n        let lp = ListParams::default().labels(label_selector);\n        let pod_list = pods.list(&lp).await?;\n        for pod in pod_list {\n            if let Some(status) = &pod.status {\n                if status.phase.as_deref() == Some(\"Running\") && containers_ready(status) {\n                    return Ok(pod);\n                }\n            }\n        }\n\n        if Instant::now() >= deadline {\n            bail!(\n                \"timed out waiting for a Running pod with label selector {}\",\n                label_selector\n            );\n        }\n        tokio::time::sleep(Duration::from_secs(1)).await;\n    }\n}\n\nfn containers_ready(status: &PodStatus) -> bool {\n    status\n        .container_statuses\n        .as_ref()\n        .map(|statuses| statuses.iter().all(|cs| cs.ready))\n        .unwrap_or(false)\n}\n\nasync fn create_sleep_deployment(\n    client: &Client,\n    namespace: &str,\n    name: &str,\n) -> anyhow::Result<Deployment> {\n    let deployments: Api<Deployment> = Api::namespaced(client.clone(), namespace);\n\n    let labels: std::collections::BTreeMap<String, String> =\n        [(\"k8s-collector-e2e\".to_string(), name.to_string())]\n            .into_iter()\n            .collect();\n    let deployment = Deployment {\n        metadata: ObjectMeta {\n            name: Some(name.to_string()),\n            namespace: Some(namespace.to_string()),\n            ..ObjectMeta::default()\n        },\n        spec: Some(DeploymentSpec {\n            replicas: Some(1),\n            selector: LabelSelector {\n                match_labels: Some(labels.clone()),\n                ..LabelSelector::default()\n            },\n            template: PodTemplateSpec {\n                metadata: Some(ObjectMeta {\n                    labels: Some(labels),\n                    ..ObjectMeta::default()\n                }),\n                spec: Some(PodSpec {\n                    containers: vec![Container {\n                        name: \"main\".into(),\n                        image: Some(\"busybox\".into()),\n                        command: Some(vec![\"/bin/sh\".into(), \"-c\".into(), \"sleep 3600\".into()]),\n                        image_pull_policy: Some(\"IfNotPresent\".into()),\n                        ..Container::default()\n                    }],\n                    ..PodSpec::default()\n                }),\n            },\n            ..DeploymentSpec::default()\n        }),\n        status: None,\n    };\n\n    Ok(deployments\n        .create(&PostParams::default(), &deployment)\n        .await?)\n}\n\nasync fn create_replicaset(\n    client: &Client,\n    namespace: &str,\n    name: &str,\n) -> anyhow::Result<ReplicaSet> {\n    let replicasets: Api<ReplicaSet> = Api::namespaced(client.clone(), namespace);\n\n    let labels: std::collections::BTreeMap<String, String> =\n        [(\"k8s-collector-e2e\".to_string(), name.to_string())]\n            .into_iter()\n            .collect();\n    let rs = ReplicaSet {\n        metadata: ObjectMeta {\n            name: Some(name.to_string()),\n            namespace: Some(namespace.to_string()),\n            ..ObjectMeta::default()\n        },\n        spec: Some(ReplicaSetSpec {\n            replicas: Some(1),\n            selector: LabelSelector {\n                match_labels: Some(labels.clone()),\n                ..LabelSelector::default()\n            },\n            template: Some(PodTemplateSpec {\n                metadata: Some(ObjectMeta {\n                    labels: Some(labels),\n                    ..ObjectMeta::default()\n                }),\n                spec: Some(PodSpec {\n                    containers: vec![Container {\n                        name: \"main\".into(),\n                        image: Some(\"busybox\".into()),\n                        command: Some(vec![\"/bin/sh\".into(), \"-c\".into(), \"sleep 3600\".into()]),\n                        image_pull_policy: Some(\"IfNotPresent\".into()),\n                        ..Container::default()\n                    }],\n                    ..PodSpec::default()\n                }),\n            }),\n            ..ReplicaSetSpec::default()\n        }),\n        status: None,\n    };\n\n    Ok(replicasets.create(&PostParams::default(), &rs).await?)\n}\n\nasync fn create_cronjob(client: &Client, namespace: &str, name: &str) -> anyhow::Result<CronJob> {\n    let cronjobs: Api<CronJob> = Api::namespaced(client.clone(), namespace);\n    let labels: std::collections::BTreeMap<String, String> =\n        [(\"k8s-collector-e2e\".to_string(), name.to_string())]\n            .into_iter()\n            .collect();\n\n    let cj = CronJob {\n        metadata: ObjectMeta {\n            name: Some(name.to_string()),\n            namespace: Some(namespace.to_string()),\n            ..ObjectMeta::default()\n        },\n        spec: Some(CronJobSpec {\n            schedule: \"* * * * *\".into(),\n            job_template: k8s_openapi::api::batch::v1::JobTemplateSpec {\n                metadata: Some(ObjectMeta {\n                    labels: Some(labels.clone()),\n                    ..ObjectMeta::default()\n                }),\n                spec: Some(JobSpec {\n                    template: PodTemplateSpec {\n                        metadata: Some(ObjectMeta {\n                            labels: Some(labels),\n                            ..ObjectMeta::default()\n                        }),\n                        spec: Some(PodSpec {\n                            containers: vec![Container {\n                                name: \"main\".into(),\n                                image: Some(\"busybox\".into()),\n                                command: Some(vec![\n                                    \"/bin/sh\".into(),\n                                    \"-c\".into(),\n                                    \"sleep 3600\".into(),\n                                ]),\n                                image_pull_policy: Some(\"IfNotPresent\".into()),\n                                ..Container::default()\n                            }],\n                            restart_policy: Some(\"Never\".into()),\n                            ..PodSpec::default()\n                        }),\n                    },\n                    ..JobSpec::default()\n                }),\n            },\n            ..CronJobSpec::default()\n        }),\n        status: None,\n    };\n\n    Ok(cronjobs.create(&PostParams::default(), &cj).await?)\n}\n\nasync fn create_job_with_cron_owner(\n    client: &Client,\n    namespace: &str,\n    cron: &CronJob,\n    job_name: &str,\n) -> anyhow::Result<Job> {\n    let jobs: Api<Job> = Api::namespaced(client.clone(), namespace);\n    let cron_meta = cron.metadata.clone();\n    let cron_name = cron_meta\n        .name\n        .clone()\n        .context(\"CronJob missing metadata.name\")?;\n    let cron_uid = cron_meta\n        .uid\n        .clone()\n        .context(\"CronJob missing metadata.uid\")?;\n\n    let labels: std::collections::BTreeMap<String, String> =\n        [(\"k8s-collector-e2e\".to_string(), job_name.to_string())]\n            .into_iter()\n            .collect();\n\n    let job = Job {\n        metadata: ObjectMeta {\n            name: Some(job_name.to_string()),\n            namespace: Some(namespace.to_string()),\n            owner_references: Some(vec![OwnerReference {\n                api_version: \"batch/v1\".into(),\n                kind: \"CronJob\".into(),\n                name: cron_name,\n                uid: cron_uid,\n                controller: Some(true),\n                ..OwnerReference::default()\n            }]),\n            ..ObjectMeta::default()\n        },\n        spec: Some(JobSpec {\n            template: PodTemplateSpec {\n                metadata: Some(ObjectMeta {\n                    labels: Some(labels),\n                    ..ObjectMeta::default()\n                }),\n                spec: Some(PodSpec {\n                    containers: vec![Container {\n                        name: \"main\".into(),\n                        image: Some(\"busybox\".into()),\n                        command: Some(vec![\"/bin/sh\".into(), \"-c\".into(), \"sleep 3600\".into()]),\n                        image_pull_policy: Some(\"IfNotPresent\".into()),\n                        ..Container::default()\n                    }],\n                    restart_policy: Some(\"Never\".into()),\n                    ..PodSpec::default()\n                }),\n            },\n            ..JobSpec::default()\n        }),\n        status: None,\n    };\n\n    Ok(jobs.create(&PostParams::default(), &job).await?)\n}\n\nstruct ObservedPod {\n    uid: String,\n    name: String,\n    expect_owner_kind: OwnerKind,\n    expect_owner_name: String,\n    saw_pod_new: bool,\n    saw_container: bool,\n}\n\nfn update_observed_from_event(obs: &mut ObservedPod, ev: &RenderEvent) {\n    match ev {\n        RenderEvent::PodNew {\n            uid,\n            owner_kind,\n            owner_name,\n            ..\n        } if uid == &obs.uid => {\n            obs.saw_pod_new = true;\n            assert_eq!(\n                *owner_kind, obs.expect_owner_kind,\n                \"unexpected owner kind for pod {}\",\n                uid\n            );\n            assert_eq!(\n                owner_name, &obs.expect_owner_name,\n                \"unexpected owner name for pod {}\",\n                uid\n            );\n            info!(\n                \"k8s-collector e2e: verified owner for pod uid={} name={} -> kind={:?} name={}\",\n                uid, obs.name, owner_kind, owner_name\n            );\n        }\n        RenderEvent::PodContainer { uid, .. } if uid == &obs.uid => {\n            obs.saw_container = true;\n        }\n        _ => {}\n    }\n}\n\nfn observed_done(obs: &ObservedPod) -> bool {\n    obs.saw_pod_new && obs.saw_container\n}\n\nasync fn pump_until<F>(collector: &mut Collector, timeout: Duration, mut f: F) -> anyhow::Result<()>\nwhere\n    F: FnMut(&RenderEvent) -> bool,\n{\n    let deadline = Instant::now() + timeout;\n    loop {\n        if Instant::now() >= deadline {\n            info!(\n                \"k8s-collector e2e: pump_until outer deadline {:?} elapsed; collector snapshot:\\n{}\",\n                timeout,\n                collector.debug_snapshot()\n            );\n            bail!(\"timed out waiting for expected collector events\");\n        }\n\n        let remaining = deadline\n            .checked_duration_since(Instant::now())\n            .unwrap_or_else(|| Duration::from_secs(1));\n        let batch = match tokio::time::timeout(remaining, collector.next_messages()).await {\n            Ok(Ok(batch)) => batch,\n            Ok(Err(e)) => {\n                info!(\n                    \"k8s-collector e2e: pump_until collector.next_messages() returned error: {:?}; snapshot:\\n{}\",\n                    e,\n                    collector.debug_snapshot()\n                );\n                return Err(e.into());\n            }\n            Err(elapsed) => {\n                info!(\n                    \"k8s-collector e2e: pump_until tokio timeout after {:?} while waiting for collector.next_messages(); snapshot:\\n{}\",\n                    remaining,\n                    collector.debug_snapshot()\n                );\n                bail!(\n                    \"tokio timeout waiting for collector.next_messages(): {}\",\n                    elapsed\n                );\n            }\n        };\n        if batch.is_empty() {\n            continue;\n        }\n\n        for ev in &batch {\n            info!(\"k8s-collector e2e: pump_until observed event: {:?}\", ev);\n            if f(ev) {\n                return Ok(());\n            }\n        }\n    }\n}\n\n#[tokio::test]\n#[cfg(target_os = \"linux\")]\n#[ignore]\nasync fn test_k8s_collector_end_to_end_e2e() -> anyhow::Result<()> {\n    init_test_logger();\n\n    // Use a per-run suffix to ensure that resource names are unique across test\n    // invocations, so we never accidentally match pods from a previous run.\n    let run_suffix = {\n        use std::time::{SystemTime, UNIX_EPOCH};\n        let nanos = SystemTime::now()\n            .duration_since(UNIX_EPOCH)\n            .unwrap_or_default()\n            .as_nanos();\n        let hex = format!(\"{:x}\", nanos);\n        // Take the last 6 hex chars to keep names short but unique-enough.\n        hex.chars()\n            .rev()\n            .take(6)\n            .collect::<String>()\n            .chars()\n            .rev()\n            .collect::<String>()\n    };\n    info!(\"k8s-collector e2e: using run suffix {}\", run_suffix);\n\n    // Skip the test if no kubeconfig is available; this environment\n    // does not guarantee a Kubernetes cluster.\n    let kubeconfig_env = std::env::var_os(\"KUBECONFIG\");\n    let default_kubeconfig_missing = std::env::var_os(\"HOME\")\n        .map(|home| {\n            let mut p = PathBuf::from(home);\n            p.push(\".kube/config\");\n            !p.exists()\n        })\n        .unwrap_or(true);\n    if kubeconfig_env.is_none() && default_kubeconfig_missing {\n        eprintln!(\n            \"KUBECONFIG not set and default kubeconfig missing; skipping k8s-collector e2e test\"\n        );\n        return Ok(());\n    }\n\n    let client = Client::try_default().await?;\n    let namespace = \"k8s-collector-e2e\";\n    info!(\n        \"k8s-collector e2e: starting test in namespace {}\",\n        namespace\n    );\n\n    ensure_namespace(&client, namespace).await?;\n    info!(\"k8s-collector e2e: namespace {} is ready\", namespace);\n\n    let pods: Api<Pod> = Api::namespaced(client.clone(), namespace);\n    let deployments: Api<Deployment> = Api::namespaced(client.clone(), namespace);\n    let jobs: Api<Job> = Api::namespaced(client.clone(), namespace);\n    let cronjobs: Api<CronJob> = Api::namespaced(client.clone(), namespace);\n    let replicasets: Api<ReplicaSet> = Api::namespaced(client.clone(), namespace);\n\n    // Per-run resource names with a unique suffix to avoid collisions\n    // between test invocations.\n    let pre_deploy_name = format!(\"kc-e2e-pre-deploy-{}\", run_suffix);\n    let pre_cron_name = format!(\"kc-e2e-pre-cron-{}\", run_suffix);\n    let pre_job_name = format!(\"kc-e2e-pre-job-{}\", run_suffix);\n    let pre_rs_name = format!(\"kc-e2e-pre-rs-{}\", run_suffix);\n    let new_deploy_name = format!(\"kc-e2e-new-deploy-{}\", run_suffix);\n    let new_cron_name = format!(\"kc-e2e-new-cron-{}\", run_suffix);\n    let new_job_name = format!(\"kc-e2e-new-job-{}\", run_suffix);\n\n    // Best-effort cleanup from previous runs with the same naming pattern.\n    let _ = delete_if_exists(&deployments, &pre_deploy_name).await;\n    let _ = delete_if_exists(&deployments, &new_deploy_name).await;\n    let _ = delete_if_exists(&cronjobs, &pre_cron_name).await;\n    let _ = delete_if_exists(&cronjobs, &new_cron_name).await;\n    let _ = delete_if_exists(&jobs, &pre_job_name).await;\n    let _ = delete_if_exists(&jobs, &new_job_name).await;\n    let _ = delete_if_exists(&replicasets, &pre_rs_name).await;\n    info!(\"k8s-collector e2e: completed pre-test cleanup of prior resources\");\n\n    // Pre-existing Deployment and CronJob+Job before the collector starts.\n    let pre_deploy = create_sleep_deployment(&client, namespace, &pre_deploy_name).await?;\n    let pre_cron = create_cronjob(&client, namespace, &pre_cron_name).await?;\n    let _pre_job = create_job_with_cron_owner(&client, namespace, &pre_cron, &pre_job_name).await?;\n    let pre_rs = create_replicaset(&client, namespace, &pre_rs_name).await?;\n\n    // Wait for their pods to be running and capture UIDs.\n    let pre_deploy_pod = wait_for_pod_running_by_label(\n        &pods,\n        &format!(\"k8s-collector-e2e={}\", pre_deploy_name),\n        Duration::from_secs(120),\n    )\n    .await?;\n    let pre_deploy_uid = pre_deploy_pod\n        .metadata\n        .uid\n        .clone()\n        .context(\"pre-deploy pod missing uid\")?;\n    let pre_deploy_pod_name = pre_deploy_pod.name_any();\n\n    let pre_cron_pod = wait_for_pod_running_by_label(\n        &pods,\n        &format!(\"k8s-collector-e2e={}\", pre_job_name),\n        Duration::from_secs(120),\n    )\n    .await?;\n    let pre_cron_pod_uid = pre_cron_pod\n        .metadata\n        .uid\n        .clone()\n        .context(\"pre-cron pod missing uid\")?;\n    let pre_cron_pod_name = pre_cron_pod.name_any();\n    let pre_rs_pod = wait_for_pod_running_by_label(\n        &pods,\n        &format!(\"k8s-collector-e2e={}\", pre_rs_name),\n        Duration::from_secs(120),\n    )\n    .await?;\n    let pre_rs_uid = pre_rs_pod\n        .metadata\n        .uid\n        .clone()\n        .context(\"pre-rs pod missing uid\")?;\n    let pre_rs_pod_name = pre_rs_pod.name_any();\n    info!(\n        \"k8s-collector e2e: pre-existing pods ready: deploy name={} uid={}, cronpod name={} uid={}, rs name={} uid={}\",\n        pre_deploy_pod_name, pre_deploy_uid, pre_cron_pod_name, pre_cron_pod_uid, pre_rs_pod_name, pre_rs_uid\n    );\n\n    let cfg = Config::default();\n    // delete_ttl/delete_capacity already have defaults; we use them as-is.\n    let mut collector = Collector::with_client(cfg, client.clone());\n    info!(\"k8s-collector e2e: collector pipeline initialized\");\n\n    let mut pre_deploy_obs = ObservedPod {\n        uid: pre_deploy_uid.clone(),\n        name: pre_deploy_pod_name.clone(),\n        expect_owner_kind: OwnerKind::Deployment,\n        expect_owner_name: pre_deploy.name_any(),\n        saw_pod_new: false,\n        saw_container: false,\n    };\n    let mut pre_cron_obs = ObservedPod {\n        uid: pre_cron_pod_uid.clone(),\n        name: pre_cron_pod_name.clone(),\n        expect_owner_kind: OwnerKind::CronJob,\n        expect_owner_name: pre_cron.name_any(),\n        saw_pod_new: false,\n        saw_container: false,\n    };\n    let mut pre_rs_obs = ObservedPod {\n        uid: pre_rs_uid.clone(),\n        name: pre_rs_pod_name.clone(),\n        expect_owner_kind: OwnerKind::ReplicaSet,\n        expect_owner_name: pre_rs.name_any(),\n        saw_pod_new: false,\n        saw_container: false,\n    };\n\n    let mut last_epoch = 0u64;\n\n    // Pump until we have seen a PodResync and both pre-existing pods fully emitted.\n    pump_until(\n        &mut collector,\n        Duration::from_secs(60),\n        |ev: &RenderEvent| {\n            if let RenderEvent::PodResync { epoch } = ev {\n                last_epoch = *epoch;\n            }\n            update_observed_from_event(&mut pre_deploy_obs, ev);\n            update_observed_from_event(&mut pre_cron_obs, ev);\n            update_observed_from_event(&mut pre_rs_obs, ev);\n            last_epoch > 0\n                && observed_done(&pre_deploy_obs)\n                && observed_done(&pre_cron_obs)\n                && observed_done(&pre_rs_obs)\n        },\n    )\n    .await?;\n\n    assert!(last_epoch > 0, \"expected at least one resync epoch\");\n    info!(\n        \"k8s-collector e2e: observed initial resync epoch {} and full emission for pre-existing pods\",\n        last_epoch\n    );\n\n    // Create new Deployment and CronJob+Job while collector is running.\n    info!(\"k8s-collector e2e: creating new Deployment and CronJob/Job resources\");\n    let new_deploy = create_sleep_deployment(&client, namespace, &new_deploy_name).await?;\n    let new_cron = create_cronjob(&client, namespace, &new_cron_name).await?;\n    let _new_job = create_job_with_cron_owner(&client, namespace, &new_cron, &new_job_name).await?;\n\n    let new_deploy_pod = wait_for_pod_running_by_label(\n        &pods,\n        &format!(\"k8s-collector-e2e={}\", new_deploy_name),\n        Duration::from_secs(120),\n    )\n    .await?;\n    let new_deploy_uid = new_deploy_pod\n        .metadata\n        .uid\n        .clone()\n        .context(\"new-deploy pod missing uid\")?;\n    let new_deploy_pod_name = new_deploy_pod.name_any();\n\n    let new_cron_pod = wait_for_pod_running_by_label(\n        &pods,\n        &format!(\"k8s-collector-e2e={}\", new_job_name),\n        Duration::from_secs(120),\n    )\n    .await?;\n    let new_cron_pod_uid = new_cron_pod\n        .metadata\n        .uid\n        .clone()\n        .context(\"new-cron pod missing uid\")?;\n    let new_cron_pod_name = new_cron_pod.name_any();\n    info!(\n        \"k8s-collector e2e: new pods ready: deploy name={} uid={}, cronpod name={} uid={}\",\n        new_deploy_pod_name, new_deploy_uid, new_cron_pod_name, new_cron_pod_uid\n    );\n\n    let mut new_deploy_obs = ObservedPod {\n        uid: new_deploy_uid.clone(),\n        name: new_deploy_pod_name.clone(),\n        expect_owner_kind: OwnerKind::Deployment,\n        expect_owner_name: new_deploy.name_any(),\n        saw_pod_new: false,\n        saw_container: false,\n    };\n    let mut new_cron_obs = ObservedPod {\n        uid: new_cron_pod_uid.clone(),\n        name: new_cron_pod_name.clone(),\n        expect_owner_kind: OwnerKind::CronJob,\n        expect_owner_name: new_cron.name_any(),\n        saw_pod_new: false,\n        saw_container: false,\n    };\n\n    pump_until(\n        &mut collector,\n        Duration::from_secs(60),\n        |ev: &RenderEvent| {\n            update_observed_from_event(&mut new_deploy_obs, ev);\n            update_observed_from_event(&mut new_cron_obs, ev);\n            observed_done(&new_deploy_obs) && observed_done(&new_cron_obs)\n        },\n    )\n    .await?;\n    info!(\"k8s-collector e2e: observed PodNew+PodContainer for new deploy and cronjob pods\");\n\n    // Force an explicit resync epoch and verify it includes all pods.\n    info!(\n        \"k8s-collector e2e: forcing explicit resync epoch after last_epoch={}\",\n        last_epoch\n    );\n    let epoch_events = collector.start_new_epoch();\n    let mut resync_epoch = None;\n    let mut seen_uids = Vec::new();\n    for ev in &epoch_events {\n        match ev {\n            RenderEvent::PodResync { epoch } => {\n                resync_epoch = Some(*epoch);\n            }\n            RenderEvent::PodNew { uid, .. } => {\n                seen_uids.push(uid.clone());\n            }\n            _ => {}\n        }\n    }\n\n    let resync_epoch = resync_epoch.context(\"expected PodResync event from start_new_epoch\")?;\n    assert!(\n        resync_epoch > last_epoch,\n        \"expected resync epoch to be greater than previous epoch\"\n    );\n    info!(\n        \"k8s-collector e2e: resync epoch {} emitted PodNew snapshot for {} pods\",\n        resync_epoch,\n        seen_uids.len()\n    );\n\n    // For each observed pod, if it still exists in the cluster under the same\n    // name+UID, require that it appears in the resync snapshot. Pods that have\n    // been deleted or replaced are skipped. When a UID is missing, emit\n    // detailed debugging about the snapshot contents.\n    for obs in [\n        &pre_deploy_obs,\n        &pre_cron_obs,\n        &pre_rs_obs,\n        &new_deploy_obs,\n        &new_cron_obs,\n    ] {\n        match pods.get(&obs.name).await {\n            Ok(pod) => {\n                let current_uid = pod.metadata.uid.as_deref().unwrap_or_default();\n                if current_uid == obs.uid {\n                    if !seen_uids.contains(&obs.uid) {\n                        info!(\n                            \"k8s-collector e2e: missing expected uid {} in resync snapshot; dumping PodNew events\",\n                            obs.uid\n                        );\n                        for ev in &epoch_events {\n                            if let RenderEvent::PodNew {\n                                uid, pod_name, ns, ..\n                            } = ev\n                            {\n                                info!(\n                                    \"k8s-collector e2e: snapshot PodNew pod_name={} ns={} uid={}\",\n                                    pod_name, ns, uid\n                                );\n                            }\n                        }\n                        info!(\"k8s-collector e2e: snapshot seen_uids={:?}\", seen_uids);\n                        info!(\n                            \"k8s-collector e2e: collector matcher state snapshot:\\n{}\",\n                            collector.debug_snapshot()\n                        );\n                        panic!(\"expected uid {} in resync snapshot\", obs.uid);\n                    }\n                    info!(\n                        \"k8s-collector e2e: snapshot contains live pod name={} uid={}\",\n                        obs.name, obs.uid\n                    );\n                } else {\n                    info!(\n                        \"k8s-collector e2e: pod name={} originally uid={} now uid={}; treating as replaced\",\n                        obs.name, obs.uid, current_uid\n                    );\n                }\n            }\n            Err(kube::Error::Api(ae)) if ae.code == 404 => {\n                info!(\n                    \"k8s-collector e2e: pod name={} uid={} no longer exists; skipping snapshot assertion\",\n                    obs.name, obs.uid\n                );\n            }\n            Err(e) => return Err(e.into()),\n        }\n    }\n\n    // Best-effort cleanup.\n    let _ = delete_if_exists(&deployments, &pre_deploy_name).await;\n    let _ = delete_if_exists(&deployments, &new_deploy_name).await;\n    let _ = delete_if_exists(&cronjobs, &pre_cron_name).await;\n    let _ = delete_if_exists(&cronjobs, &new_cron_name).await;\n    let _ = delete_if_exists(&jobs, &pre_job_name).await;\n    let _ = delete_if_exists(&jobs, &new_job_name).await;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/k8s-collector-bin/Cargo.toml",
    "content": "[package]\nname = \"k8s-collector-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[[bin]]\nname = \"k8s-collector\"\npath = \"src/main.rs\"\n\n[dependencies]\nclap = { version = \"4\", features = [\"derive\"] }\nk8s-collector = { path = \"../k8s-collector\" }\nenv_logger = \"0.11\"\nk8s-openapi = { version = \"0.26\", features = [\"v1_30\"] }\n"
  },
  {
    "path": "crates/k8s-collector-bin/src/main.rs",
    "content": "//! CLI entrypoint for the Kubernetes metadata collector.\n//!\n//! Parses configuration from flags and environment variables, then executes\n//! the collector pipeline.\n\nuse clap::Parser;\nuse k8s_collector::config::Config;\n\n#[derive(Parser, Debug)]\n#[command(\n    name = \"k8s-collector\",\n    about = \"OpenTelemetry eBPF Kubernetes Metadata Collector (Rust)\"\n)]\nstruct Cli {\n    /// Print configuration values and exit\n    #[arg(long = \"print-config\")]\n    print_config: bool,\n\n    /// Deletion tombstone TTL in seconds\n    #[arg(long = \"delete-ttl-secs\", default_value_t = 60)]\n    delete_ttl_secs: u64,\n    /// Deletion tombstone capacity\n    #[arg(long = \"delete-capacity\", default_value_t = 10_000)]\n    delete_capacity: usize,\n\n    /// Reducer intake host\n    #[arg(long = \"intake-host\")]\n    intake_host: Option<String>,\n    /// Reducer intake port\n    #[arg(long = \"intake-port\")]\n    intake_port: Option<u16>,\n}\n\n/// Build the final [`Config`] from CLI flags and environment variables.\nfn build_config(cli: &Cli) -> Result<Config, String> {\n    // Allow overriding the TTL via env if present\n    let ttl_secs: u64 = std::env::var(\"K8S_COLLECTOR_DELETE_TTL_SECS\")\n        .ok()\n        .and_then(|v| v.parse().ok())\n        .unwrap_or(cli.delete_ttl_secs);\n    let ttl = std::time::Duration::from_secs(ttl_secs);\n    let cap: usize = std::env::var(\"K8S_COLLECTOR_DELETE_CAPACITY\")\n        .ok()\n        .and_then(|v| v.parse().ok())\n        .unwrap_or(cli.delete_capacity);\n\n    let mut cfg = Config::default();\n    // Intake host/port via env or args\n    if let Some(h) = std::env::var(\"EBPF_NET_INTAKE_HOST\")\n        .ok()\n        .or(cli.intake_host.clone())\n    {\n        cfg.intake_host = h;\n    }\n    if let Some(p) = std::env::var(\"EBPF_NET_INTAKE_PORT\")\n        .ok()\n        .and_then(|v| v.parse().ok())\n        .or(cli.intake_port)\n    {\n        cfg.intake_port = p;\n    }\n    cfg.delete_ttl = ttl;\n    cfg.delete_capacity = cap;\n    Ok(cfg)\n}\n\n/// Print the effective configuration in a human-readable form.\nfn print_config(cfg: &Config) {\n    println!(\"intake_host: {}\", cfg.intake_host);\n    println!(\"intake_port: {}\", cfg.intake_port);\n    println!(\"delete_ttl_secs: {}\", cfg.delete_ttl.as_secs());\n    println!(\"delete_capacity: {}\", cfg.delete_capacity);\n}\n\nfn main() {\n    // Initialize logging for the collector binary.\n    let _ = env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(\"info\"))\n        .try_init();\n\n    let cli = Cli::parse();\n    let cfg = match build_config(&cli) {\n        Ok(c) => c,\n        Err(e) => {\n            eprintln!(\"{}\", e);\n            std::process::exit(2);\n        }\n    };\n\n    if cli.print_config {\n        print_config(&cfg);\n        return;\n    }\n\n    // In this minimal integration, run validates config and returns.\n    if let Err(e) = k8s_collector::run_with_config(cfg) {\n        eprintln!(\"collector error: {}\", e);\n        std::process::exit(1);\n    }\n}\n"
  },
  {
    "path": "crates/k8s-relay-bin/Cargo.toml",
    "content": "[package]\nname = \"k8s-relay-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nk8s-relay-sys = { path = \"../k8s-relay-sys\" }\n\n[[bin]]\nname = \"k8s-relay\"\npath = \"src/main.rs\"\n\n"
  },
  {
    "path": "crates/k8s-relay-bin/src/main.rs",
    "content": "fn main() {\n    let code = k8s_relay_sys::run_with_args(std::env::args_os());\n    std::process::exit(code);\n}\n"
  },
  {
    "path": "crates/k8s-relay-sys/Cargo.toml",
    "content": "[package]\nname = \"k8s-relay-sys\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"k8s_relay_sys\"\npath = \"src/lib.rs\"\n\n[dependencies]\nencoder_ebpf_net_ingest = { path = \"../render/ebpf_net/ingest\" }\n\n"
  },
  {
    "path": "crates/k8s-relay-sys/build.rs",
    "content": "include!(\"../build/otn_link_build.rs\");\n\nfn main() {\n    run();\n}\n"
  },
  {
    "path": "crates/k8s-relay-sys/src/lib.rs",
    "content": "use std::os::raw::{c_char, c_int};\n\n// Ensure encoder crates are linked so C++ static libs can resolve their\n// extern \"C\" symbols via our dependency graph.\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_ingest;\n\nextern \"C\" {\n    pub fn otn_k8s_relay_main(argc: c_int, argv: *const *const c_char) -> c_int;\n}\n\npub fn run_with_args<I, S>(args: I) -> i32\nwhere\n    I: IntoIterator<Item = S>,\n    S: Into<std::ffi::OsString>,\n{\n    use std::ffi::CString;\n\n    let mut c_strings: Vec<CString> = Vec::new();\n    for arg in args {\n        let s: std::ffi::OsString = arg.into();\n        let bytes = std::os::unix::ffi::OsStringExt::into_vec(s);\n        c_strings.push(CString::new(bytes).expect(\"argv contains NUL byte\"));\n    }\n\n    let ptrs: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();\n    unsafe { otn_k8s_relay_main(ptrs.len() as c_int, ptrs.as_ptr()) }\n}\n"
  },
  {
    "path": "crates/kernel-collector-bin/Cargo.toml",
    "content": "[package]\nname = \"kernel-collector-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nkernel-collector-sys = { path = \"../kernel-collector-sys\" }\n\n[[bin]]\nname = \"kernel-collector\"\npath = \"src/main.rs\"\n"
  },
  {
    "path": "crates/kernel-collector-bin/src/main.rs",
    "content": "fn main() {\n    // Capture the process args and forward to the C++ entrypoint.\n    let code = kernel_collector_sys::run_with_args(std::env::args_os());\n    // Propagate the exit code to the OS.\n    std::process::exit(code);\n}\n"
  },
  {
    "path": "crates/kernel-collector-sys/Cargo.toml",
    "content": "[package]\nname = \"kernel-collector-sys\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"kernel_collector_sys\"\npath = \"src/lib.rs\"\n\n[dependencies]\nencoder_ebpf_net_ingest = { path = \"../render/ebpf_net/ingest\" }\nencoder_ebpf_net_kernel_collector = { path = \"../render/ebpf_net/kernel_collector\" }\n"
  },
  {
    "path": "crates/kernel-collector-sys/build.rs",
    "content": "include!(\"../build/otn_link_build.rs\");\n\nfn main() {\n    run();\n}\n"
  },
  {
    "path": "crates/kernel-collector-sys/src/lib.rs",
    "content": "use std::os::raw::{c_char, c_int};\n\n// Ensure encoder crates are linked so C++ static libs can resolve their\n// extern \"C\" symbols via our dependency graph.\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_ingest;\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_kernel_collector;\n\nextern \"C\" {\n    pub fn otn_kernel_collector_main(argc: c_int, argv: *const *const c_char) -> c_int;\n}\n\npub fn run_with_args<I, S>(args: I) -> i32\nwhere\n    I: IntoIterator<Item = S>,\n    S: Into<std::ffi::OsString>,\n{\n    use std::ffi::CString;\n\n    let mut c_strings: Vec<CString> = Vec::new();\n    for arg in args {\n        let s: std::ffi::OsString = arg.into();\n        let bytes = std::os::unix::ffi::OsStringExt::into_vec(s);\n        c_strings.push(CString::new(bytes).expect(\"argv contains NUL byte\"));\n    }\n\n    let ptrs: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();\n    // Ensure null-terminated argv if desired by C libraries (not strictly required for main)\n    // ptrs.push(std::ptr::null());\n\n    unsafe { otn_kernel_collector_main(ptrs.len() as c_int, ptrs.as_ptr()) }\n}\n"
  },
  {
    "path": "crates/otlp_export/Cargo.toml",
    "content": "[package]\nname = \"otlp_export\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"otlp_export\"\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\ncxx = { version = \"1.0\" }\ntokio = { version = \"1\", features = [\"rt-multi-thread\"] }\nopentelemetry-proto = { version = \"0.31\", default-features = false, features = [\"gen-tonic\", \"metrics\"] }\ntonic = { version = \"0.14\", features = [\"transport\"] }\n\n[build-dependencies]\ncxx-build = { version = \"1.0\" }\n"
  },
  {
    "path": "crates/otlp_export/src/lib.rs",
    "content": "#![allow(clippy::new_without_default)]\n\n#[cxx::bridge]\npub mod ffi {\n    /// Lightweight key/value label representation.\n    #[derive(Debug, Clone)]\n    pub struct Label {\n        pub key: String,\n        pub value: String,\n    }\n\n    /// Statistics mirroring the counters tracked by the C++ OTLP client.\n    #[derive(Debug)]\n    pub struct PublisherStats {\n        pub bytes_sent: u64,\n        pub bytes_failed: u64,\n        pub data_points_sent: u64,\n        pub data_points_failed: u64,\n        pub requests_sent: u64,\n        pub requests_failed: u64,\n        pub unknown_response_tags: u64,\n    }\n\n    /// Metric kind to encode.\n    #[repr(i32)]\n    #[derive(Debug)]\n    pub enum MetricKind {\n        Sum = 0,\n        Gauge = 1,\n    }\n\n    extern \"Rust\" {\n        type Publisher;\n\n        /// Create a new OTLP publisher. `endpoint` is host:port or full endpoint string.\n        fn otlp_publisher_new(endpoint: &str) -> Box<Publisher>;\n\n        /// Publish a u64 metric point.\n        fn publish_metric_u64(\n            self: &mut Publisher,\n            name: &str,\n            unit: &str,\n            description: &str,\n            kind: MetricKind,\n            labels: &Vec<Label>,\n            timestamp_unix_nano: i64,\n            value: u64,\n        );\n\n        /// Publish a f64 metric point.\n        fn publish_metric_f64(\n            self: &mut Publisher,\n            name: &str,\n            unit: &str,\n            description: &str,\n            kind: MetricKind,\n            labels: &Vec<Label>,\n            timestamp_unix_nano: i64,\n            value: f64,\n        );\n\n        /// Publish a TCP flow log equivalent (kept minimal to match reducer fields).\n        fn publish_flow_log(\n            self: &mut Publisher,\n            labels: &Vec<Label>,\n            timestamp_unix_nano: i64,\n            tcp_sum_bytes: u64,\n            tcp_active_rtts: u32,\n            tcp_active_sockets: u32,\n            tcp_sum_srtt: u64,\n            tcp_sum_delivered: u64,\n            tcp_sum_retrans: u64,\n            tcp_syn_timeouts: u64,\n            tcp_new_sockets: u64,\n            tcp_resets: u64,\n        );\n\n        /// Flush any in-flight batches and process responses.\n        fn flush(self: &mut Publisher);\n\n        /// Shut down the publisher.\n        fn shutdown(self: &mut Publisher);\n\n        /// Read current counters/statistics.\n        fn stats(self: &Publisher) -> PublisherStats;\n    }\n}\n\nuse ffi::{Label, MetricKind, PublisherStats};\nuse opentelemetry_proto::tonic::collector::metrics::v1 as otlp_collector;\nuse opentelemetry_proto::tonic::collector::metrics::v1::metrics_service_client::MetricsServiceClient;\nuse opentelemetry_proto::tonic::common::v1 as otlp_common;\nuse opentelemetry_proto::tonic::metrics::v1 as otlp_metrics;\nuse opentelemetry_proto::tonic::resource::v1 as otlp_resource;\nuse tokio::runtime::Runtime;\nuse tonic::Request;\n\n/// Minimal placeholder publisher. This crate defines the FFI surface and basic accounting.\n/// Internals can be extended to perform real async OTLP export.\npub struct Publisher {\n    endpoint: String,\n    runtime: Runtime,\n    resource_attributes: Vec<(String, String)>,\n    scope_name: String,\n    // Buffered metrics and logs to export on flush.\n    buf: Vec<PendingMetric>,\n    // Simple counters following the C++ client semantics.\n    bytes_sent: u64,\n    bytes_failed: u64,\n    data_points_sent: u64,\n    data_points_failed: u64,\n    requests_sent: u64,\n    requests_failed: u64,\n    unknown_response_tags: u64,\n    // Simple buffered counters to roll up into a \"request\" on flush.\n    buffered_points: u64,\n    buffered_bytes: u64,\n}\n\nenum PointValue {\n    U64(u64),\n    F64(f64),\n}\n\nstruct PendingMetric {\n    name: String,\n    unit: String,\n    description: String,\n    kind: MetricKind,\n    labels: Vec<Label>,\n    timestamp_unix_nano: i64,\n    value: PointValue,\n}\n\npub fn otlp_publisher_new(endpoint: &str) -> Box<Publisher> {\n    // Resolve endpoint: accept full URL or host:port and default to http with no path for gRPC.\n    let endpoint_resolved = normalize_grpc_endpoint(endpoint);\n\n    // Static resource/scope metadata for now; can be extended via FFI later.\n    let resource_attributes = vec![\n        (\"service.name\".to_string(), \"reducer\".to_string()),\n        (\"telemetry.sdk.language\".to_string(), \"rust\".to_string()),\n    ];\n    let scope_name = \"reducer-ffi\".to_string();\n\n    // Create a small Tokio runtime for async export.\n    let runtime = Runtime::new().expect(\"failed to create Tokio runtime\");\n\n    Box::new(Publisher {\n        endpoint: endpoint_resolved,\n        runtime,\n        resource_attributes,\n        scope_name,\n        buf: Vec::new(),\n        bytes_sent: 0,\n        bytes_failed: 0,\n        data_points_sent: 0,\n        data_points_failed: 0,\n        requests_sent: 0,\n        requests_failed: 0,\n        unknown_response_tags: 0,\n        buffered_points: 0,\n        buffered_bytes: 0,\n    })\n}\n\nimpl Publisher {\n    pub fn publish_metric_u64(\n        &mut self,\n        name: &str,\n        unit: &str,\n        description: &str,\n        kind: MetricKind,\n        labels: &Vec<Label>,\n        timestamp_unix_nano: i64,\n        value: u64,\n    ) {\n        self.buf.push(PendingMetric {\n            name: name.to_string(),\n            unit: unit.to_string(),\n            description: description.to_string(),\n            kind,\n            labels: labels\n                .iter()\n                .map(|l| Label {\n                    key: l.key.clone(),\n                    value: l.value.clone(),\n                })\n                .collect(),\n            timestamp_unix_nano,\n            value: PointValue::U64(value),\n        });\n\n        let approx = approx_bytes_metric(name, labels);\n        self.buffered_points += 1;\n        self.buffered_bytes = self.buffered_bytes.saturating_add(approx);\n    }\n\n    pub fn publish_metric_f64(\n        &mut self,\n        name: &str,\n        unit: &str,\n        description: &str,\n        kind: MetricKind,\n        labels: &Vec<Label>,\n        timestamp_unix_nano: i64,\n        value: f64,\n    ) {\n        self.buf.push(PendingMetric {\n            name: name.to_string(),\n            unit: unit.to_string(),\n            description: description.to_string(),\n            kind,\n            labels: labels\n                .iter()\n                .map(|l| Label {\n                    key: l.key.clone(),\n                    value: l.value.clone(),\n                })\n                .collect(),\n            timestamp_unix_nano,\n            value: PointValue::F64(value),\n        });\n\n        let approx = approx_bytes_metric(name, labels);\n        self.buffered_points += 1;\n        self.buffered_bytes = self.buffered_bytes.saturating_add(approx);\n    }\n}\n\nimpl Publisher {\n    pub fn publish_flow_log(\n        &mut self,\n        _labels: &Vec<Label>,\n        _timestamp_unix_nano: i64,\n        _tcp_sum_bytes: u64,\n        _tcp_active_rtts: u32,\n        _tcp_active_sockets: u32,\n        _tcp_sum_srtt: u64,\n        _tcp_sum_delivered: u64,\n        _tcp_sum_retrans: u64,\n        _tcp_syn_timeouts: u64,\n        _tcp_new_sockets: u64,\n        _tcp_resets: u64,\n    ) {\n        // Future: publish logs via opentelemetry logs SDK. For now, account as one data point.\n        self.buffered_points += 1;\n        // Approximate fixed size for a log line.\n        self.buffered_bytes = self.buffered_bytes.saturating_add(128);\n    }\n\n    pub fn flush(&mut self) {\n        if self.buf.is_empty() {\n            return;\n        }\n\n        // Build OTLP ResourceMetrics -> ScopeMetrics -> Metric with one datapoint each.\n        let mut metrics: Vec<otlp_metrics::Metric> = Vec::with_capacity(self.buf.len());\n        for pm in &self.buf {\n            let attrs = labels_to_otlp_kv(&pm.labels);\n\n            // For sums, set start_time to slot start (30s window) to match reducer semantics.\n            let time_unix_nano = pm.timestamp_unix_nano as u64;\n            let start_time_unix_nano = pm.timestamp_unix_nano.saturating_sub(30_000_000_000) as u64;\n\n            let metric = match pm.kind {\n                MetricKind::Sum => {\n                    let ndp = match pm.value {\n                        PointValue::U64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano,\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsInt(\n                                saturating_u64_to_i64(v),\n                            )),\n                        },\n                        PointValue::F64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano,\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsDouble(v)),\n                        },\n                    };\n\n                    let sum = otlp_metrics::Sum {\n                        data_points: vec![ndp],\n                        aggregation_temporality: otlp_metrics::AggregationTemporality::Delta as i32,\n                        is_monotonic: true,\n                    };\n\n                    otlp_metrics::Metric {\n                        name: pm.name.clone(),\n                        description: pm.description.clone(),\n                        unit: pm.unit.clone(),\n                        metadata: vec![],\n                        data: Some(otlp_metrics::metric::Data::Sum(sum)),\n                    }\n                }\n                MetricKind::Gauge => {\n                    let ndp = match pm.value {\n                        PointValue::U64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano: 0, // ignored for Gauge\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsInt(\n                                saturating_u64_to_i64(v),\n                            )),\n                        },\n                        PointValue::F64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano: 0,\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsDouble(v)),\n                        },\n                    };\n                    let gauge = otlp_metrics::Gauge {\n                        data_points: vec![ndp],\n                    };\n                    otlp_metrics::Metric {\n                        name: pm.name.clone(),\n                        description: pm.description.clone(),\n                        unit: pm.unit.clone(),\n                        metadata: vec![],\n                        data: Some(otlp_metrics::metric::Data::Gauge(gauge)),\n                    }\n                }\n                _ => {\n                    let ndp = match pm.value {\n                        PointValue::U64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano: 0, // ignored for Gauge\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsInt(\n                                saturating_u64_to_i64(v),\n                            )),\n                        },\n                        PointValue::F64(v) => otlp_metrics::NumberDataPoint {\n                            attributes: attrs,\n                            start_time_unix_nano: 0,\n                            time_unix_nano,\n                            exemplars: vec![],\n                            flags: 0,\n                            value: Some(otlp_metrics::number_data_point::Value::AsDouble(v)),\n                        },\n                    };\n                    let gauge = otlp_metrics::Gauge {\n                        data_points: vec![ndp],\n                    };\n                    otlp_metrics::Metric {\n                        name: pm.name.clone(),\n                        description: pm.description.clone(),\n                        unit: pm.unit.clone(),\n                        metadata: vec![],\n                        data: Some(otlp_metrics::metric::Data::Gauge(gauge)),\n                    }\n                }\n            };\n\n            metrics.push(metric);\n        }\n\n        let scope_metrics = otlp_metrics::ScopeMetrics {\n            scope: Some(otlp_common::InstrumentationScope {\n                name: self.scope_name.clone(),\n                version: String::new(),\n                attributes: vec![],\n                dropped_attributes_count: 0,\n            }),\n            metrics,\n            schema_url: String::new(),\n        };\n\n        let resource = otlp_resource::Resource {\n            attributes: self\n                .resource_attributes\n                .iter()\n                .map(|(k, v)| otlp_common::KeyValue {\n                    key: k.clone(),\n                    value: Some(otlp_common::AnyValue {\n                        value: Some(otlp_common::any_value::Value::StringValue(v.clone())),\n                    }),\n                })\n                .collect(),\n            dropped_attributes_count: 0,\n            entity_refs: vec![],\n        };\n\n        let rm = otlp_metrics::ResourceMetrics {\n            resource: Some(resource),\n            scope_metrics: vec![scope_metrics],\n            schema_url: String::new(),\n        };\n\n        let req = otlp_collector::ExportMetricsServiceRequest {\n            resource_metrics: vec![rm],\n        };\n\n        // Send synchronously on our runtime.\n        let endpoint = self.endpoint.clone();\n        let buffered_points = self.buffered_points;\n        let buffered_bytes = self.buffered_bytes;\n        let export_res = self.runtime.block_on(async move {\n            match MetricsServiceClient::connect(endpoint).await {\n                Ok(mut client) => client.export(Request::new(req)).await,\n                Err(e) => Err(tonic::Status::unknown(format!(\"connect error: {}\", e))),\n            }\n        });\n\n        match export_res {\n            Ok(resp) => {\n                self.requests_sent = self.requests_sent.saturating_add(1);\n                let mut accepted = buffered_points;\n                if let Some(ps) = resp.into_inner().partial_success {\n                    if ps.rejected_data_points > 0 {\n                        let rej = ps.rejected_data_points as u64;\n                        let acc = accepted.saturating_sub(rej);\n                        self.data_points_failed = self.data_points_failed.saturating_add(rej);\n                        accepted = acc;\n                    }\n                }\n                self.data_points_sent = self.data_points_sent.saturating_add(accepted);\n                self.bytes_sent = self.bytes_sent.saturating_add(buffered_bytes);\n            }\n            Err(_e) => {\n                self.requests_failed = self.requests_failed.saturating_add(1);\n                self.data_points_failed = self.data_points_failed.saturating_add(buffered_points);\n                self.bytes_failed = self.bytes_failed.saturating_add(buffered_bytes);\n            }\n        }\n\n        self.buf.clear();\n        self.buffered_points = 0;\n        self.buffered_bytes = 0;\n    }\n\n    pub fn shutdown(&mut self) {\n        self.flush();\n        // Nothing else to shutdown for the exporter.\n    }\n\n    pub fn stats(&self) -> PublisherStats {\n        PublisherStats {\n            bytes_sent: self.bytes_sent,\n            bytes_failed: self.bytes_failed,\n            data_points_sent: self.data_points_sent,\n            data_points_failed: self.data_points_failed,\n            requests_sent: self.requests_sent,\n            requests_failed: self.requests_failed,\n            unknown_response_tags: self.unknown_response_tags,\n        }\n    }\n}\n\nfn approx_bytes_metric(name: &str, labels: &Vec<Label>) -> u64 {\n    let mut n = name.len() as u64;\n    for kv in labels {\n        n = n.saturating_add(kv.key.len() as u64 + kv.value.len() as u64 + 2);\n    }\n    n\n}\n\nfn labels_to_otlp_kv(labels: &Vec<Label>) -> Vec<otlp_common::KeyValue> {\n    labels\n        .iter()\n        .map(|l| otlp_common::KeyValue {\n            key: l.key.clone(),\n            value: Some(otlp_common::AnyValue {\n                value: Some(otlp_common::any_value::Value::StringValue(l.value.clone())),\n            }),\n        })\n        .collect()\n}\n\nfn normalize_grpc_endpoint(input: &str) -> String {\n    // Do not append a path. gRPC expects a host:port with scheme.\n    if input.starts_with(\"http://\") || input.starts_with(\"https://\") {\n        input.to_string()\n    } else if input.contains(\"://\") {\n        input.to_string()\n    } else {\n        format!(\"http://{}\", input)\n    }\n}\n\nfn saturating_u64_to_i64(v: u64) -> i64 {\n    if v > i64::MAX as u64 {\n        i64::MAX\n    } else {\n        v as i64\n    }\n}\n"
  },
  {
    "path": "crates/perfect_hash_map/Cargo.toml",
    "content": "[package]\nname = \"perfect_hash_map\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"Apache-2.0\"\ndescription = \"HashMap-like fixed-capacity map using a generator-provided perfect hash function\"\nrepository = \"https://github.com/open-telemetry/opentelemetry-ebpf\"\n\n[lib]\npath = \"src/lib.rs\"\n\n[features]\ndefault = []\n\n[dev-dependencies]\nproptest = \"1.4\"\n"
  },
  {
    "path": "crates/perfect_hash_map/src/lib.rs",
    "content": "//! PerfectHashMap: a HashMap-like container backed by a generator-provided\n//! perfect hash function.\n//!\n//! Storage is a fixed-size `Vec<Option<Entry<V>>>` with capacity equal to the\n//! provided hash size; there are no re-allocations. Keys are `u32` RPC IDs.\n//! The perfect hash maps a key to a unique slot in `[0, hash_size)` for the\n//! known key set. For safety with unknown keys, each slot stores the key\n//! alongside the value, and lookups verify the exact key match. Inserts that\n//! would overwrite a different key in the same slot return a `Collision` error\n//! (no probing).\n//!\n//! Differences from `std::collections::HashMap`:\n//! - Fixed capacity equal to the perfect-hash size (no growth or rehashing).\n//! - `insert` returns a `CollisionError` if a different key occupies the slot.\n//! - `try_insert` returns `Occupied` (same key) or `Collision` (different key).\n//! - `iter_mut` yields `(Key, &mut V)` (the key by value) to avoid aliasing.\n//!\n//! Example\n//! ```\n//! use perfect_hash_map::PerfectHashMap;\n//!\n//! // A toy \"perfect\" hash for this example: modulo 8\n//! let hash = |k: u32| k % 8;\n//! let mut m: PerfectHashMap<&'static str, _> = PerfectHashMap::new(8, hash);\n//!\n//! assert!(m.is_empty());\n//! assert_eq!(m.insert(1, \"one\"), Ok(None));\n//! assert_eq!(m.get(&1), Some(&\"one\"));\n//!\n//! // Replacing the same key returns the previous value\n//! assert_eq!(m.insert(1, \"uno\"), Ok(Some(\"one\")));\n//! assert_eq!(m.get(&1), Some(&\"uno\"));\n//!\n//! // Colliding with a different key (1 and 9 map to the same slot)\n//! let err = m.insert(9, \"nine\").unwrap_err();\n//! assert_eq!(err.existing_key, 1);\n//! ```\n\n#![deny(missing_docs)]\n\n/// Key type for the map (rpc_id).\npub type Key = u32;\n\n/// Type of a generator-provided perfect hash function.\npub type HashFn = fn(Key) -> u32;\n\n/// An entry stored in a map slot, keeping the key for validation.\n#[derive(Debug)]\npub struct Entry<V> {\n    /// The rpc_id key placed at this slot.\n    pub key: Key,\n    /// The value associated with the key.\n    pub value: V,\n}\n\n/// Error for `insert` when the slot is occupied by a different key.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct CollisionError {\n    /// Key already occupying the computed slot.\n    pub existing_key: Key,\n}\n\n/// A handle to an occupied entry, giving access to the existing value.\npub struct OccupiedEntry<'a, V> {\n    value: &'a mut V,\n}\n\nimpl<'a, V> OccupiedEntry<'a, V> {\n    /// Returns an immutable reference to the existing value.\n    pub fn get(&self) -> &V {\n        self.value\n    }\n\n    /// Returns a mutable reference to the existing value.\n    pub fn get_mut(&mut self) -> &mut V {\n        self.value\n    }\n\n    /// Replaces the existing value with `new_value`, returning the old value.\n    pub fn replace(&mut self, new_value: V) -> V {\n        core::mem::replace(self.value, new_value)\n    }\n}\n\n/// Error for `try_insert` with more detailed outcomes.\npub enum TryInsertError<'a, V> {\n    /// The same key is already present; use the entry to access/modify it.\n    Occupied {\n        /// The value that could not be inserted.\n        value: V,\n        /// A handle to the existing entry's value.\n        entry: OccupiedEntry<'a, V>,\n    },\n    /// A different key occupies the slot; insertion is rejected.\n    Collision {\n        /// The value that could not be inserted.\n        value: V,\n        /// The key already occupying the target slot.\n        existing_key: Key,\n    },\n}\n\nimpl<'a, V> core::fmt::Debug for TryInsertError<'a, V> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            Self::Occupied { .. } => f.debug_struct(\"Occupied\").finish_non_exhaustive(),\n            Self::Collision { existing_key, .. } => f\n                .debug_struct(\"Collision\")\n                .field(\"existing_key\", existing_key)\n                .finish(),\n        }\n    }\n}\n\n/// Fixed-capacity perfect-hash map.\n///\n/// - `V` is the stored value type.\n/// - `F` is a callable that implements `Fn(u32) -> u32` and maps keys to slots\n///   in `[0, capacity)`. Typically this is the generated `app_hash` function.\npub struct PerfectHashMap<V, F = HashFn>\nwhere\n    F: Fn(Key) -> u32 + Copy,\n{\n    hash: F,\n    slots: Vec<Option<Entry<V>>>,\n    len: usize,\n}\n\nimpl<V, F> PerfectHashMap<V, F>\nwhere\n    F: Fn(Key) -> u32 + Copy,\n{\n    /// Creates a new map with a fixed capacity equal to `hash_size` and using\n    /// the provided perfect hash function `hash`.\n    ///\n    /// Panics\n    /// - Methods that write (e.g., `insert`, `try_insert`) will panic if the\n    ///   hash function returns a slot `>= hash_size`.\n    pub fn new(hash_size: usize, hash: F) -> Self {\n        let mut slots = Vec::with_capacity(hash_size);\n        slots.resize_with(hash_size, || None);\n        Self {\n            hash,\n            slots,\n            len: 0,\n        }\n    }\n\n    /// Number of key-value pairs in the map.\n    pub fn len(&self) -> usize {\n        self.len\n    }\n\n    /// Returns true if the map contains no elements.\n    pub fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n\n    /// Fixed capacity (equals the provided hash size).\n    pub fn capacity(&self) -> usize {\n        self.slots.len()\n    }\n\n    /// Returns true if the given key is present.\n    pub fn contains_key(&self, key: &Key) -> bool {\n        self.get(key).is_some()\n    }\n\n    /// Returns a shared reference to the value corresponding to the key.\n    ///\n    /// If the hashed slot is out of range, this returns `None`.\n    pub fn get(&self, key: &Key) -> Option<&V> {\n        let i = (self.hash)(*key) as usize;\n        match self.slots.get(i) {\n            Some(Some(e)) if e.key == *key => Some(&e.value),\n            _ => None,\n        }\n    }\n\n    /// Returns a mutable reference to the value corresponding to the key.\n    ///\n    /// If the hashed slot is out of range, this returns `None`.\n    pub fn get_mut(&mut self, key: &Key) -> Option<&mut V> {\n        let i = (self.hash)(*key) as usize;\n        match self.slots.get_mut(i) {\n            Some(Some(e)) if e.key == *key => Some(&mut e.value),\n            _ => None,\n        }\n    }\n\n    /// Removes a key from the map, returning the value if present.\n    pub fn remove(&mut self, key: &Key) -> Option<V> {\n        let i = (self.hash)(*key) as usize;\n        if i >= self.slots.len() {\n            return None;\n        }\n        let match_key = matches!(self.slots[i].as_ref(), Some(e) if e.key == *key);\n        if match_key {\n            let entry = self.slots[i].take().expect(\"was Some above\");\n            self.len -= 1;\n            Some(entry.value)\n        } else {\n            None\n        }\n    }\n\n    /// Clears the map, removing all key-value pairs.\n    pub fn clear(&mut self) {\n        for slot in &mut self.slots {\n            if slot.is_some() {\n                *slot = None;\n            }\n        }\n        self.len = 0;\n    }\n\n    /// Inserts a key-value pair.\n    ///\n    /// Returns `Ok(None)` if the key was not present, or `Ok(Some(old))` if\n    /// the key was present and the value was replaced. Returns\n    /// `Err(CollisionError)` if the slot is occupied by a different key.\n    ///\n    /// Panics\n    /// - If the hash function returns a slot `>= capacity()`.\n    pub fn insert(&mut self, key: Key, value: V) -> Result<Option<V>, CollisionError> {\n        let i = (self.hash)(key) as usize;\n        match self.slots.get_mut(i) {\n            Some(slot) => match slot {\n                None => {\n                    *slot = Some(Entry { key, value });\n                    self.len += 1;\n                    Ok(None)\n                }\n                Some(e) if e.key == key => Ok(Some(core::mem::replace(&mut e.value, value))),\n                Some(e) => Err(CollisionError {\n                    existing_key: e.key,\n                }),\n            },\n            None => panic!(\"hash function returned out-of-range slot\"),\n        }\n    }\n\n    /// Attempts to insert a key-value pair.\n    ///\n    /// On success, returns a mutable reference to the inserted value.\n    /// On failure due to same-key occupancy, returns an `Occupied` error with\n    /// a handle to the existing value. On different-key collision, returns a\n    /// `Collision` error without modifying the map.\n    ///\n    /// Panics\n    /// - If the hash function returns a slot `>= capacity()`.\n    pub fn try_insert(&mut self, key: Key, value: V) -> Result<&mut V, TryInsertError<'_, V>> {\n        let i = (self.hash)(key) as usize;\n        match self.slots.get_mut(i) {\n            Some(slot) => {\n                if slot.is_none() {\n                    *slot = Some(Entry { key, value });\n                    self.len += 1;\n                    let e = slot.as_mut().unwrap();\n                    return Ok(&mut e.value);\n                }\n                let e = slot.as_mut().unwrap();\n                if e.key == key {\n                    Err(TryInsertError::Occupied {\n                        value,\n                        entry: OccupiedEntry {\n                            value: &mut e.value,\n                        },\n                    })\n                } else {\n                    Err(TryInsertError::Collision {\n                        value,\n                        existing_key: e.key,\n                    })\n                }\n            }\n            None => panic!(\"hash function returned out-of-range slot\"),\n        }\n    }\n\n    /// Returns an iterator over `(&Key, &V)` pairs.\n    ///\n    /// Items are produced in slot order (increasing slot index).\n    pub fn iter(&self) -> Iter<'_, V> {\n        Iter {\n            slots: &self.slots,\n            pos: 0,\n        }\n    }\n\n    /// Returns a mutable iterator over `(Key, &mut V)` pairs.\n    ///\n    /// The key is yielded by value to avoid `(&Key, &mut V)` aliasing.\n    pub fn iter_mut(&mut self) -> IterMut<'_, V> {\n        IterMut {\n            slots: self.slots.as_mut_ptr(),\n            len: self.slots.len(),\n            pos: 0,\n            _marker: core::marker::PhantomData,\n        }\n    }\n\n    /// Returns an iterator over keys.\n    pub fn keys(&self) -> Keys<'_, V> {\n        Keys { inner: self.iter() }\n    }\n\n    /// Returns an iterator over values.\n    pub fn values(&self) -> Values<'_, V> {\n        Values { inner: self.iter() }\n    }\n\n    /// Returns a mutable iterator over values.\n    pub fn values_mut(&mut self) -> ValuesMut<'_, V> {\n        ValuesMut {\n            inner: self.iter_mut(),\n        }\n    }\n}\n\n/// Immutable iterator yielding `(&Key, &V)`.\npub struct Iter<'a, V> {\n    slots: &'a [Option<Entry<V>>],\n    pos: usize,\n}\n\nimpl<'a, V> Iterator for Iter<'a, V> {\n    type Item = (&'a Key, &'a V);\n    fn next(&mut self) -> Option<Self::Item> {\n        while self.pos < self.slots.len() {\n            let i = self.pos;\n            self.pos += 1;\n            if let Some(ref e) = self.slots[i] {\n                return Some((&e.key, &e.value));\n            }\n        }\n        None\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (0, Some(self.slots.len().saturating_sub(self.pos)))\n    }\n}\n\nuse core::marker::PhantomData;\n\n/// Mutable iterator yielding `(Key, &mut V)`.\npub struct IterMut<'a, V> {\n    slots: *mut Option<Entry<V>>,\n    len: usize,\n    pos: usize,\n    _marker: PhantomData<&'a mut [Option<Entry<V>>]>,\n}\n\nimpl<'a, V> Iterator for IterMut<'a, V> {\n    type Item = (Key, &'a mut V);\n    fn next(&mut self) -> Option<Self::Item> {\n        let n = self.len;\n        while self.pos < n {\n            let i = self.pos;\n            self.pos += 1;\n            // SAFETY: we yield at most one &mut to any slot across iterations.\n            // The iterator holds exclusive &mut access to the entire slice for\n            // its lifetime via PhantomData. We never alias two &mut to the\n            // same element.\n            let slot = unsafe { &mut *self.slots.add(i) };\n            if let Some(entry) = slot.as_mut() {\n                let k = entry.key;\n                let v: *mut V = &mut entry.value;\n                return Some((k, unsafe { &mut *v }));\n            }\n        }\n        None\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (0, Some(self.len.saturating_sub(self.pos)))\n    }\n}\n\n/// Iterator over keys.\npub struct Keys<'a, V> {\n    inner: Iter<'a, V>,\n}\n\nimpl<'a, V> Iterator for Keys<'a, V> {\n    type Item = &'a Key;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.next().map(|(k, _)| k)\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\n/// Iterator over values.\npub struct Values<'a, V> {\n    inner: Iter<'a, V>,\n}\n\nimpl<'a, V> Iterator for Values<'a, V> {\n    type Item = &'a V;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.next().map(|(_, v)| v)\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\n/// Mutable iterator over values.\npub struct ValuesMut<'a, V> {\n    inner: IterMut<'a, V>,\n}\n\nimpl<'a, V> Iterator for ValuesMut<'a, V> {\n    type Item = &'a mut V;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.next().map(|(_, v)| v)\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\n// unit tests live in `tests/` to avoid duplication and improve discoverability\n"
  },
  {
    "path": "crates/perfect_hash_map/tests/basic.rs",
    "content": "//! Deterministic unit tests for PerfectHashMap.\n//! These cover construction, basic operations, collisions, clear, queries,\n//! iterators, out-of-range panics, and generic hash closure usage.\n\nuse perfect_hash_map::{HashFn, PerfectHashMap, TryInsertError};\n\nfn phash(k: u32) -> u32 {\n    k % 8\n}\n\n/// Validates new map state, capacity and emptiness.\n#[test]\nfn new_and_capacity() {\n    let m = PerfectHashMap::<i32, HashFn>::new(8, phash as HashFn);\n    assert!(m.is_empty());\n    assert_eq!(m.capacity(), 8);\n    assert_eq!(m.len(), 0);\n}\n\n/// Basic insert, get, replace, and remove lifecycle.\n#[test]\nfn basic_insert_get_update_remove() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    assert_eq!(m.insert(1, \"one\"), Ok(None));\n    assert!(m.contains_key(&1));\n    assert_eq!(m.len(), 1);\n    assert_eq!(m.get(&1), Some(&\"one\"));\n    assert_eq!(m.insert(1, \"uno\"), Ok(Some(\"one\")));\n    assert_eq!(m.get(&1), Some(&\"uno\"));\n    assert_eq!(m.remove(&1), Some(\"uno\"));\n    assert!(!m.contains_key(&1));\n    assert_eq!(m.len(), 0);\n}\n\n/// Validates collision handling between different keys mapping to the same slot.\n#[test]\nfn collision_handling() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    // keys 1 and 9 collide (1 % 8 == 1, 9 % 8 == 1)\n    assert_eq!(m.insert(1, \"one\"), Ok(None));\n    let err = m.insert(9, \"nine\").unwrap_err();\n    assert_eq!(err.existing_key, 1);\n    match m.try_insert(9, \"nine\") {\n        Err(TryInsertError::Collision { existing_key, .. }) => assert_eq!(existing_key, 1),\n        _ => panic!(\"expected collision\"),\n    }\n    // same-key occupied: try_insert should return Occupied with access to entry\n    match m.try_insert(1, \"uno\") {\n        Err(TryInsertError::Occupied { mut entry, .. }) => {\n            assert_eq!(entry.replace(\"eins\"), \"one\");\n            assert_eq!(m.get(&1), Some(&\"eins\"));\n        }\n        _ => panic!(\"expected occupied\"),\n    }\n}\n\n/// clear() empties the map and resets counters; subsequent inserts behave as from empty.\n#[test]\nfn clear_empties_map() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    for k in [0u32, 2, 4] {\n        let _ = m.insert(k, k * 10);\n    }\n    assert_eq!(m.len(), 3);\n    m.clear();\n    assert!(m.is_empty());\n    for k in [0u32, 2, 4] {\n        assert!(!m.contains_key(&k));\n    }\n    assert_eq!(m.insert(2, 42), Ok(None));\n    assert_eq!(m.get(&2), Some(&42));\n}\n\n/// contains_key/get/get_mut reflect presence and allow in-place mutation.\n#[test]\nfn queries_and_get_mut() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    assert_eq!(m.get(&3), None);\n    assert_eq!(m.insert(3, 7), Ok(None));\n    assert!(m.contains_key(&3));\n    if let Some(v) = m.get_mut(&3) {\n        *v += 1;\n    }\n    assert_eq!(m.get(&3), Some(&8));\n    // a different key hashing to the same slot should not be visible\n    assert_eq!(m.get(&11), None); // 11 % 8 == 3\n}\n\n/// Iterator correctness and mutation via iter_mut.\n#[test]\nfn iterators() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    for k in [0u32, 2, 4] {\n        let _ = m.insert(k, k * 10);\n    }\n    // iter yields all pairs\n    let mut kv: Vec<(u32, u32)> = m.iter().map(|(k, v)| (*k, *v)).collect();\n    kv.sort_by_key(|x| x.0);\n    assert_eq!(kv, vec![(0, 0), (2, 20), (4, 40)]);\n    // iter_mut allows mutation\n    for (k, v) in m.iter_mut() {\n        if k == 2 {\n            *v = 99;\n        }\n    }\n    assert_eq!(m.get(&2), Some(&99));\n    // keys() and values() projections\n    let keys: Vec<u32> = m.keys().copied().collect();\n    assert!(keys.contains(&0) && keys.contains(&2) && keys.contains(&4));\n    let values: Vec<u32> = m.values().copied().collect();\n    assert!(values.contains(&0) && values.contains(&99) && values.contains(&40));\n}\n\n/// Insert/try_insert panic on out-of-range hash; get/remove return None.\n#[test]\n#[should_panic]\nfn insert_panics_on_out_of_range_hash() {\n    let mut m = PerfectHashMap::new(0, |_k| 0);\n    let _ = m.insert(0, 1); // panics: slot out-of-range\n}\n\n/// try_insert panic path for out-of-range hash.\n#[test]\n#[should_panic]\nfn try_insert_panics_on_out_of_range_hash() {\n    let mut m = PerfectHashMap::new(0, |_k| 0);\n    let _ = m.try_insert(0, 1);\n}\n\n/// get/remove do not panic for out-of-range hash and return None.\n#[test]\nfn get_remove_out_of_range_hash() {\n    let mut m: PerfectHashMap<i32, _> = PerfectHashMap::new(0, |_k| 0);\n    assert_eq!(m.get(&0), None);\n    assert_eq!(m.remove(&0), None);\n}\n\n/// Generic hash function via a closure capturing a seed.\n#[test]\nfn generic_hash_closure() {\n    let cap = 8usize;\n    let seed: u32 = 7;\n    let h = move |k: u32| (k ^ seed) % (cap as u32);\n    let mut m = PerfectHashMap::new(cap, h);\n    assert_eq!(m.insert(1, 10), Ok(None));\n    assert_eq!(m.get(&1), Some(&10));\n}\n"
  },
  {
    "path": "crates/perfect_hash_map/tests/drops.rs",
    "content": "//! Drop semantics tests: ensure values are dropped exactly once on replace,\n//! remove, clear, and map drop.\n\nuse std::cell::Cell;\nuse std::rc::Rc;\n\nuse perfect_hash_map::{HashFn, PerfectHashMap};\n\nfn phash(k: u32) -> u32 {\n    k % 8\n}\n\n#[derive(Debug)]\nstruct DropCounter(Rc<Cell<usize>>);\n\nimpl Drop for DropCounter {\n    fn drop(&mut self) {\n        self.0.set(self.0.get() + 1);\n    }\n}\n\n/// Replacement drops the old value exactly once.\n#[test]\nfn drop_on_replace() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    let count = Rc::new(Cell::new(0));\n    let _ = m.insert(1, DropCounter(count.clone()));\n    let prev = m.insert(1, DropCounter(count.clone())).unwrap();\n    assert!(prev.is_some());\n    // Drop the returned previous value now.\n    drop(prev);\n    assert_eq!(count.get(), 1);\n}\n\n/// Removing a present key drops its value exactly once.\n#[test]\nfn drop_on_remove() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    let count = Rc::new(Cell::new(0));\n    let _ = m.insert(1, DropCounter(count.clone()));\n    let val = m.remove(&1).unwrap();\n    drop(val);\n    assert_eq!(count.get(), 1);\n}\n\n/// clear() drops all present values.\n#[test]\nfn drop_on_clear() {\n    let mut m = PerfectHashMap::new(8, phash as HashFn);\n    let count = Rc::new(Cell::new(0));\n    let _ = m.insert(1, DropCounter(count.clone()));\n    let _ = m.insert(2, DropCounter(count.clone()));\n    m.clear();\n    assert_eq!(count.get(), 2);\n}\n\n/// Map drop drops remaining values.\n#[test]\nfn drop_on_map_drop() {\n    let count = Rc::new(Cell::new(0));\n    {\n        let mut m = PerfectHashMap::new(8, phash as HashFn);\n        let _ = m.insert(1, DropCounter(count.clone()));\n        let _ = m.insert(2, DropCounter(count.clone()));\n        assert_eq!(count.get(), 0);\n    }\n    // Map dropped, both values dropped\n    assert_eq!(count.get(), 2);\n}\n"
  },
  {
    "path": "crates/perfect_hash_map/tests/prop.rs",
    "content": "//! Property-based tests using proptest. We model expected behavior with a\n//! shadow vector that mirrors PerfectHashMap semantics (no probing; key must\n//! match at the hashed slot). Each random operation is checked against both\n//! models and invariants are asserted after every step.\n\nuse perfect_hash_map::PerfectHashMap;\nuse proptest::prelude::*;\n\n// Simple shadow model mirroring PerfectHashMap behavior.\n#[derive(Clone, Debug)]\nstruct Shadow {\n    slots: Vec<Option<(u32, i32)>>,\n}\n\nimpl Shadow {\n    fn new(n: usize) -> Self {\n        Self {\n            slots: vec![None; n],\n        }\n    }\n    fn capacity(&self) -> usize {\n        self.slots.len()\n    }\n    fn hash(&self, seed: u32, k: u32) -> usize {\n        ((k ^ seed) % (self.capacity() as u32)) as usize\n    }\n\n    fn insert(&mut self, seed: u32, key: u32, val: i32) -> Result<Option<i32>, u32> {\n        let i = self.hash(seed, key);\n        match &mut self.slots[i] {\n            None => {\n                self.slots[i] = Some((key, val));\n                Ok(None)\n            }\n            Some((k, v)) if *k == key => Ok(Some(std::mem::replace(v, val))),\n            Some((k, _)) => Err(*k),\n        }\n    }\n    fn try_insert(&mut self, seed: u32, key: u32, val: i32) -> Result<(), TryOutcome> {\n        let i = self.hash(seed, key);\n        match &mut self.slots[i] {\n            None => {\n                self.slots[i] = Some((key, val));\n                Ok(())\n            }\n            Some((k, _v)) if *k == key => Err(TryOutcome::Occupied),\n            Some((k, _)) => Err(TryOutcome::Collision { existing_key: *k }),\n        }\n    }\n    fn get(&self, seed: u32, key: u32) -> Option<i32> {\n        let i = self.hash(seed, key);\n        match &self.slots[i] {\n            Some((k, v)) if *k == key => Some(*v),\n            _ => None,\n        }\n    }\n    fn get_mut_add(&mut self, seed: u32, key: u32, delta: i32) -> bool {\n        let i = self.hash(seed, key);\n        match &mut self.slots[i] {\n            Some((k, v)) if *k == key => {\n                *v += delta;\n                true\n            }\n            _ => false,\n        }\n    }\n    fn remove(&mut self, seed: u32, key: u32) -> Option<i32> {\n        let i = self.hash(seed, key);\n        match &mut self.slots[i] {\n            Some((k, _)) if *k == key => self.slots[i].take().map(|(_, v)| v),\n            _ => None,\n        }\n    }\n    fn clear(&mut self) {\n        for s in &mut self.slots {\n            *s = None;\n        }\n    }\n    fn len(&self) -> usize {\n        self.slots.iter().filter(|e| e.is_some()).count()\n    }\n}\n\n#[derive(Clone, Debug)]\nenum TryOutcome {\n    Occupied,\n    Collision { existing_key: u32 },\n}\n\n#[derive(Clone, Debug)]\nenum Op {\n    Insert { key: u32, val: i32 },\n    TryInsert { key: u32, val: i32 },\n    Remove { key: u32 },\n    Get { key: u32 },\n    GetMutAdd { key: u32, delta: i32 },\n    Clear,\n}\n\n/// Executes a random sequence of operations and checks behavior against a shadow model,\n/// asserting invariants after each step.\n#[test]\nfn prop_sequence_matches_shadow() {\n    let config = ProptestConfig {\n        cases: 64,\n        ..ProptestConfig::default()\n    };\n    proptest!(config, |(cap in 1usize..16, seed in any::<u32>(), ops in prop::collection::vec((any::<u8>(), any::<u32>(), any::<i32>()), 0..100))| {\n        let hash = move |k: u32| (k ^ seed) % (cap as u32);\n        let mut m = PerfectHashMap::new(cap, hash);\n        let mut sh = Shadow::new(cap);\n\n        for (tag, kraw, vraw) in ops.iter().copied() {\n            let key = if cap == 0 { 0 } else { kraw % ((cap as u32) * 4) };\n            let delta = (vraw % 51) as i32; // small delta for get_mut_add\n            let op = match tag % 6 {\n                0 => Op::Insert { key, val: vraw },\n                1 => Op::TryInsert { key, val: vraw },\n                2 => Op::Remove { key },\n                3 => Op::Get { key },\n                4 => Op::GetMutAdd { key, delta },\n                _ => Op::Clear,\n            };\n            match op {\n                Op::Insert { key, val } => {\n                    let model = sh.insert(seed, key, val);\n                    let dut = m.insert(key, val);\n                    match (model, dut) {\n                        (Ok(None), Ok(None)) => {},\n                        (Ok(Some(x)), Ok(Some(y))) => assert_eq!(x, y),\n                        (Err(km), Err(ke)) => assert_eq!(km, ke.existing_key),\n                        other => panic!(\"mismatch on insert: {:?}\", other),\n                    }\n                }\n                Op::TryInsert { key, val } => {\n                    let model = sh.try_insert(seed, key, val);\n                    let dut = m.try_insert(key, val);\n                    match (model, dut) {\n                        (Ok(()), Ok(_)) => {},\n                        (Err(TryOutcome::Occupied), Err(perfect_hash_map::TryInsertError::Occupied { .. })) => {},\n                        (Err(TryOutcome::Collision { existing_key: km }), Err(perfect_hash_map::TryInsertError::Collision { existing_key: ke, .. })) => assert_eq!(km, ke),\n                        other => panic!(\"mismatch on try_insert: {:?}\", other),\n                    }\n                }\n                Op::Remove { key } => {\n                    let model = sh.remove(seed, key);\n                    let dut = m.remove(&key);\n                    prop_assert_eq!(dut, model);\n                }\n                Op::Get { key } => {\n                    let model = sh.get(seed, key);\n                    let dut = m.get(&key).copied();\n                    prop_assert_eq!(dut, model);\n                }\n                Op::GetMutAdd { key, delta } => {\n                    let _ = sh.get_mut_add(seed, key, delta);\n                    if let Some(v) = m.get_mut(&key) { *v = v.saturating_add(delta); }\n                }\n                Op::Clear => {\n                    sh.clear();\n                    m.clear();\n                }\n            }\n            // Verify invariants after each operation\n            prop_assert_eq!(m.len(), sh.len());\n            // Compare by slot order via iter; build current snapshot from shadow\n            let mut exp = Vec::new();\n            for s in &sh.slots { if let Some((k,v)) = s { exp.push((*k,*v)); } }\n            let got: Vec<(u32, i32)> = m.iter().map(|(k,v)| (*k,*v)).collect();\n            prop_assert_eq!(got, exp);\n        }\n    });\n}\n\n/// After a random state is built, applying iter_mut with a transform updates all values as expected.\n#[test]\nfn prop_iter_mut_transform() {\n    let config = ProptestConfig {\n        cases: 32,\n        ..ProptestConfig::default()\n    };\n    proptest!(config, |(cap in 1usize..16, seed in any::<u32>(), pairs in prop::collection::vec((any::<u32>(), -1000i32..1000), 0..128))| {\n        let hash = move |k: u32| (k ^ seed) % (cap as u32);\n        let mut m = PerfectHashMap::new(cap, hash);\n        let mut sh = Shadow::new(cap);\n        for (kraw, v) in pairs.iter().copied() {\n            let k = if cap == 0 { 0 } else { kraw % ((cap as u32) * 4) };\n            let _ = sh.insert(seed, k, v);\n            let _ = m.insert(k, v);\n        }\n        for (_, v) in m.iter_mut() { *v = v.saturating_add(1); }\n        for s in &mut sh.slots { if let Some((_, v)) = s { *v = v.saturating_add(1); } }\n        let exp: Vec<(u32, i32)> = sh.slots.iter().filter_map(|e| e.map(|(k,v)| (k,v))).collect();\n        let got: Vec<(u32, i32)> = m.iter().map(|(k, v)| (*k, *v)).collect();\n        prop_assert_eq!(got, exp);\n    });\n}\n"
  },
  {
    "path": "crates/reducer/Cargo.toml",
    "content": "[package]\nname = \"reducer\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"reducer\"\npath = \"src/lib.rs\"\n\n[dependencies]\nclap = { version = \"4\", features = [\"derive\"] }\nreducer-sys = { workspace = true }\nelement-queue = { path = \"../element-queue\" }\ntimeslot = { path = \"../timeslot\" }\nrender_parser = { workspace = true }\nencoder_ebpf_net_aggregation = { path = \"../render/ebpf_net/aggregation\" }\ncxx = \"1\"\nrc-hashmap = { workspace = true }\notlp_export = { workspace = true }\n\n[dev-dependencies]\nproptest = \"1\"\n"
  },
  {
    "path": "crates/reducer/src/aggregation_core.rs",
    "content": "use std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\n\nuse render_parser::Parser;\n\n// Render-generated perfect hash and metadata for aggregation\nuse crate::aggregation_message_handler::AggregationMessageHandler;\nuse crate::aggregator::Aggregator;\nuse crate::otlp_encoding::OtlpExporter;\nuse crate::queue_handler::QueueHandler;\nuse encoder_ebpf_net_aggregation::hash::{aggregation_hash, AGGREGATION_HASH_SIZE};\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\ntype Handler = Box<dyn Fn(u32, usize, u64, &[u8]) + 'static>;\n\npub struct AggregationCore {\n    queue_handler: QueueHandler,\n    parser: Parser<Handler, fn(u32) -> u32>,\n    shard: u32,\n    stop: Arc<AtomicBool>,\n    aggregator: Rc<RefCell<Aggregator>>,\n    exporter: OtlpExporter,\n}\n\nimpl AggregationCore {\n    pub fn new(\n        eq_views: &[(usize, u32, u32)],\n        shard: u32,\n        enable_id_id: bool,\n        enable_az_id: bool,\n        endpoint: &str,\n        disable_node_ip_field: bool,\n        enable_metric_descriptions: bool,\n    ) -> Self {\n        // Shared stop flag and queue handler from descriptors\n        let stop = Arc::new(AtomicBool::new(false));\n        let queue_handler = QueueHandler::new_from_views(eq_views, stop.clone());\n\n        // Build parser with render-provided perfect hash\n        let hash_size = AGGREGATION_HASH_SIZE as usize;\n        let mut parser: Parser<Handler, fn(u32) -> u32> = Parser::new(hash_size, aggregation_hash);\n\n        // Create aggregator and handler; closures capture Rc clones\n        let aggregator = Rc::new(RefCell::new(Aggregator::new()));\n        let _handler = AggregationMessageHandler::new(&mut parser, aggregator.clone());\n\n        let this = Self {\n            queue_handler,\n            parser,\n            shard,\n            stop,\n            aggregator,\n            exporter: if !endpoint.is_empty() {\n                OtlpExporter::with_endpoint(endpoint.to_string(), enable_metric_descriptions)\n            } else {\n                OtlpExporter::new_local(enable_metric_descriptions)\n            },\n        };\n\n        // Configure aggregator flags\n        {\n            let mut agg = this.aggregator.borrow_mut();\n            agg.enable_id_id = enable_id_id;\n            agg.enable_az_id = enable_az_id;\n            agg.disable_node_ip_field = disable_node_ip_field;\n        }\n        this\n    }\n\n    pub fn stop(&self) {\n        self.stop.store(true, Ordering::Relaxed);\n    }\n\n    pub fn run(&mut self) {\n        if self.queue_handler.is_empty() {\n            return;\n        }\n\n        // Bind parser and aggregator/exporter into queue callbacks\n        let shard = self.shard;\n        let parser = &self.parser;\n        let agg = self.aggregator.clone();\n        let exporter = &mut self.exporter;\n\n        self.queue_handler.run(\n            // Message handler: parse and dispatch\n            move |queue_idx, bytes| match parser.handle(bytes) {\n                Ok(ok) => (ok.value)(shard, queue_idx, ok.timestamp, ok.message),\n                Err(e) => {\n                    println!(\"agg[shard={}] eq={} parse_error={:?}\", shard, queue_idx, e);\n                }\n            },\n            // Timeslot end: flush metrics\n            move |window_end_ns| {\n                agg.borrow_mut().output_metrics(window_end_ns, exporter);\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/aggregation_framework.rs",
    "content": "//! Aggregation Framework: generic group-by + fold utilities for reducer\n//!\n//! Goals\n//! - Provide a minimal, generic API to aggregate arbitrary items by a key `Range` and fold values into `T`.\n//! - Use closures (no traits) for projection and reduction to keep call sites simple and flexible.\n//! - Support both a simple one-shot API that returns a map and an advanced API that accumulates into an existing map\n//!   with a custom initializer.\n//!\n//! Core idea\n//! - Inputs are arbitrary iterator items `E`.\n//! - A projection function maps an item to a key: `project(&E) -> Range`.\n//! - An add function folds each item into `T`: `add(&mut T, &E)`.\n//! - Aggregation iterates once, grouping by `Range` and reducing into a `HashMap<Range, T>`.\n//!\n//! Ownership & lifetimes\n//! - `project(&E) -> R` should produce an owned `R` (e.g., `String`, `u64`, tuples). Avoid returning references into\n//!   ephemeral data inside `E` that won't live past the iteration.\n//! - If keys are large or expensive to clone, consider interning or using `Arc<str>`/`Arc<[u8]>` inside `project`.\n//!\n//! Complexity\n//! - Time: expected O(n) with the default hasher.\n//! - Memory: proportional to the number of distinct `Range` keys.\n//! - Iteration order of the returned map is unspecified.\n//!\n//! Example\n//! ```\n//! use std::collections::HashMap;\n//! use reducer::aggregation_framework::aggregate;\n//!\n//! // Count by parity of u8\n//! let data: Vec<(u8, ())> = vec![(1,()), (2,()), (4,()), (5,())];\n//! let project = |(d, _s): &(u8, ())| d % 2;        // key: even/odd\n//! let add = |t: &mut u64, _item: &(u8, ())| *t += 1; // count occurrences\n//! let by_parity: HashMap<u8, u64> = aggregate(data, project, add);\n//! assert_eq!(by_parity.get(&0), Some(&2)); // 2 and 4\n//! assert_eq!(by_parity.get(&1), Some(&2)); // 1 and 5\n//! ```\n\nuse std::collections::HashMap;\nuse std::hash::{BuildHasher, Hash};\n\n/// Advanced aggregation: accumulate into an existing map with a custom initializer.\n///\n/// - `dest`: destination map to accumulate into.\n/// - `iter`: items to aggregate. Consumed exactly once.\n/// - `project`: maps each item `E` to an owned key `R`.\n/// - `init`: constructs a fresh `T` when a new key is encountered.\n/// - `add`: folds each item into the corresponding `T`.\npub fn aggregate_with<I, E, R, T, P, F, A, H>(\n    dest: &mut HashMap<R, T, H>,\n    iter: I,\n    mut project: P,\n    mut init: F,\n    mut add: A,\n) where\n    I: IntoIterator<Item = E>,\n    P: FnMut(&E) -> R,\n    F: FnMut() -> T,\n    A: FnMut(&mut T, &E),\n    R: Eq + Hash,\n    H: BuildHasher,\n{\n    let it = iter.into_iter();\n    let (lower, _) = it.size_hint();\n    dest.reserve(lower);\n    for item in it {\n        let r = project(&item);\n        let entry = dest.entry(r).or_insert_with(&mut init);\n        add(entry, &item);\n    }\n}\n\n/// Simple aggregation: one-shot group-by + fold that returns a fresh `HashMap`.\n///\n/// This is sugar over `aggregate_with`, using `T: Default` for initialization and the default hasher.\npub fn aggregate<I, E, R, T, P, A>(iter: I, project: P, add: A) -> HashMap<R, T>\nwhere\n    I: IntoIterator<Item = E>,\n    P: FnMut(&E) -> R,\n    A: FnMut(&mut T, &E),\n    R: Eq + Hash,\n    T: Default,\n{\n    let inner = iter.into_iter();\n    let (lower, _) = inner.size_hint();\n    let mut map: HashMap<R, T> = HashMap::with_capacity(lower);\n    aggregate_with(&mut map, inner, project, T::default, add);\n    map\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::collection::vec;\n    use proptest::prelude::*;\n\n    #[test]\n    fn empty_iterator_returns_empty_map() {\n        let data: Vec<(u32, u32)> = vec![];\n        let m: HashMap<u32, u64> = aggregate(\n            data,\n            |(d, _s): &(u32, u32)| *d,\n            |t: &mut u64, (_d, s): &(u32, u32)| *t += *s as u64,\n        );\n        assert!(m.is_empty());\n    }\n\n    #[test]\n    fn single_key_all_elements_aggregated() {\n        let data: Vec<(u8, u16)> = (0..10).map(|i| (i, 1u16)).collect();\n        let m: HashMap<u8, u64> =\n            aggregate(data, |_item: &(u8, u16)| 0u8, |t, (_d, s)| *t += *s as u64);\n        assert_eq!(m.len(), 1);\n        assert_eq!(m.get(&0u8), Some(&10u64));\n    }\n\n    #[test]\n    fn distinct_keys_each_once() {\n        let data: Vec<(u32, u16)> = (0..100).map(|i| (i, 1u16)).collect();\n        let m: HashMap<u32, u64> = aggregate(\n            data,\n            |(d, _s): &(u32, u16)| *d,\n            |t, (_d, s)| *t += *s as u64,\n        );\n        assert_eq!(m.len(), 100);\n        for i in 0u32..100 {\n            assert_eq!(m.get(&i), Some(&1u64));\n        }\n    }\n\n    #[test]\n    fn aggregate_with_custom_init_non_default_type() {\n        #[derive(Debug, PartialEq, Eq)]\n        struct NonDefault {\n            sum: u64,\n            flag: bool,\n        }\n\n        let data = vec![(1u8, 5u16), (1u8, 7u16), (2u8, 10u16)];\n        let mut dest: HashMap<u8, NonDefault> = HashMap::new();\n        aggregate_with(\n            &mut dest,\n            data,\n            |(d, _s): &(u8, u16)| *d,\n            || NonDefault { sum: 1, flag: true }, // custom init\n            |t: &mut NonDefault, (_d, s): &(u8, u16)| t.sum += *s as u64,\n        );\n\n        assert_eq!(dest.len(), 2);\n        assert_eq!(\n            dest.get(&1u8),\n            Some(&NonDefault {\n                sum: 1 + 5 + 7,\n                flag: true\n            })\n        );\n        assert_eq!(\n            dest.get(&2u8),\n            Some(&NonDefault {\n                sum: 1 + 10,\n                flag: true\n            })\n        );\n    }\n\n    proptest! {\n        #[test]\n        fn proptest_equivalence_to_naive_fold(values in vec((any::<u8>(), any::<u16>()), 0..200), k in 1u8..=8u8) {\n            // naive fold\n            let mut naive: HashMap<u8, u64> = HashMap::new();\n            for (d, s) in &values {\n                let key = d % k;\n                *naive.entry(key).or_insert(0) += *s as u64;\n            }\n\n            // aggregate helper\n            let project = |(d, _s): &(u8, u16)| d % k;\n            let add = |t: &mut u64, (_d, s): &(u8, u16)| *t += *s as u64;\n            let got: HashMap<u8, u64> = aggregate(values.clone(), project, add);\n\n            prop_assert_eq!(got, naive);\n        }\n\n        #[test]\n        fn proptest_chunked_aggregation_matches_one_shot(values in vec((any::<u8>(), any::<u16>()), 0..200), k in 1u8..=8u8) {\n            let mid = values.len().saturating_div(2);\n            let (left, right) = values.split_at(mid);\n\n            let mut dest: HashMap<u8, u64> = HashMap::new();\n            let project = |(d, _s): &(u8, u16)| d % k;\n            let add = |t: &mut u64, (_d, s): &(u8, u16)| *t += *s as u64;\n\n            aggregate_with(&mut dest, left.to_vec(), project, || 0u64, add);\n            aggregate_with(&mut dest, right.to_vec(), |(d, _s): &(u8, u16)| d % k, || 0u64, |t, (_d, s): &(u8, u16)| *t += *s as u64);\n\n            // one-shot\n            let one_shot: HashMap<u8, u64> = aggregate(values.clone(), |(d, _s): &(u8, u16)| d % k, |t, (_d, s): &(u8, u16)| *t += *s as u64);\n\n            prop_assert_eq!(dest, one_shot);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/aggregation_message_handler.rs",
    "content": "//! Message decoding and dispatch into the Aggregator using a handler struct.\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse render_parser::Parser;\n\nuse crate::aggregator::{AggRootKey, Aggregator, Az, Direction, Node, Side};\nuse crate::metrics::{DnsMetrics, HttpMetrics, TcpMetrics, UdpMetrics};\nuse encoder_ebpf_net_aggregation::parsed_message;\nuse encoder_ebpf_net_aggregation::wire_messages;\n\ntype Handler = Box<dyn Fn(u32, usize, u64, &[u8]) + 'static>;\n\n#[derive(Clone)]\npub struct AggregationMessageHandler {\n    agg: Rc<RefCell<Aggregator>>,\n}\n\nimpl AggregationMessageHandler {\n    pub fn new(\n        parser: &mut Parser<Handler, fn(u32) -> u32>,\n        agg: Rc<RefCell<Aggregator>>,\n    ) -> Rc<Self> {\n        let rc = Rc::new(Self { agg });\n\n        // Register closures that capture Rc<Self>\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__agg_root_start::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_agg_root_start(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__agg_root_end::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_agg_root_end(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__update_node::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_update_node(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__update_tcp_metrics::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_update_tcp(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__update_udp_metrics::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_update_udp(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__update_http_metrics::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_update_http(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__update_dns_metrics::metadata(),\n                Box::new(move |_shard, q, _ts, buf| h.on_update_dns(q, buf)),\n            );\n        }\n        {\n            let h = rc.clone();\n            let _ = parser.add_message(\n                wire_messages::jb_aggregation__pulse::metadata(),\n                Box::new(move |_shard, _q, _ts, buf| h.on_pulse(buf)),\n            );\n        }\n\n        rc\n    }\n\n    fn on_agg_root_start(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::agg_root_start::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                self.agg.borrow_mut().agg_root_start(key);\n            }\n            Err(_e) => self\n                .agg\n                .borrow_mut()\n                .events\n                .inc_decode_error_agg_root_start(),\n        }\n    }\n\n    fn on_agg_root_end(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::agg_root_end::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                self.agg.borrow_mut().agg_root_end(key);\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_agg_root_end(),\n        }\n    }\n\n    fn on_update_node(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::update_node::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                let az = Az {\n                    az: msg.az.to_string(),\n                    role: msg.role.to_string(),\n                    version: msg.version.to_string(),\n                    env: msg.env.to_string(),\n                    ns: msg.ns.to_string(),\n                    node_type: msg.node_type as u8,\n                    process: msg.process.to_string(),\n                    container: msg.container.to_string(),\n                    role_uid: msg.role_uid.to_string(),\n                };\n                let node = Node {\n                    id: msg.id.to_string(),\n                    address: msg.address.to_string(),\n                    pod_name: msg.pod_name.to_string(),\n                };\n                self.agg.borrow_mut().update_node(\n                    key,\n                    if msg.side == 0 { Side::A } else { Side::B },\n                    az,\n                    node,\n                );\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_update_node(),\n        }\n    }\n\n    fn on_update_tcp(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::update_tcp_metrics::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                let m = TcpMetrics {\n                    active_sockets: msg.active_sockets as u64,\n                    sum_bytes: msg.sum_bytes as u64,\n                    sum_srtt: msg.sum_srtt as u64,\n                    sum_delivered: msg.sum_delivered as u64,\n                    sum_retrans: msg.sum_retrans as u64,\n                    active_rtts: msg.active_rtts as u64,\n                    syn_timeouts: msg.syn_timeouts as u64,\n                    new_sockets: msg.new_sockets as u64,\n                    tcp_resets: msg.tcp_resets as u64,\n                };\n                let dir = if msg.direction == 0 {\n                    Direction::AtoB\n                } else {\n                    Direction::BtoA\n                };\n                self.agg.borrow_mut().add_tcp(key, dir, m);\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_update_tcp(),\n        }\n    }\n\n    fn on_update_udp(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::update_udp_metrics::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                let m = UdpMetrics {\n                    active_sockets: msg.active_sockets as u64,\n                    bytes: msg.bytes as u64,\n                    addr_changes: msg.addr_changes as u64,\n                    packets: msg.packets as u64,\n                    drops: msg.drops as u64,\n                };\n                let dir = if msg.direction == 0 {\n                    Direction::AtoB\n                } else {\n                    Direction::BtoA\n                };\n                self.agg.borrow_mut().add_udp(key, dir, m);\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_update_udp(),\n        }\n    }\n\n    fn on_update_http(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::update_http_metrics::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                let m = HttpMetrics {\n                    active_sockets: msg.active_sockets as u64,\n                    sum_total_time_ns: msg.sum_total_time_ns as u64,\n                    sum_processing_time_ns: msg.sum_processing_time_ns as u64,\n                    sum_code_200: msg.sum_code_200 as u64,\n                    sum_code_400: msg.sum_code_400 as u64,\n                    sum_code_500: msg.sum_code_500 as u64,\n                    sum_code_other: msg.sum_code_other as u64,\n                };\n                let dir = if msg.direction == 0 {\n                    Direction::AtoB\n                } else {\n                    Direction::BtoA\n                };\n                self.agg.borrow_mut().add_http(key, dir, m);\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_update_http(),\n        }\n    }\n\n    fn on_update_dns(&self, queue_idx: usize, buf: &[u8]) {\n        match parsed_message::update_dns_metrics::decode(buf) {\n            Ok(msg) => {\n                let key: AggRootKey = (queue_idx, msg._ref);\n                let m = DnsMetrics {\n                    active_sockets: msg.active_sockets as u64,\n                    sum_total_time_ns: msg.sum_total_time_ns as u64,\n                    sum_processing_time_ns: msg.sum_processing_time_ns as u64,\n                    requests_a: msg.requests_a as u64,\n                    requests_aaaa: msg.requests_aaaa as u64,\n                    responses: msg.responses as u64,\n                    timeouts: msg.timeouts as u64,\n                };\n                let dir = if msg.direction == 0 {\n                    Direction::AtoB\n                } else {\n                    Direction::BtoA\n                };\n                self.agg.borrow_mut().add_dns(key, dir, m);\n            }\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_update_dns(),\n        }\n    }\n\n    fn on_pulse(&self, buf: &[u8]) {\n        match parsed_message::pulse::decode(buf) {\n            Ok(_msg) => {}\n            Err(_e) => self.agg.borrow_mut().events.inc_decode_error_pulse(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/aggregator.rs",
    "content": "//! Aggregator: core domain state and API (skeleton).\n\nuse std::collections::HashMap;\n\nuse crate::aggregation_framework::aggregate;\nuse crate::internal_events::Counters;\nuse crate::metrics::{DnsMetrics, HttpMetrics, TcpMetrics, UdpMetrics};\nuse crate::otlp_encoding::{\n    add_node_labels, Labels, OtlpExporter, LABEL_AGGREGATION, LABEL_SF_PRODUCT,\n    LABEL_SF_PRODUCT_VALUE,\n};\nuse otlp_export::ffi::Label as OLabel;\nuse rc_hashmap::{RcHashMap, Ref};\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum Side {\n    A,\n    B,\n}\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum Direction {\n    AtoB,\n    BtoA,\n}\n\npub type AggRootKey = (usize, u64); // (queue_index, _ref)\n\n#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]\npub struct Az {\n    pub az: String,\n    pub role: String,\n    pub version: String,\n    pub env: String,\n    pub ns: String,\n    pub node_type: u8,\n    pub process: String,\n    pub container: String,\n    pub role_uid: String,\n}\n\n#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]\npub struct Node {\n    pub id: String,\n    pub address: String,\n    pub pod_name: String,\n}\n\ntype AzRef = Ref<Az, ()>;\ntype NodeRef = Ref<Node, ()>;\n\n#[derive(Default)]\nstruct AggRoot {\n    a_node: Option<NodeRef>,\n    a_az: Option<AzRef>,\n    b_node: Option<NodeRef>,\n    b_az: Option<AzRef>,\n    a_to_b: Option<NodeNodeRef>,\n    b_to_a: Option<NodeNodeRef>,\n}\n\n#[derive(Clone, PartialEq, Eq, Hash)]\nstruct NodeNodeKey {\n    src_az: AzRef,\n    src_node: NodeRef,\n    dst_az: AzRef,\n    dst_node: NodeRef,\n}\n\n#[derive(Clone, PartialEq, Eq, Hash)]\nstruct NodeAzKey {\n    src_az: AzRef,\n    src_node: NodeRef,\n    dst_az: AzRef,\n}\n#[derive(Clone, PartialEq, Eq, Hash)]\nstruct AzNodeKey {\n    src_az: AzRef,\n    dst_node: NodeRef,\n    dst_az: AzRef,\n}\n#[derive(Clone, PartialEq, Eq, Hash)]\nstruct AzAzKey {\n    src_az: AzRef,\n    dst_az: AzRef,\n}\n#[derive(Default)]\nstruct NodeNodeEntry {\n    metrics: AllMetrics,\n    keepalive: Option<NodeNodeRef>,\n}\n\ntype NodeNodeRef = Ref<NodeNodeKey, NodeNodeEntry>;\n\n// Common labels computation across all keys.\nfn compute_labels(\n    az_store: &RcHashMap<Az, ()>,\n    node_store: &RcHashMap<Node, ()>,\n    src_az: &AzRef,\n    dst_az: &AzRef,\n    src_node: Option<&NodeRef>,\n    dst_node: Option<&NodeRef>,\n) -> Labels {\n    let mut labels: Labels = Vec::new();\n    labels.push(OLabel {\n        key: LABEL_SF_PRODUCT.to_string(),\n        value: LABEL_SF_PRODUCT_VALUE.to_string(),\n    });\n\n    let src_az_v = src_az.key(az_store).ok();\n    let dst_az_v = dst_az.key(az_store).ok();\n    let src_node_v = src_node.and_then(|n| n.key(node_store).ok());\n    let dst_node_v = dst_node.and_then(|n| n.key(node_store).ok());\n\n    add_node_labels(\"source.\", src_node_v, src_az_v, &mut labels);\n    add_node_labels(\"dest.\", dst_node_v, dst_az_v, &mut labels);\n\n    if let (Some(sa), Some(da)) = (src_az_v, dst_az_v) {\n        if !sa.az.is_empty() && !da.az.is_empty() {\n            labels.push(OLabel {\n                key: \"az_equal\".to_string(),\n                value: (sa.az == da.az).to_string(),\n            });\n        }\n    }\n    labels\n}\n\nimpl NodeNodeKey {\n    fn to_otlp_labels(\n        &self,\n        az_store: &RcHashMap<Az, ()>,\n        node_store: &RcHashMap<Node, ()>,\n    ) -> Labels {\n        let mut labels = compute_labels(\n            az_store,\n            node_store,\n            &self.src_az,\n            &self.dst_az,\n            Some(&self.src_node),\n            Some(&self.dst_node),\n        );\n        labels.push(OLabel {\n            key: LABEL_AGGREGATION.to_string(),\n            value: \"id_id\".to_string(),\n        });\n        labels\n    }\n}\nimpl NodeAzKey {\n    fn to_otlp_labels(\n        &self,\n        az_store: &RcHashMap<Az, ()>,\n        node_store: &RcHashMap<Node, ()>,\n    ) -> Labels {\n        let mut labels = compute_labels(\n            az_store,\n            node_store,\n            &self.src_az,\n            &self.dst_az,\n            Some(&self.src_node),\n            None,\n        );\n        labels.push(OLabel {\n            key: LABEL_AGGREGATION.to_string(),\n            value: \"id_az\".to_string(),\n        });\n        labels\n    }\n}\nimpl AzNodeKey {\n    fn to_otlp_labels(\n        &self,\n        az_store: &RcHashMap<Az, ()>,\n        node_store: &RcHashMap<Node, ()>,\n    ) -> Labels {\n        let mut labels = compute_labels(\n            az_store,\n            node_store,\n            &self.src_az,\n            &self.dst_az,\n            None,\n            Some(&self.dst_node),\n        );\n        labels.push(OLabel {\n            key: LABEL_AGGREGATION.to_string(),\n            value: \"az_id\".to_string(),\n        });\n        labels\n    }\n}\nimpl AzAzKey {\n    fn to_otlp_labels(\n        &self,\n        az_store: &RcHashMap<Az, ()>,\n        _node_store: &RcHashMap<Node, ()>,\n    ) -> Labels {\n        let mut labels = compute_labels(\n            az_store,\n            _node_store,\n            &self.src_az,\n            &self.dst_az,\n            None,\n            None,\n        );\n        labels.push(OLabel {\n            key: LABEL_AGGREGATION.to_string(),\n            value: \"az_az\".to_string(),\n        });\n        labels\n    }\n}\n\n#[derive(Default)]\npub struct Aggregator {\n    pub events: Counters,\n\n    // Identity stores\n    az_store: RcHashMap<Az, ()>,\n    node_store: RcHashMap<Node, ()>,\n\n    // Roots and node-node aggregation\n    agg_roots: HashMap<AggRootKey, AggRoot>,\n    node_node_store: RcHashMap<NodeNodeKey, NodeNodeEntry>,\n\n    // Feature flags (subset for now)\n    pub enable_id_id: bool,\n    pub enable_az_id: bool,\n    pub disable_node_ip_field: bool,\n}\n\nimpl Aggregator {\n    pub fn new() -> Self {\n        Self {\n            events: Counters::default(),\n            az_store: RcHashMap::new(),\n            node_store: RcHashMap::new(),\n            agg_roots: HashMap::new(),\n            node_node_store: RcHashMap::new(),\n            enable_id_id: true,\n            enable_az_id: true,\n            disable_node_ip_field: false,\n        }\n    }\n\n    pub fn agg_root_start(&mut self, key: AggRootKey) {\n        // Insert or reset the root entry\n        self.agg_roots.insert(key, AggRoot::default());\n    }\n    pub fn agg_root_end(&mut self, key: AggRootKey) {\n        // Remove only the root; node-node entries persist via keepalive\n        let _ = self.agg_roots.remove(&key);\n    }\n\n    pub fn update_node(&mut self, key: AggRootKey, side: Side, az: Az, mut node: Node) {\n        // Optionally blank IP address for uniqueness if disabled\n        if self.disable_node_ip_field {\n            node.address.clear();\n        }\n\n        // Upsert AZ (intern full Az)\n        let az_ref = match self.az_store.find(&az) {\n            Some(r) => r,\n            None => self.az_store.insert(az, ()).expect(\"az insert failed\"),\n        };\n\n        // Upsert Node (intern full Node)\n        let node_ref = match self.node_store.find(&node) {\n            Some(r) => r,\n            None => self\n                .node_store\n                .insert(node, ())\n                .expect(\"node insert failed\"),\n        };\n\n        // Upsert root and attach side\n        let root = self.agg_roots.entry(key).or_insert_with(AggRoot::default);\n        match side {\n            Side::A => {\n                root.a_az = Some(az_ref.clone());\n                root.a_node = Some(node_ref.clone());\n            }\n            Side::B => {\n                root.b_az = Some(az_ref.clone());\n                root.b_node = Some(node_ref.clone());\n            }\n        }\n\n        // If both sides known and node-node refs not set, wire them\n        if root.a_to_b.is_none() && root.b_to_a.is_none() {\n            if let (Some(a_node), Some(a_az), Some(b_node), Some(b_az)) =\n                (&root.a_node, &root.a_az, &root.b_node, &root.b_az)\n            {\n                let a2b_key = NodeNodeKey {\n                    src_az: a_az.clone(),\n                    src_node: a_node.clone(),\n                    dst_az: b_az.clone(),\n                    dst_node: b_node.clone(),\n                };\n                let b2a_key = NodeNodeKey {\n                    src_az: b_az.clone(),\n                    src_node: b_node.clone(),\n                    dst_az: a_az.clone(),\n                    dst_node: a_node.clone(),\n                };\n                let a2b_ref = match self.node_node_store.find(&a2b_key) {\n                    Some(r) => r,\n                    None => self\n                        .node_node_store\n                        .insert(a2b_key, NodeNodeEntry::default())\n                        .expect(\"a2b insert\"),\n                };\n                let b2a_ref = match self.node_node_store.find(&b2a_key) {\n                    Some(r) => r,\n                    None => self\n                        .node_node_store\n                        .insert(b2a_key, NodeNodeEntry::default())\n                        .expect(\"b2a insert\"),\n                };\n                root.a_to_b = Some(a2b_ref);\n                root.b_to_a = Some(b2a_ref);\n            }\n        }\n    }\n\n    pub fn add_tcp(&mut self, key: AggRootKey, dir: Direction, m: TcpMetrics) {\n        if let Some(entry_ref) = self.find_node_node_ref(key, dir) {\n            if let Ok(entry) = entry_ref.value_mut(&mut self.node_node_store) {\n                if entry.keepalive.is_none() {\n                    entry.keepalive = Some(entry_ref.clone());\n                }\n                entry.metrics.tcp.add_from(&m);\n            }\n        } else {\n            self.events.inc_metric_before_sides_resolved();\n        }\n    }\n    pub fn add_udp(&mut self, key: AggRootKey, dir: Direction, m: UdpMetrics) {\n        if let Some(entry_ref) = self.find_node_node_ref(key, dir) {\n            if let Ok(entry) = entry_ref.value_mut(&mut self.node_node_store) {\n                if entry.keepalive.is_none() {\n                    entry.keepalive = Some(entry_ref.clone());\n                }\n                entry.metrics.udp.add_from(&m);\n            }\n        } else {\n            self.events.inc_metric_before_sides_resolved();\n        }\n    }\n    pub fn add_http(&mut self, key: AggRootKey, dir: Direction, m: HttpMetrics) {\n        if let Some(entry_ref) = self.find_node_node_ref(key, dir) {\n            if let Ok(entry) = entry_ref.value_mut(&mut self.node_node_store) {\n                if entry.keepalive.is_none() {\n                    entry.keepalive = Some(entry_ref.clone());\n                }\n                entry.metrics.http.add_from(&m);\n            }\n        } else {\n            self.events.inc_metric_before_sides_resolved();\n        }\n    }\n    pub fn add_dns(&mut self, key: AggRootKey, dir: Direction, m: DnsMetrics) {\n        if let Some(entry_ref) = self.find_node_node_ref(key, dir) {\n            if let Ok(entry) = entry_ref.value_mut(&mut self.node_node_store) {\n                if entry.keepalive.is_none() {\n                    entry.keepalive = Some(entry_ref.clone());\n                }\n                entry.metrics.dns.add_from(&m);\n            }\n        } else {\n            self.events.inc_metric_before_sides_resolved();\n        }\n    }\n\n    fn find_node_node_ref(&mut self, key: AggRootKey, dir: Direction) -> Option<NodeNodeRef> {\n        let root = match self.agg_roots.get(&key) {\n            Some(r) => r,\n            None => {\n                self.events.inc_missing_root_for_metric();\n                return None;\n            }\n        };\n        match dir {\n            Direction::AtoB => root.a_to_b.clone(),\n            Direction::BtoA => root.b_to_a.clone(),\n        }\n    }\n\n    pub fn output_metrics(&mut self, window_end_ns: u64, exporter: &mut OtlpExporter) {\n        // Aggregate projections first (without mutating node-node entries)\n        let (node_az_map, az_node_map, az_az_map) = if self.enable_az_id {\n            let node_az_map: HashMap<NodeAzKey, AllMetrics> = aggregate(\n                self.node_node_store.iter(),\n                |it| {\n                    let k = it.key(&self.node_node_store).unwrap();\n                    NodeAzKey {\n                        src_az: k.src_az.clone(),\n                        src_node: k.src_node.clone(),\n                        dst_az: k.dst_az.clone(),\n                    }\n                },\n                |t: &mut AllMetrics, it| {\n                    let m = &it.value(&self.node_node_store).unwrap().metrics;\n                    t.add(m)\n                },\n            );\n            let az_node_map: HashMap<AzNodeKey, AllMetrics> = aggregate(\n                self.node_node_store.iter(),\n                |it| {\n                    let k = it.key(&self.node_node_store).unwrap();\n                    AzNodeKey {\n                        src_az: k.src_az.clone(),\n                        dst_node: k.dst_node.clone(),\n                        dst_az: k.dst_az.clone(),\n                    }\n                },\n                |t: &mut AllMetrics, it| {\n                    let m = &it.value(&self.node_node_store).unwrap().metrics;\n                    t.add(m)\n                },\n            );\n            // Derive AzAz from NodeAz (avoid recomputing from NodeNode)\n            let az_az_map: HashMap<AzAzKey, AllMetrics> = aggregate(\n                node_az_map.iter(),\n                |&(k, _m)| AzAzKey {\n                    src_az: k.src_az.clone(),\n                    dst_az: k.dst_az.clone(),\n                },\n                |t: &mut AllMetrics, &(_k, m)| t.add(m),\n            );\n            (Some(node_az_map), Some(az_node_map), Some(az_az_map))\n        } else {\n            (None, None, None)\n        };\n\n        // Emit node-node (id_id)\n        if self.enable_id_id {\n            for it in self.node_node_store.iter() {\n                let k = it.key(&self.node_node_store).unwrap();\n                let m = &it.value(&self.node_node_store).unwrap().metrics;\n                if !m.should_emit_any() {\n                    continue;\n                }\n                let labels = k.to_otlp_labels(&self.az_store, &self.node_store);\n                exporter.emit_all_metrics(window_end_ns as i64, &labels, m);\n            }\n        }\n\n        // Emit node-az, az-node, az-az\n        if let (Some(node_az_map), Some(az_node_map), Some(az_az_map)) = (\n            node_az_map.as_ref(),\n            az_node_map.as_ref(),\n            az_az_map.as_ref(),\n        ) {\n            for (k, m) in node_az_map.iter() {\n                if !m.should_emit_any() {\n                    continue;\n                }\n                let labels = k.to_otlp_labels(&self.az_store, &self.node_store);\n                exporter.emit_all_metrics(window_end_ns as i64, &labels, m);\n            }\n            for (k, m) in az_node_map.iter() {\n                if !m.should_emit_any() {\n                    continue;\n                }\n                let labels = k.to_otlp_labels(&self.az_store, &self.node_store);\n                exporter.emit_all_metrics(window_end_ns as i64, &labels, m);\n            }\n            for (k, m) in az_az_map.iter() {\n                if !m.should_emit_any() {\n                    continue;\n                }\n                let labels = k.to_otlp_labels(&self.az_store, &self.node_store);\n                exporter.emit_all_metrics(window_end_ns as i64, &labels, m);\n            }\n        }\n\n        exporter.flush();\n\n        // Cleanup node-node entries after aggregations and emissions\n        for mut it in self.node_node_store.iter_mut() {\n            let e = it.value_mut();\n            let had_tcp = !e.metrics.tcp.is_zero();\n            let had_udp = !e.metrics.udp.is_zero();\n            let had_http = !e.metrics.http.is_zero();\n            let had_dns = !e.metrics.dns.is_zero();\n\n            let mut next = AllMetrics::default();\n            next.tcp_prev_window_had_samples = had_tcp;\n            next.udp_prev_window_had_samples = had_udp;\n            next.http_prev_window_had_samples = had_http;\n            next.dns_prev_window_had_samples = had_dns;\n            e.metrics = next;\n\n            if !(had_tcp || had_udp || had_http || had_dns) {\n                e.keepalive = None;\n            }\n        }\n    }\n}\n\n#[derive(Default, Clone)]\npub struct AllMetrics {\n    pub tcp: TcpMetrics,\n    pub udp: UdpMetrics,\n    pub http: HttpMetrics,\n    pub dns: DnsMetrics,\n    pub tcp_prev_window_had_samples: bool,\n    pub udp_prev_window_had_samples: bool,\n    pub http_prev_window_had_samples: bool,\n    pub dns_prev_window_had_samples: bool,\n}\n\nimpl AllMetrics {\n    fn add(&mut self, other: &Self) {\n        self.tcp.add_from(&other.tcp);\n        self.udp.add_from(&other.udp);\n        self.http.add_from(&other.http);\n        self.dns.add_from(&other.dns);\n        self.tcp_prev_window_had_samples |= other.tcp_prev_window_had_samples;\n        self.udp_prev_window_had_samples |= other.udp_prev_window_had_samples;\n        self.http_prev_window_had_samples |= other.http_prev_window_had_samples;\n        self.dns_prev_window_had_samples |= other.dns_prev_window_had_samples;\n    }\n    fn should_emit_any(&self) -> bool {\n        let tcp_emit = !self.tcp.is_zero() || self.tcp_prev_window_had_samples;\n        let udp_emit = !self.udp.is_zero() || self.udp_prev_window_had_samples;\n        let http_emit = !self.http.is_zero() || self.http_prev_window_had_samples;\n        let dns_emit = !self.dns.is_zero() || self.dns_prev_window_had_samples;\n        tcp_emit || udp_emit || http_emit || dns_emit\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/ffi.rs",
    "content": "#[cxx::bridge(namespace = \"reducer_agg\")]\nmod ffi {\n    // Plain descriptor for contiguous element-queue storage\n    #[derive(Debug)]\n    pub struct EqView {\n        pub data: *mut u8, // base pointer to contiguous storage (shared)\n        pub n_elems: u32,  // ring size (power of two)\n        pub buf_len: u32,  // data buffer size (power of two)\n    }\n\n    extern \"Rust\" {\n        type AggregationCore;\n\n        /// Create a new AggregationCore from element-queue descriptors.\n        fn aggregation_core_new(\n            queues: &CxxVector<EqView>,\n            shard: u32,\n            enable_id_id: bool,\n            enable_az_id: bool,\n            endpoint: &str,\n            disable_node_ip_field: bool,\n            enable_metric_descriptions: bool,\n        ) -> Box<AggregationCore>;\n        /// Run the core loop until stopped.\n        fn aggregation_core_run(self: Pin<&mut AggregationCore>);\n        /// Request cooperative stop.\n        fn aggregation_core_stop(self: Pin<&mut AggregationCore>);\n    }\n}\n\nuse crate::aggregation_core::AggregationCore;\n\nimpl AggregationCore {\n    fn from_views(\n        views: &cxx::CxxVector<ffi::EqView>,\n        shard: u32,\n        enable_id_id: bool,\n        enable_az_id: bool,\n        endpoint: &str,\n        disable_node_ip_field: bool,\n        enable_metric_descriptions: bool,\n    ) -> Self {\n        let mut v = Vec::with_capacity(views.len());\n        for ev in views {\n            v.push((ev.data as usize, ev.n_elems, ev.buf_len));\n        }\n        AggregationCore::new(\n            &v[..],\n            shard,\n            enable_id_id,\n            enable_az_id,\n            endpoint,\n            disable_node_ip_field,\n            enable_metric_descriptions,\n        )\n    }\n}\n\nfn aggregation_core_new(\n    queues: &cxx::CxxVector<ffi::EqView>,\n    shard: u32,\n    enable_id_id: bool,\n    enable_az_id: bool,\n    endpoint: &str,\n    disable_node_ip_field: bool,\n    enable_metric_descriptions: bool,\n) -> Box<AggregationCore> {\n    Box::new(AggregationCore::from_views(\n        queues,\n        shard,\n        enable_id_id,\n        enable_az_id,\n        endpoint,\n        disable_node_ip_field,\n        enable_metric_descriptions,\n    ))\n}\n\nimpl AggregationCore {\n    fn aggregation_core_run(self: core::pin::Pin<&mut Self>) {\n        // Safe because we don't move after pin\n        let core_ref: &mut AggregationCore = unsafe { self.get_unchecked_mut() };\n        core_ref.run();\n    }\n\n    fn aggregation_core_stop(self: core::pin::Pin<&mut Self>) {\n        let core_ref: &mut AggregationCore = unsafe { self.get_unchecked_mut() };\n        core_ref.stop();\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/internal_events.rs",
    "content": "//! Internal events and counters for exceptional conditions.\n\n#[derive(Default, Debug, Clone)]\npub struct Counters {\n    pub decode_error_agg_root_start: u64,\n    pub decode_error_agg_root_end: u64,\n    pub decode_error_update_node: u64,\n    pub decode_error_update_tcp: u64,\n    pub decode_error_update_udp: u64,\n    pub decode_error_update_http: u64,\n    pub decode_error_update_dns: u64,\n    pub decode_error_pulse: u64,\n\n    pub missing_root_for_metric: u64,\n    pub metric_before_sides_resolved: u64,\n}\n\nimpl Counters {\n    pub fn inc_decode_error_agg_root_start(&mut self) {\n        self.decode_error_agg_root_start += 1;\n    }\n    pub fn inc_decode_error_agg_root_end(&mut self) {\n        self.decode_error_agg_root_end += 1;\n    }\n    pub fn inc_decode_error_update_node(&mut self) {\n        self.decode_error_update_node += 1;\n    }\n    pub fn inc_decode_error_update_tcp(&mut self) {\n        self.decode_error_update_tcp += 1;\n    }\n    pub fn inc_decode_error_update_udp(&mut self) {\n        self.decode_error_update_udp += 1;\n    }\n    pub fn inc_decode_error_update_http(&mut self) {\n        self.decode_error_update_http += 1;\n    }\n    pub fn inc_decode_error_update_dns(&mut self) {\n        self.decode_error_update_dns += 1;\n    }\n    pub fn inc_decode_error_pulse(&mut self) {\n        self.decode_error_pulse += 1;\n    }\n\n    pub fn inc_missing_root_for_metric(&mut self) {\n        self.missing_root_for_metric += 1;\n    }\n    pub fn inc_metric_before_sides_resolved(&mut self) {\n        self.metric_before_sides_resolved += 1;\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/lib.rs",
    "content": "#![allow(clippy::too_many_arguments)]\n\nuse clap::Parser;\n\nuse reducer_sys::ffi::{ReducerConfig as FfiReducerConfig, TsdbFormat};\n\nmod aggregation_core;\npub mod aggregation_framework;\nmod aggregation_message_handler;\nmod aggregator;\npub mod ffi;\nmod internal_events;\nmod metrics;\nmod otlp_encoding;\nmod queue_handler;\n\n#[derive(Parser, Debug)]\n#[command(name = \"reducer\", about = \"OpenTelemetry eBPF Reducer\")]\nstruct Cli {\n    // Logging configuration\n    /// Do not write logs to a file\n    #[arg(long = \"no-log-file\")]\n    no_log_file: bool,\n    /// Log to console (stdout)\n    #[arg(long = \"log-console\", alias = \"console-log\")]\n    log_console: bool,\n    /// Set minimum log level to trace\n    #[arg(long = \"trace\")]\n    trace: bool,\n    /// Set minimum log level to debug\n    #[arg(long = \"debug\")]\n    debug: bool,\n    /// Set minimum log level to info\n    #[arg(long = \"info\")]\n    info: bool,\n    /// Set minimum log level to warning\n    #[arg(long = \"warning\")]\n    warning: bool,\n    /// Set minimum log level to error\n    #[arg(long = \"error\")]\n    error: bool,\n    /// Set minimum log level to critical\n    #[arg(long = \"critical\")]\n    critical: bool,\n\n    /// Print configuration values and exit\n    #[arg(long = \"print-config\")]\n    print_config: bool,\n\n    /// TCP port to listen on for incoming connections from collectors\n    #[arg(short = 'p', long = \"port\")]\n    port: Option<u32>,\n\n    /// Format of TSDB data for scraped metrics (prometheus|json)\n    #[arg(long = \"metrics-tsdb-format\", default_value = \"prometheus\")]\n    metrics_tsdb_format: String,\n\n    // Features\n    #[arg(long = \"enable-aws-enrichment\")]\n    enable_aws_enrichment: bool,\n    #[arg(long = \"disable-node-ip-field\")]\n    disable_node_ip_field: bool,\n    #[arg(long = \"enable-id-id\")]\n    enable_id_id: bool,\n    #[arg(long = \"enable-az-id\")]\n    enable_az_id: bool,\n    #[arg(long = \"enable-flow-logs\")]\n    enable_flow_logs: bool,\n    #[arg(long = \"enable-autonomous-system-ip\")]\n    enable_autonomous_system_ip: bool,\n    #[arg(long = \"enable-percentile-latencies\")]\n    enable_percentile_latencies: bool,\n\n    // Scaling\n    #[arg(long = \"num-ingest-shards\")]\n    num_ingest_shards: Option<u32>,\n    #[arg(long = \"num-matching-shards\")]\n    num_matching_shards: Option<u32>,\n    #[arg(long = \"num-aggregation-shards\")]\n    num_aggregation_shards: Option<u32>,\n    #[arg(long = \"partitions-per-shard\")]\n    partitions_per_shard: Option<u32>,\n\n    // Prometheus output\n    #[arg(long = \"disable-prometheus-metrics\")]\n    disable_prometheus_metrics: bool,\n    #[arg(long = \"shard-prometheus-metrics\")]\n    shard_prometheus_metrics: bool,\n    #[arg(long = \"prom\")]\n    prom_bind: Option<String>,\n    #[arg(long = \"scrape-size-limit-bytes\")]\n    scrape_size_limit_bytes: Option<u64>,\n\n    // OTLP gRPC output\n    #[arg(long = \"enable-otlp-grpc-metrics\")]\n    enable_otlp_grpc_metrics: bool,\n    #[arg(long = \"otlp-grpc-metrics-host\", default_value = \"localhost\")]\n    otlp_grpc_metrics_address: String,\n    #[arg(long = \"otlp-grpc-metrics-port\")]\n    otlp_grpc_metrics_port: Option<u32>,\n    #[arg(long = \"otlp-grpc-batch-size\")]\n    otlp_grpc_batch_size: Option<i32>,\n    #[arg(long = \"enable-otlp-grpc-metric-descriptions\")]\n    enable_otlp_grpc_metric_descriptions: bool,\n\n    // Metrics output enable/disable\n    #[arg(long = \"disable-metrics\")]\n    disable_metrics: Option<String>,\n    #[arg(long = \"enable-metrics\")]\n    enable_metrics: Option<String>,\n\n    // Internal stats\n    #[arg(long = \"internal-prom\")]\n    internal_prom_bind: Option<String>,\n    #[arg(long = \"stats-scrape-size-limit-bytes\")]\n    stats_scrape_size_limit_bytes: Option<u64>,\n\n    // Logging and debugging\n    #[arg(long = \"index-dump-interval\")]\n    index_dump_interval: Option<u64>,\n\n    // Whitelist controls\n    /// Enable all logging whitelists (equivalent to '--log-whitelist-*=*')\n    #[arg(long = \"log-whitelist-all\")]\n    log_whitelist_all: bool,\n    /// Comma-separated list for client-type whitelist\n    #[arg(long = \"log-whitelist-client-type\")]\n    log_whitelist_client_type: Option<String>,\n    /// Comma-separated list for node-resolution-type whitelist\n    #[arg(long = \"log-whitelist-node-resolution-type\")]\n    log_whitelist_node_resolution_type: Option<String>,\n    /// Comma-separated list for channel whitelist\n    #[arg(long = \"log-whitelist-channel\")]\n    log_whitelist_channel: Option<String>,\n    /// Comma-separated list for ingest whitelist\n    #[arg(long = \"log-whitelist-ingest\")]\n    log_whitelist_ingest: Option<String>,\n    /// Comma-separated list for matching whitelist\n    #[arg(long = \"log-whitelist-matching\")]\n    log_whitelist_matching: Option<String>,\n}\n\nfn default_config() -> FfiReducerConfig {\n    FfiReducerConfig {\n        telemetry_port: 8000,\n\n        num_ingest_shards: 1,\n        num_matching_shards: 1,\n        num_aggregation_shards: 1,\n        partitions_per_shard: 1,\n\n        enable_id_id: false,\n        enable_az_id: false,\n        enable_flow_logs: false,\n\n        enable_otlp_grpc_metrics: false,\n        otlp_grpc_metrics_address: \"localhost\".into(),\n        otlp_grpc_metrics_port: 4317,\n        otlp_grpc_batch_size: 1000,\n        enable_otlp_grpc_metric_descriptions: false,\n\n        disable_prometheus_metrics: false,\n        shard_prometheus_metrics: false,\n        prom_bind: \"127.0.0.1:7010\".into(),\n        scrape_size_limit_bytes: 0,\n        internal_prom_bind: \"0.0.0.0:7001\".into(),\n        stats_scrape_size_limit_bytes: 0,\n        scrape_metrics_tsdb_format: TsdbFormat::prometheus,\n\n        disable_node_ip_field: false,\n        enable_autonomous_system_ip: false,\n\n        geoip_path: String::new(),\n\n        enable_aws_enrichment: false,\n        enable_percentile_latencies: false,\n\n        disable_metrics: String::new(),\n        enable_metrics: String::new(),\n\n        index_dump_interval: 0,\n\n        log_whitelist_all: false,\n        log_whitelist_client_type: String::new(),\n        log_whitelist_node_resolution_type: String::new(),\n        log_whitelist_channel: String::new(),\n        log_whitelist_ingest: String::new(),\n        log_whitelist_matching: String::new(),\n    }\n}\n\nfn parse_tsdb_format(s: &str) -> Result<TsdbFormat, String> {\n    match s.to_ascii_lowercase().as_str() {\n        \"prometheus\" => Ok(TsdbFormat::prometheus),\n        \"json\" => Ok(TsdbFormat::json),\n        other => Err(format!(\n            \"Invalid TSDB format for scraped metrics: {}. Supported formats: prometheus, json\",\n            other\n        )),\n    }\n}\n\nfn build_final_config(cli: &Cli) -> Result<FfiReducerConfig, String> {\n    let mut cfg = default_config();\n\n    if let Some(v) = cli.port {\n        cfg.telemetry_port = v;\n    }\n\n    if let Some(v) = cli.num_ingest_shards {\n        cfg.num_ingest_shards = v;\n    }\n    if let Some(v) = cli.num_matching_shards {\n        cfg.num_matching_shards = v;\n    }\n    if let Some(v) = cli.num_aggregation_shards {\n        cfg.num_aggregation_shards = v;\n    }\n    if let Some(v) = cli.partitions_per_shard {\n        cfg.partitions_per_shard = v;\n    }\n\n    cfg.enable_id_id |= cli.enable_id_id;\n    cfg.enable_az_id |= cli.enable_az_id;\n    cfg.enable_flow_logs |= cli.enable_flow_logs;\n\n    cfg.enable_otlp_grpc_metrics |= cli.enable_otlp_grpc_metrics;\n    cfg.otlp_grpc_metrics_address = cli.otlp_grpc_metrics_address.clone();\n    if let Some(v) = cli.otlp_grpc_metrics_port {\n        cfg.otlp_grpc_metrics_port = v;\n    }\n    if let Some(v) = cli.otlp_grpc_batch_size {\n        cfg.otlp_grpc_batch_size = v;\n    }\n    cfg.enable_otlp_grpc_metric_descriptions |= cli.enable_otlp_grpc_metric_descriptions;\n\n    cfg.disable_prometheus_metrics |= cli.disable_prometheus_metrics;\n    cfg.shard_prometheus_metrics |= cli.shard_prometheus_metrics;\n    if let Some(v) = &cli.prom_bind {\n        cfg.prom_bind = v.clone();\n    }\n    if let Some(v) = cli.scrape_size_limit_bytes {\n        cfg.scrape_size_limit_bytes = v;\n    }\n    if let Some(v) = &cli.internal_prom_bind {\n        cfg.internal_prom_bind = v.clone();\n    }\n\n    // stats limit mirrors scrape limit when not provided\n    cfg.stats_scrape_size_limit_bytes = match cli.stats_scrape_size_limit_bytes {\n        Some(v) => v,\n        None => cfg.scrape_size_limit_bytes,\n    };\n\n    cfg.scrape_metrics_tsdb_format = parse_tsdb_format(&cli.metrics_tsdb_format)?;\n\n    cfg.disable_node_ip_field |= cli.disable_node_ip_field;\n    cfg.enable_autonomous_system_ip |= cli.enable_autonomous_system_ip;\n\n    // geoip from env, empty => unset\n    if let Ok(val) = std::env::var(\"GEOIP_PATH\") {\n        if !val.is_empty() {\n            cfg.geoip_path = val;\n        }\n    }\n\n    cfg.enable_aws_enrichment |= cli.enable_aws_enrichment;\n    cfg.enable_percentile_latencies |= cli.enable_percentile_latencies;\n\n    if let Some(v) = &cli.disable_metrics {\n        cfg.disable_metrics = v.clone();\n    }\n    if let Some(v) = &cli.enable_metrics {\n        cfg.enable_metrics = v.clone();\n    }\n\n    if let Some(v) = cli.index_dump_interval {\n        cfg.index_dump_interval = v;\n    }\n\n    // Logging whitelist pass-through (strings and all-flag)\n    cfg.log_whitelist_all |= cli.log_whitelist_all;\n    if let Some(v) = &cli.log_whitelist_client_type {\n        cfg.log_whitelist_client_type = v.clone();\n    }\n    if let Some(v) = &cli.log_whitelist_node_resolution_type {\n        cfg.log_whitelist_node_resolution_type = v.clone();\n    }\n    if let Some(v) = &cli.log_whitelist_channel {\n        cfg.log_whitelist_channel = v.clone();\n    }\n    if let Some(v) = &cli.log_whitelist_ingest {\n        cfg.log_whitelist_ingest = v.clone();\n    }\n    if let Some(v) = &cli.log_whitelist_matching {\n        cfg.log_whitelist_matching = v.clone();\n    }\n\n    Ok(cfg)\n}\n\nfn to_string_tsdb(fmt: TsdbFormat) -> &'static str {\n    match fmt {\n        TsdbFormat::prometheus => \"prometheus\",\n        TsdbFormat::json => \"json\",\n        TsdbFormat::otlp_grpc => \"otlp_grpc\",\n        // cxx::bridge enums are repr types; be exhaustive defensively\n        _ => \"prometheus\",\n    }\n}\n\nfn print_config(cfg: &FfiReducerConfig) {\n    // Mirror reducer/reducer_config.inl printing semantics\n    println!(\"telemetry_port: {}\", cfg.telemetry_port);\n    println!(\"num_ingest_shards: {}\", cfg.num_ingest_shards);\n    println!(\"num_matching_shards: {}\", cfg.num_matching_shards);\n    println!(\"num_aggregation_shards: {}\", cfg.num_aggregation_shards);\n    println!(\"partitions_per_shard: {}\", cfg.partitions_per_shard);\n    println!(\"enable_id_id: {}\", cfg.enable_id_id);\n    println!(\"enable_az_id: {}\", cfg.enable_az_id);\n    println!(\"enable_flow_logs: {}\", cfg.enable_flow_logs);\n    println!(\"enable_otlp_grpc_metrics: {}\", cfg.enable_otlp_grpc_metrics);\n    println!(\n        \"otlp_grpc_metrics_address: {}\",\n        cfg.otlp_grpc_metrics_address\n    );\n    println!(\"otlp_grpc_metrics_port: {}\", cfg.otlp_grpc_metrics_port);\n    println!(\"otlp_grpc_batch_size: {}\", cfg.otlp_grpc_batch_size);\n    println!(\n        \"enable_otlp_grpc_metric_descriptions: {}\",\n        cfg.enable_otlp_grpc_metric_descriptions\n    );\n    println!(\n        \"disable_prometheus_metrics: {}\",\n        cfg.disable_prometheus_metrics\n    );\n    println!(\"shard_prometheus_metrics: {}\", cfg.shard_prometheus_metrics);\n    println!(\"prom_bind: {}\", cfg.prom_bind);\n    println!(\"internal_prom_bind: {}\", cfg.internal_prom_bind);\n\n    if cfg.scrape_size_limit_bytes == 0 {\n        println!(\"scrape_size_limit_bytes: unlimited\");\n    } else {\n        println!(\"scrape_size_limit_bytes: {}\", cfg.scrape_size_limit_bytes);\n    }\n\n    if cfg.stats_scrape_size_limit_bytes == 0 {\n        println!(\"stats_scrape_size_limit_bytes: unlimited\");\n    } else {\n        println!(\n            \"stats_scrape_size_limit_bytes: {}\",\n            cfg.stats_scrape_size_limit_bytes\n        );\n    }\n\n    println!(\n        \"scrape_metrics_tsdb_format: {}\",\n        to_string_tsdb(cfg.scrape_metrics_tsdb_format)\n    );\n    println!(\"disable_node_ip_field: {}\", cfg.disable_node_ip_field);\n    println!(\n        \"enable_autonomous_system_ip: {}\",\n        cfg.enable_autonomous_system_ip\n    );\n    println!(\n        \"geoip_path: {}\",\n        if cfg.geoip_path.is_empty() {\n            \"none\"\n        } else {\n            &cfg.geoip_path\n        }\n    );\n    println!(\"enable_aws_enrichment: {}\", cfg.enable_aws_enrichment);\n    println!(\n        \"enable_percentile_latencies: {}\",\n        cfg.enable_percentile_latencies\n    );\n    println!(\"disable_metrics: {}\", cfg.disable_metrics);\n    println!(\"enable_metrics: {}\", cfg.enable_metrics);\n    println!(\"index_dump_interval: {}\", cfg.index_dump_interval);\n}\n\npub fn run_with_env_args() -> i32 {\n    let cli = Cli::parse();\n\n    // Initialize logging similar to legacy C++ ArgsParser handler\n    unsafe {\n        reducer_sys::ffi::otn_init_logging(cli.log_console, cli.no_log_file);\n        // Apply explicit level only if any of the level flags are set\n        let mut set_level = None;\n        if cli.trace {\n            set_level = Some(0);\n        } else if cli.debug {\n            set_level = Some(1);\n        } else if cli.info {\n            set_level = Some(2);\n        } else if cli.warning {\n            set_level = Some(3);\n        } else if cli.error {\n            set_level = Some(4);\n        } else if cli.critical {\n            set_level = Some(5);\n        }\n        if let Some(code) = set_level {\n            reducer_sys::ffi::otn_set_log_level(code);\n        }\n    }\n\n    let cfg = match build_final_config(&cli) {\n        Ok(c) => c,\n        Err(e) => {\n            eprintln!(\"{}\", e);\n            return 1;\n        }\n    };\n\n    if cli.print_config {\n        print_config(&cfg);\n        return 0;\n    }\n\n    unsafe { reducer_sys::ffi::otn_reducer_main_with_config(&cfg) }\n}\n"
  },
  {
    "path": "crates/reducer/src/metrics.rs",
    "content": "//! Protocol metric structs and simple fold helpers.\n\n#[derive(Default, Debug, Clone, PartialEq, Eq)]\npub struct TcpMetrics {\n    pub active_sockets: u64,\n    pub sum_bytes: u64,\n    pub sum_srtt: u64,\n    pub sum_delivered: u64,\n    pub sum_retrans: u64,\n    pub active_rtts: u64,\n    pub syn_timeouts: u64,\n    pub new_sockets: u64,\n    pub tcp_resets: u64,\n}\n\nimpl TcpMetrics {\n    pub fn add_from(&mut self, other: &Self) {\n        self.active_sockets += other.active_sockets;\n        self.sum_bytes += other.sum_bytes;\n        self.sum_srtt += other.sum_srtt;\n        self.sum_delivered += other.sum_delivered;\n        self.sum_retrans += other.sum_retrans;\n        self.active_rtts += other.active_rtts;\n        self.syn_timeouts += other.syn_timeouts;\n        self.new_sockets += other.new_sockets;\n        self.tcp_resets += other.tcp_resets;\n    }\n    pub fn is_zero(&self) -> bool {\n        // Consider only whether there were any samples this window\n        self.active_sockets == 0\n    }\n}\n\n#[derive(Default, Debug, Clone, PartialEq, Eq)]\npub struct UdpMetrics {\n    pub active_sockets: u64,\n    pub bytes: u64,\n    pub addr_changes: u64,\n    pub packets: u64,\n    pub drops: u64,\n}\n\nimpl UdpMetrics {\n    pub fn add_from(&mut self, other: &Self) {\n        self.active_sockets += other.active_sockets;\n        self.bytes += other.bytes;\n        self.addr_changes += other.addr_changes;\n        self.packets += other.packets;\n        self.drops += other.drops;\n    }\n    pub fn is_zero(&self) -> bool {\n        self.active_sockets == 0\n    }\n}\n\n#[derive(Default, Debug, Clone, PartialEq, Eq)]\npub struct HttpMetrics {\n    pub active_sockets: u64,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub sum_code_200: u64,\n    pub sum_code_400: u64,\n    pub sum_code_500: u64,\n    pub sum_code_other: u64,\n}\n\nimpl HttpMetrics {\n    pub fn add_from(&mut self, other: &Self) {\n        self.active_sockets += other.active_sockets;\n        self.sum_total_time_ns += other.sum_total_time_ns;\n        self.sum_processing_time_ns += other.sum_processing_time_ns;\n        self.sum_code_200 += other.sum_code_200;\n        self.sum_code_400 += other.sum_code_400;\n        self.sum_code_500 += other.sum_code_500;\n        self.sum_code_other += other.sum_code_other;\n    }\n    pub fn is_zero(&self) -> bool {\n        self.active_sockets == 0\n    }\n}\n\n#[derive(Default, Debug, Clone, PartialEq, Eq)]\npub struct DnsMetrics {\n    pub active_sockets: u64,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub requests_a: u64,\n    pub requests_aaaa: u64,\n    pub responses: u64,\n    pub timeouts: u64,\n}\n\nimpl DnsMetrics {\n    pub fn add_from(&mut self, other: &Self) {\n        self.active_sockets += other.active_sockets;\n        self.sum_total_time_ns += other.sum_total_time_ns;\n        self.sum_processing_time_ns += other.sum_processing_time_ns;\n        self.requests_a += other.requests_a;\n        self.requests_aaaa += other.requests_aaaa;\n        self.responses += other.responses;\n        self.timeouts += other.timeouts;\n    }\n    pub fn is_zero(&self) -> bool {\n        self.active_sockets == 0\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/otlp_encoding.rs",
    "content": "//! OTLP encoding helpers (stubbed in this iteration).\n\nuse crate::aggregator::{AllMetrics, Az, Node};\nuse crate::metrics::{DnsMetrics, HttpMetrics, TcpMetrics, UdpMetrics};\nuse otlp_export::ffi::{Label as OLabel, MetricKind, PublisherStats};\nuse otlp_export::{otlp_publisher_new, Publisher};\n\n// Canonical metric descriptions (parity with C++ MetricInfo).\nconst DESC_TCP_BYTES: &str =\n    \"The total number of TCP bytes between the source and destination measured for the prior thirty seconds.\";\nconst DESC_TCP_RTT_NUM: &str =\n    \"The number of measurements made in calculating the current RTT average value.\";\nconst DESC_TCP_ACTIVE: &str =\n    \"The number of TCP connections considered to be open and alive between the source and destination at the point the measurement was taken.\";\nconst DESC_TCP_RTT_AVG: &str =\n    \"The computed average round trip time between the source and destination as measured in microseconds.\";\nconst DESC_TCP_PACKETS: &str =\n    \"The total number of TCP packets between the source and destination measured for the prior thirty seconds.\";\nconst DESC_TCP_RETRANS: &str =\n    \"The total number of TCP retransmission requests between the source and destination measured for the prior thirty seconds.\";\nconst DESC_TCP_SYN_TIMEOUTS: &str =\n    \"The total number of TCP SYN timeouts between the source and destination measured for the prior thirty seconds.\";\nconst DESC_TCP_NEW_SOCKETS: &str =\n    \"The total number of new TCP sockets opened between the source and destination measured for the prior thirty seconds.\";\nconst DESC_TCP_RESETS: &str =\n    \"The total number of TCP resets sent between the source and destination measured for the prior thirty seconds.\";\n\nconst DESC_UDP_BYTES: &str =\n    \"The total number of UDP bytes between the source and destination measured for the prior thirty seconds.\";\nconst DESC_UDP_PACKETS: &str =\n    \"The total number of UDP packets between the source and destination measured for the prior thirty seconds.\";\nconst DESC_UDP_ACTIVE: &str =\n    \"The number of UDP connections considered to be open and alive between the source and destination at the point the measurement was taken.\";\nconst DESC_UDP_DROPS: &str =\n    \"The total number of UDP connections dropped between the source and destination measured for the prior thirty seconds.\";\n\nconst DESC_DNS_CLIENT_AVG: &str =\n    \"This metric is the average duration in microseconds from when the client sends a DNS request, until the response is received back from the server. As such, it includes the communication round-trip times, plus the server processing latency. Computed by the summation of all times, divided by dns.responses.\";\nconst DESC_DNS_SERVER_AVG: &str =\n    \"This metric is the average duration in microseconds for the server to respond to a request received locally. Thus, it does not include the network latency from or to the client. Computed by the summation of all times, divided by dns.responses.\";\nconst DESC_DNS_ACTIVE: &str =\n    \"The number of DNS connections for which measurements were taken in the prior thirty seconds.\";\nconst DESC_DNS_RESPONSES: &str =\n    \"The total number of DNS responses sent between the source and destination measured for the prior thirty seconds.\";\nconst DESC_DNS_TIMEOUTS: &str =\n    \"The total number of DNS timeouts between the source and destination measured for the prior thirty seconds.\";\n\nconst DESC_HTTP_CLIENT_AVG: &str =\n    \"This metric is the average duration in microseconds from when the client sends an HTTP request, until the response is received back from the server. As such, it includes the communication round-trip times, plus the server processing latency. Computed by summation of all times, divided by http.active_sockets.\";\nconst DESC_HTTP_SERVER_AVG: &str =\n    \"This metric is the average duration in microseconds for the server to respond to a request received locally. Thus, it does not include the network latency from or to the client. Computed by summation of all times, divided by http.active_sockets.\";\nconst DESC_HTTP_ACTIVE: &str =\n    \"The number of unencrypted HTTPv1 connections for which measurements were taken in the prior thirty seconds.\";\nconst DESC_HTTP_STATUS: &str =\n    \"For a given class of response code (see 'response_code' dimension), the number of times an unencrypted server sent an HTTPv1 status code between the source and destination measured for the prior thirty seconds.\";\n\n// Emit-ready labels vector; build these in aggregator to avoid per-call conversions.\npub type Labels = Vec<OLabel>;\n// Map numeric node_type to C++ NodeResolutionType strings.\npub fn resolution_type_string(node_type: u8) -> &'static str {\n    match node_type {\n        0 => \"NONE\",\n        1 => \"IP\",\n        2 => \"DNS\",\n        3 => \"AWS\",\n        4 => \"INSTANCE_METADATA\",\n        5 => \"PROCESS\",\n        6 => \"LOCALHOST\",\n        7 => \"K8S_CONTAINER\",\n        8 => \"CONTAINER\",\n        9 => \"NOMAD\",\n        _ => \"\",\n    }\n}\n\n// Build \"source.\" or \"dest.\" labels union for a Node+Az pair (some fields may be missing).\npub fn add_node_labels(prefix: &str, node: Option<&Node>, az: Option<&Az>, out: &mut Labels) {\n    let p = |k: &str| format!(\"{}{}\", prefix, k);\n    if let Some(az) = az {\n        out.push(OLabel {\n            key: p(\"workload.name\"),\n            value: az.role.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"workload.uid\"),\n            value: az.role_uid.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"availability_zone\"),\n            value: az.az.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"resolution_type\"),\n            value: resolution_type_string(az.node_type).to_string(),\n        });\n        out.push(OLabel {\n            key: p(\"image_version\"),\n            value: az.version.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"environment\"),\n            value: az.env.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"namespace.name\"),\n            value: az.ns.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"process.name\"),\n            value: az.process.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"container.name\"),\n            value: az.container.clone(),\n        });\n    }\n    if let Some(node) = node {\n        out.push(OLabel {\n            key: p(\"id\"),\n            value: node.id.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"ip\"),\n            value: node.address.clone(),\n        });\n        out.push(OLabel {\n            key: p(\"pod\"),\n            value: node.pod_name.clone(),\n        });\n    }\n}\n\npub const LABEL_SF_PRODUCT: &str = \"sf_product\";\npub const LABEL_SF_PRODUCT_VALUE: &str = \"network-explorer\";\npub const LABEL_AGGREGATION: &str = \"aggregation\";\n\n// Minimal exporter stub for this iteration.\npub struct OtlpExporter {\n    pub enable_descriptions: bool,\n    pub endpoint: String,\n    pub publisher: Box<Publisher>,\n}\n\nimpl Default for OtlpExporter {\n    fn default() -> Self {\n        // Default to localhost:4317 as in default reducer config.\n        let endpoint = \"http://localhost:4317\".to_string();\n        let publisher = otlp_publisher_new(&endpoint);\n        Self {\n            enable_descriptions: false,\n            endpoint,\n            publisher,\n        }\n    }\n}\n\nimpl OtlpExporter {\n    pub fn with_endpoint(endpoint: String, enable_descriptions: bool) -> Self {\n        let publisher = otlp_publisher_new(&endpoint);\n        Self {\n            enable_descriptions,\n            endpoint,\n            publisher,\n        }\n    }\n    pub fn new_local(enable_descriptions: bool) -> Self {\n        // Same endpoint as Default but allows control over descriptions at construction.\n        let endpoint = \"http://localhost:4317\".to_string();\n        let publisher = otlp_publisher_new(&endpoint);\n        Self {\n            enable_descriptions,\n            endpoint,\n            publisher,\n        }\n    }\n    fn publish_u64(\n        &mut self,\n        name: &str,\n        unit: &str,\n        desc: &str,\n        kind: MetricKind,\n        labels: &Labels,\n        ts_ns: i64,\n        v: u64,\n    ) {\n        self.publisher.publish_metric_u64(\n            name,\n            unit,\n            if self.enable_descriptions { desc } else { \"\" },\n            kind,\n            labels,\n            ts_ns,\n            v,\n        );\n    }\n    fn publish_f64(\n        &mut self,\n        name: &str,\n        unit: &str,\n        desc: &str,\n        kind: MetricKind,\n        labels: &Labels,\n        ts_ns: i64,\n        v: f64,\n    ) {\n        self.publisher.publish_metric_f64(\n            name,\n            unit,\n            if self.enable_descriptions { desc } else { \"\" },\n            kind,\n            labels,\n            ts_ns,\n            v,\n        );\n    }\n\n    // New per-protocol emitters\n    pub fn emit_tcp(&mut self, ts: i64, labels: &Labels, tcp: &TcpMetrics) {\n        self.publish_u64(\n            \"tcp.bytes\",\n            \"By\",\n            DESC_TCP_BYTES,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.sum_bytes,\n        );\n        self.publish_u64(\n            \"tcp.rtt.num_measurements\",\n            \"1\",\n            DESC_TCP_RTT_NUM,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            tcp.active_rtts,\n        );\n        self.publish_u64(\n            \"tcp.active\",\n            \"1\",\n            DESC_TCP_ACTIVE,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            tcp.active_sockets,\n        );\n        let avg_us = if tcp.active_rtts > 0 {\n            (tcp.sum_srtt as f64) / 8.0 / 1_000_000.0 / (tcp.active_rtts as f64)\n        } else {\n            0.0\n        };\n        self.publish_f64(\n            \"tcp.rtt.average\",\n            \"us\",\n            DESC_TCP_RTT_AVG,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            avg_us,\n        );\n        self.publish_u64(\n            \"tcp.packets\",\n            \"1\",\n            DESC_TCP_PACKETS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.sum_delivered,\n        );\n        self.publish_u64(\n            \"tcp.retrans\",\n            \"1\",\n            DESC_TCP_RETRANS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.sum_retrans,\n        );\n        self.publish_u64(\n            \"tcp.syn_timeouts\",\n            \"1\",\n            DESC_TCP_SYN_TIMEOUTS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.syn_timeouts,\n        );\n        self.publish_u64(\n            \"tcp.new_sockets\",\n            \"1\",\n            DESC_TCP_NEW_SOCKETS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.new_sockets,\n        );\n        self.publish_u64(\n            \"tcp.resets\",\n            \"1\",\n            DESC_TCP_RESETS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            tcp.tcp_resets,\n        );\n    }\n\n    pub fn emit_udp(&mut self, ts: i64, labels: &Labels, udp: &UdpMetrics) {\n        self.publish_u64(\n            \"udp.bytes\",\n            \"By\",\n            DESC_UDP_BYTES,\n            MetricKind::Sum,\n            labels,\n            ts,\n            udp.bytes,\n        );\n        self.publish_u64(\n            \"udp.packets\",\n            \"1\",\n            DESC_UDP_PACKETS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            udp.packets,\n        );\n        self.publish_u64(\n            \"udp.active\",\n            \"1\",\n            DESC_UDP_ACTIVE,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            udp.active_sockets,\n        );\n        self.publish_u64(\n            \"udp.drops\",\n            \"1\",\n            DESC_UDP_DROPS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            udp.drops,\n        );\n    }\n\n    pub fn emit_dns(&mut self, ts: i64, labels: &Labels, dns: &DnsMetrics) {\n        self.publish_u64(\n            \"dns.active_sockets\",\n            \"1\",\n            DESC_DNS_ACTIVE,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            dns.active_sockets,\n        );\n        self.publish_u64(\n            \"dns.responses\",\n            \"1\",\n            DESC_DNS_RESPONSES,\n            MetricKind::Sum,\n            labels,\n            ts,\n            dns.responses,\n        );\n        let avg_client_us = if dns.responses > 0 {\n            (dns.sum_total_time_ns as f64) / 1_000_000_000.0 / (dns.responses as f64)\n        } else {\n            0.0\n        };\n        let avg_server_us = if dns.responses > 0 {\n            (dns.sum_processing_time_ns as f64) / 1_000_000_000.0 / (dns.responses as f64)\n        } else {\n            0.0\n        };\n        self.publish_f64(\n            \"dns.client.duration.average\",\n            \"us\",\n            DESC_DNS_CLIENT_AVG,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            avg_client_us,\n        );\n        self.publish_f64(\n            \"dns.server.duration.average\",\n            \"us\",\n            DESC_DNS_SERVER_AVG,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            avg_server_us,\n        );\n        self.publish_u64(\n            \"dns.timeouts\",\n            \"1\",\n            DESC_DNS_TIMEOUTS,\n            MetricKind::Sum,\n            labels,\n            ts,\n            dns.timeouts,\n        );\n    }\n\n    pub fn emit_http(&mut self, ts: i64, labels: &Labels, http: &HttpMetrics) {\n        self.publish_u64(\n            \"http.active_sockets\",\n            \"1\",\n            DESC_HTTP_ACTIVE,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            http.active_sockets,\n        );\n        // Requests (sum of codes)\n        let total = http.sum_code_200 + http.sum_code_400 + http.sum_code_500 + http.sum_code_other;\n        self.publish_u64(\"http.requests\", \"1\", \"\", MetricKind::Sum, labels, ts, total);\n        // Average durations per request (0 when no requests)\n        let avg_client_us = if total > 0 {\n            (http.sum_total_time_ns as f64) / 1_000_000.0 / (total as f64)\n        } else {\n            0.0\n        };\n        let avg_server_us = if total > 0 {\n            (http.sum_processing_time_ns as f64) / 1_000_000.0 / (total as f64)\n        } else {\n            0.0\n        };\n        self.publish_f64(\n            \"http.client.duration.average\",\n            \"us\",\n            DESC_HTTP_CLIENT_AVG,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            avg_client_us,\n        );\n        self.publish_f64(\n            \"http.server.duration.average\",\n            \"us\",\n            DESC_HTTP_SERVER_AVG,\n            MetricKind::Gauge,\n            labels,\n            ts,\n            avg_server_us,\n        );\n        // Status codes with label\n        let mut l2 = labels.clone();\n        l2.push(OLabel {\n            key: \"status_code\".to_string(),\n            value: \"200\".to_string(),\n        });\n        self.publish_u64(\n            \"http.status_code\",\n            \"1\",\n            DESC_HTTP_STATUS,\n            MetricKind::Sum,\n            &l2,\n            ts,\n            http.sum_code_200,\n        );\n        let mut l3 = labels.clone();\n        l3.push(OLabel {\n            key: \"status_code\".to_string(),\n            value: \"400\".to_string(),\n        });\n        self.publish_u64(\n            \"http.status_code\",\n            \"1\",\n            DESC_HTTP_STATUS,\n            MetricKind::Sum,\n            &l3,\n            ts,\n            http.sum_code_400,\n        );\n        let mut l4 = labels.clone();\n        l4.push(OLabel {\n            key: \"status_code\".to_string(),\n            value: \"500\".to_string(),\n        });\n        self.publish_u64(\n            \"http.status_code\",\n            \"1\",\n            DESC_HTTP_STATUS,\n            MetricKind::Sum,\n            &l4,\n            ts,\n            http.sum_code_500,\n        );\n        let mut l5 = labels.clone();\n        l5.push(OLabel {\n            key: \"status_code\".to_string(),\n            value: \"other\".to_string(),\n        });\n        self.publish_u64(\n            \"http.status_code\",\n            \"1\",\n            DESC_HTTP_STATUS,\n            MetricKind::Sum,\n            &l5,\n            ts,\n            http.sum_code_other,\n        );\n    }\n\n    // Emit a full AllMetrics, gating per protocol using prev-window flags.\n    pub fn emit_all_metrics(&mut self, ts: i64, labels: &Labels, m: &AllMetrics) {\n        if !m.tcp.is_zero() || m.tcp_prev_window_had_samples {\n            self.emit_tcp(ts, labels, &m.tcp);\n        }\n        if !m.udp.is_zero() || m.udp_prev_window_had_samples {\n            self.emit_udp(ts, labels, &m.udp);\n        }\n        if !m.http.is_zero() || m.http_prev_window_had_samples {\n            self.emit_http(ts, labels, &m.http);\n        }\n        if !m.dns.is_zero() || m.dns_prev_window_had_samples {\n            self.emit_dns(ts, labels, &m.dns);\n        }\n    }\n\n    pub fn flush(&mut self) {\n        self.publisher.flush();\n    }\n    pub fn stats(&self) -> PublisherStats {\n        self.publisher.stats()\n    }\n}\n"
  },
  {
    "path": "crates/reducer/src/queue_handler.rs",
    "content": "use std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::time::{Duration, Instant};\n\nuse element_queue::ElementQueue;\nuse timeslot::virtual_clock::VirtualClock;\nuse timeslot::FastDiv;\n\n// Keep batch size reasonable to avoid starving other work\nconst K_MAX_RPC_BATCH_PER_QUEUE: usize = 10_000;\n\n/// Drives reading from element queues and advancing a virtual clock, invoking\n/// user-provided callbacks for each message and at the end of each timeslot.\npub struct QueueHandler {\n    queues: Vec<ElementQueue>,\n    clock: VirtualClock,\n    timeslot_div: FastDiv,\n    stop: Arc<AtomicBool>,\n    last_processed_ts: u64,\n}\n\nimpl QueueHandler {\n    /// Construct from contiguous element-queue descriptors and a shared stop flag.\n    pub fn new_from_views(eq_views: &[(usize, u32, u32)], stop: Arc<AtomicBool>) -> Self {\n        // Build queues from contiguous storage descriptors\n        let mut queues = Vec::with_capacity(eq_views.len());\n        for (data, n_elems, buf_len) in eq_views.iter().cloned() {\n            let ptr = data as *mut u8;\n            let q = unsafe { ElementQueue::new_from_contiguous(n_elems, buf_len, ptr) }\n                .expect(\"failed to create ElementQueue from contiguous storage\");\n            queues.push(q);\n        }\n\n        // Virtual clock configured with 30s timeslots (approximate)\n        let timeslot_div = FastDiv::new(30e9_f64, 16);\n        let mut clock = VirtualClock::new(timeslot_div.clone());\n        clock.add_inputs(queues.len());\n\n        Self {\n            queues,\n            clock,\n            timeslot_div,\n            stop,\n            last_processed_ts: 0,\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.queues.is_empty()\n    }\n\n    /// Run the queue handling loop until `stop` is set.\n    ///\n    /// - `handle_message(queue_idx, bytes)` is invoked for each element that\n    ///   falls into the current timeslot.\n    /// - `handle_timeslot_end(window_end_ns)` is invoked every time the clock\n    ///   advances past the current timeslot; `window_end_ns` is aligned to the\n    ///   configured timeslot size using an approximate divider.\n    pub fn run<HM, HT>(&mut self, mut handle_message: HM, mut handle_timeslot_end: HT)\n    where\n        HM: FnMut(usize, &[u8]),\n        HT: FnMut(u64),\n    {\n        if self.queues.is_empty() {\n            return;\n        }\n\n        let mut next_idx: usize = 0;\n        let time_budget = Duration::from_millis(20);\n\n        while !self.stop.load(Ordering::Relaxed) {\n            let start_cycle = Instant::now();\n\n            for _ in 0..self.queues.len() {\n                let i = next_idx;\n                next_idx = (next_idx + 1) % self.queues.len();\n\n                if !self.clock.can_update(i) {\n                    continue;\n                }\n\n                // RAII read guard\n                let rb = self.queues[i].start_read();\n                let mut handled_in_queue = 0usize;\n\n                while handled_in_queue < K_MAX_RPC_BATCH_PER_QUEUE\n                    && self.clock.can_update(i)\n                    && rb.peek_len().is_ok()\n                {\n                    // Peek timestamp (native-endian u64 at start of element)\n                    let ts = match rb.peek_value::<u64>() {\n                        Ok(v) => v,\n                        Err(_e) => {\n                            // Drain malformed element and continue\n                            let _ = rb.read();\n                            continue;\n                        }\n                    };\n\n                    // Update clock for this input\n                    match self.clock.update(i, ts) {\n                        Ok(()) => {}\n                        Err(timeslot::virtual_clock::UpdateError::PastTimeslot) => {\n                            // Drain and continue\n                            let _ = rb.read();\n                            continue;\n                        }\n                        Err(timeslot::virtual_clock::UpdateError::NotPermitted) => {\n                            break;\n                        }\n                    }\n\n                    if self.clock.is_current(i) {\n                        match rb.read() {\n                            Ok(bytes) => {\n                                handle_message(i, bytes);\n                                // Track last processed timestamp while in current slot\n                                self.last_processed_ts = ts;\n                            }\n                            Err(_e) => break,\n                        }\n                        handled_in_queue += 1;\n                    }\n\n                    if start_cycle.elapsed() >= time_budget {\n                        break; // yield and rotate queues\n                    }\n                }\n\n                // Publish read heads\n                let _ = rb.finish();\n            }\n\n            if self.clock.advance() {\n                // Compute the window end timestamp aligned to the 30s slot\n                let slot_ns = self.timeslot_div.estimated_reciprocal().round() as u64;\n                let rem = self.timeslot_div.remainder(self.last_processed_ts, slot_ns);\n                let window_end_ns = self\n                    .last_processed_ts\n                    .saturating_sub(rem)\n                    .saturating_add(slot_ns);\n                handle_timeslot_end(window_end_ns);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/reducer-bin/Cargo.toml",
    "content": "[package]\nname = \"reducer-bin\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nreducer-sys = { path = \"../reducer-sys\" }\nreducer = { workspace = true }\n\n[[bin]]\nname = \"reducer\"\npath = \"src/main.rs\"\n"
  },
  {
    "path": "crates/reducer-bin/src/main.rs",
    "content": "fn main() {\n    let code = reducer::run_with_env_args();\n    std::process::exit(code);\n}\n"
  },
  {
    "path": "crates/reducer-sys/Cargo.toml",
    "content": "[package]\nname = \"reducer-sys\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\nname = \"reducer_sys\"\npath = \"src/lib.rs\"\n\n[dependencies]\ncxx = \"1\"\nencoder_ebpf_net_ingest = { path = \"../render/ebpf_net/ingest\" }\nencoder_ebpf_net_matching = { path = \"../render/ebpf_net/matching\" }\nencoder_ebpf_net_aggregation = { path = \"../render/ebpf_net/aggregation\" }\nencoder_ebpf_net_logging = { path = \"../render/ebpf_net/logging\" }\notlp_export = { workspace = true }\n"
  },
  {
    "path": "crates/reducer-sys/build.rs",
    "content": "include!(\"../build/otn_link_build.rs\");\n\nfn main() {\n    run();\n}\n"
  },
  {
    "path": "crates/reducer-sys/src/lib.rs",
    "content": "#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_aggregation;\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_ingest;\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_logging;\n#[allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_matching;\n#[allow(unused_extern_crates)]\nextern crate otlp_export;\n\n#[cxx::bridge(namespace = \"reducer_cfg\")]\npub mod ffi {\n    #[repr(u16)]\n    #[derive(Debug, Clone, Copy)]\n    pub enum TsdbFormat {\n        prometheus = 1,\n        json = 2,\n        otlp_grpc = 3,\n    }\n\n    /// Final, CXX-friendly reducer configuration.\n    /// Note: sentinel values encode optional fields:\n    /// - size limits: 0 = unlimited\n    /// - geoip_path: empty string = unset\n    #[derive(Debug)]\n    pub struct ReducerConfig {\n        pub telemetry_port: u32,\n\n        pub num_ingest_shards: u32,\n        pub num_matching_shards: u32,\n        pub num_aggregation_shards: u32,\n        pub partitions_per_shard: u32,\n\n        pub enable_id_id: bool,\n        pub enable_az_id: bool,\n        pub enable_flow_logs: bool,\n\n        pub enable_otlp_grpc_metrics: bool,\n        pub otlp_grpc_metrics_address: String,\n        pub otlp_grpc_metrics_port: u32,\n        pub otlp_grpc_batch_size: i32,\n        pub enable_otlp_grpc_metric_descriptions: bool,\n\n        pub disable_prometheus_metrics: bool,\n        pub shard_prometheus_metrics: bool,\n        pub prom_bind: String,\n        pub scrape_size_limit_bytes: u64, // 0 => unlimited\n        pub internal_prom_bind: String,\n        pub stats_scrape_size_limit_bytes: u64, // 0 => unlimited\n        pub scrape_metrics_tsdb_format: TsdbFormat,\n\n        pub disable_node_ip_field: bool,\n        pub enable_autonomous_system_ip: bool,\n\n        pub geoip_path: String, // empty => unset\n\n        pub enable_aws_enrichment: bool,\n        pub enable_percentile_latencies: bool,\n\n        pub disable_metrics: String,\n        pub enable_metrics: String,\n\n        pub index_dump_interval: u64,\n\n        // Logging whitelist controls\n        pub log_whitelist_all: bool,\n        pub log_whitelist_client_type: String,\n        pub log_whitelist_node_resolution_type: String,\n        pub log_whitelist_channel: String,\n        pub log_whitelist_ingest: String,\n        pub log_whitelist_matching: String,\n    }\n\n    extern \"C++\" {\n\n        include!(\"reducer/entrypoint.h\");\n\n        // Implemented in C++ (entrypoint.cc)\n        unsafe fn otn_reducer_main_with_config(cfg: &ReducerConfig) -> i32;\n\n        // Logging controls (implemented in C++)\n        unsafe fn otn_init_logging(log_console: bool, no_log_file: bool);\n        /// Level code mapping: 0=trace,1=debug,2=info,3=warning,4=error,5=critical\n        unsafe fn otn_set_log_level(level_code: i32);\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_all\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Aggregator remains a staticlib for C++ consumers; Rust binaries\n# link per-app crates directly via rlib dependencies.\ncrate-type = [\"staticlib\"]\n\n[dependencies]\nencoder_ebpf_net_agent_internal = { path = \"agent_internal\" }\nencoder_ebpf_net_ingest = { path = \"ingest\" }\nencoder_ebpf_net_matching = { path = \"matching\" }\nencoder_ebpf_net_aggregation = { path = \"aggregation\" }\nencoder_ebpf_net_kernel_collector = { path = \"kernel_collector\" }\nencoder_ebpf_net_cloud_collector = { path = \"cloud_collector\" }\nencoder_ebpf_net_logging = { path = \"logging\" }\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_agent_internal\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::agent_internal\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_dns_packet(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    pkt: JbBlob,\n    total_len: u16,\n    is_rx: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(pkt.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_pkt: &[u8] = unsafe { slice::from_raw_parts(pkt.buf as *const u8, pkt.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__dns_packet = jb_agent_internal__dns_packet {\n        _rpc_id: 331 as u16,\n        _len: __consumed as u16,\n        total_len,\n        is_rx,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_pkt.is_empty() {\n        let __len = __sl_pkt.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pkt);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_reset_tcp_counters(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    bytes_acked: u64,\n    packets_delivered: u32,\n    packets_retrans: u32,\n    bytes_received: u64,\n    pid: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 40 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__reset_tcp_counters = jb_agent_internal__reset_tcp_counters {\n        _rpc_id: 332 as u16,\n        packets_delivered,\n        sk,\n        bytes_acked,\n        bytes_received,\n        packets_retrans,\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_new_sock_created(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__new_sock_created = jb_agent_internal__new_sock_created {\n        _rpc_id: 333 as u16,\n        pid,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_udp_new_socket(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    sk: u64,\n    laddr: *const u8,\n    lport: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_laddr: &[u8] = unsafe { slice::from_raw_parts(laddr, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__udp_new_socket = jb_agent_internal__udp_new_socket {\n        _rpc_id: 334 as u16,\n        lport,\n        pid,\n        sk,\n        laddr: __sl_laddr.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_udp_destroy_socket(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__udp_destroy_socket = jb_agent_internal__udp_destroy_socket {\n        _rpc_id: 335 as u16,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_udp_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    raddr: *const u8,\n    packets: u32,\n    bytes: u32,\n    changed_af: u8,\n    rport: u16,\n    is_rx: u8,\n    laddr: *const u8,\n    lport: u16,\n    drops: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 60 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_raddr: &[u8] = unsafe { slice::from_raw_parts(raddr, 16) };\n    let __sl_laddr: &[u8] = unsafe { slice::from_raw_parts(laddr, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__udp_stats = jb_agent_internal__udp_stats {\n        _rpc_id: 336 as u16,\n        rport,\n        packets,\n        sk,\n        bytes,\n        drops,\n        lport,\n        raddr: __sl_raddr.try_into().unwrap(),\n        changed_af,\n        is_rx,\n        laddr: __sl_laddr.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 60 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 60 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_pid_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n    cgroup: u64,\n    parent_pid: i32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 36 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__pid_info = jb_agent_internal__pid_info {\n        _rpc_id: 337 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n        cgroup,\n        parent_pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_pid_close(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__pid_close = jb_agent_internal__pid_close {\n        _rpc_id: 338 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_pid_set_comm(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__pid_set_comm = jb_agent_internal__pid_set_comm {\n        _rpc_id: 371 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_set_state_ipv4(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    dest: u32,\n    src: u32,\n    dport: u16,\n    sport: u16,\n    sk: u64,\n    tx_rx: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 26 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__set_state_ipv4 = jb_agent_internal__set_state_ipv4 {\n        _rpc_id: 339 as u16,\n        dport,\n        dest,\n        sk,\n        src,\n        tx_rx,\n        sport,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 26 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 26 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_set_state_ipv6(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    dest: *const u8,\n    src: *const u8,\n    dport: u16,\n    sport: u16,\n    sk: u64,\n    tx_rx: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 50 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_dest: &[u8] = unsafe { slice::from_raw_parts(dest, 16) };\n    let __sl_src: &[u8] = unsafe { slice::from_raw_parts(src, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__set_state_ipv6 = jb_agent_internal__set_state_ipv6 {\n        _rpc_id: 340 as u16,\n        dport,\n        tx_rx,\n        sk,\n        sport,\n        dest: __sl_dest.try_into().unwrap(),\n        src: __sl_src.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 50 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 50 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_rtt_estimator(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    srtt: u32,\n    snd_cwnd: u32,\n    bytes_acked: u64,\n    ca_state: u8,\n    sk: u64,\n    packets_in_flight: u32,\n    packets_delivered: u32,\n    packets_retrans: u32,\n    rcv_holes: u32,\n    bytes_received: u64,\n    rcv_delivered: u32,\n    rcv_rtt: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 60 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__rtt_estimator = jb_agent_internal__rtt_estimator {\n        _rpc_id: 361 as u16,\n        ca_state,\n        srtt,\n        bytes_acked,\n        sk,\n        bytes_received,\n        snd_cwnd,\n        packets_in_flight,\n        packets_delivered,\n        packets_retrans,\n        rcv_holes,\n        rcv_delivered,\n        rcv_rtt,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 60 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 60 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_close_sock_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__close_sock_info = jb_agent_internal__close_sock_info {\n        _rpc_id: 362 as u16,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_kill_css(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cgroup_parent: u64,\n    name: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 280 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] = unsafe { slice::from_raw_parts(name, 256) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__kill_css = jb_agent_internal__kill_css {\n        _rpc_id: 363 as u16,\n        name: __sl_name.try_into().unwrap(),\n        cgroup,\n        cgroup_parent,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 280 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 280 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_css_populate_dir(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cgroup_parent: u64,\n    name: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 280 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] = unsafe { slice::from_raw_parts(name, 256) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__css_populate_dir = jb_agent_internal__css_populate_dir {\n        _rpc_id: 364 as u16,\n        name: __sl_name.try_into().unwrap(),\n        cgroup,\n        cgroup_parent,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 280 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 280 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_existing_cgroup_probe(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cgroup_parent: u64,\n    name: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 280 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] = unsafe { slice::from_raw_parts(name, 256) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__existing_cgroup_probe =\n        jb_agent_internal__existing_cgroup_probe {\n            _rpc_id: 365 as u16,\n            name: __sl_name.try_into().unwrap(),\n            cgroup,\n            cgroup_parent,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 280 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 280 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_cgroup_attach_task(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__cgroup_attach_task = jb_agent_internal__cgroup_attach_task {\n        _rpc_id: 366 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_nf_conntrack_alter_reply(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    ct: u64,\n    src_ip: u32,\n    src_port: u16,\n    dst_ip: u32,\n    dst_port: u16,\n    proto: u8,\n    nat_src_ip: u32,\n    nat_src_port: u16,\n    nat_dst_ip: u32,\n    nat_dst_port: u16,\n    nat_proto: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 36 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__nf_conntrack_alter_reply =\n        jb_agent_internal__nf_conntrack_alter_reply {\n            _rpc_id: 367 as u16,\n            src_port,\n            src_ip,\n            ct,\n            dst_ip,\n            nat_src_ip,\n            nat_dst_ip,\n            dst_port,\n            nat_src_port,\n            nat_dst_port,\n            proto,\n            nat_proto,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_nf_nat_cleanup_conntrack(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    ct: u64,\n    src_ip: u32,\n    src_port: u16,\n    dst_ip: u32,\n    dst_port: u16,\n    proto: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 23 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__nf_nat_cleanup_conntrack =\n        jb_agent_internal__nf_nat_cleanup_conntrack {\n            _rpc_id: 368 as u16,\n            src_port,\n            src_ip,\n            ct,\n            dst_ip,\n            dst_port,\n            proto,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 23 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 23 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_existing_conntrack_tuple(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    ct: u64,\n    src_ip: u32,\n    src_port: u16,\n    dst_ip: u32,\n    dst_port: u16,\n    proto: u8,\n    dir: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__existing_conntrack_tuple =\n        jb_agent_internal__existing_conntrack_tuple {\n            _rpc_id: 369 as u16,\n            src_port,\n            src_ip,\n            ct,\n            dst_ip,\n            dst_port,\n            proto,\n            dir,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_tcp_syn_timeout(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__tcp_syn_timeout = jb_agent_internal__tcp_syn_timeout {\n        _rpc_id: 370 as u16,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_http_response(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    pid: u32,\n    code: u16,\n    latency_ns: u64,\n    client_server: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 25 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__http_response = jb_agent_internal__http_response {\n        _rpc_id: 372 as u16,\n        code,\n        pid,\n        sk,\n        latency_ns,\n        client_server,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 25 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 25 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_bpf_log(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    filelineid: u64,\n    code: u64,\n    arg0: u64,\n    arg1: u64,\n    arg2: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__bpf_log = jb_agent_internal__bpf_log {\n        _rpc_id: 373 as u16,\n        filelineid,\n        code,\n        arg0,\n        arg1,\n        arg2,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_stack_trace(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    kernel_stack_id: i32,\n    user_stack_id: i32,\n    tgid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__stack_trace = jb_agent_internal__stack_trace {\n        _rpc_id: 374 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        kernel_stack_id,\n        user_stack_id,\n        tgid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_tcp_data(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    pid: u32,\n    length: u32,\n    offset: u64,\n    stream_type: u8,\n    client_server: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 28 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__tcp_data = jb_agent_internal__tcp_data {\n        _rpc_id: 375 as u16,\n        stream_type,\n        client_server,\n        pid,\n        sk,\n        offset,\n        length,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 28 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 28 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_pid_exit(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    tgid: u64,\n    pid: u32,\n    exit_code: i32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 20 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__pid_exit = jb_agent_internal__pid_exit {\n        _rpc_id: 377 as u16,\n        pid,\n        tgid,\n        exit_code,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_report_debug_event(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    event: u16,\n    arg1: u64,\n    arg2: u64,\n    arg3: u64,\n    arg4: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 40 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__report_debug_event = jb_agent_internal__report_debug_event {\n        _rpc_id: 378 as u16,\n        event,\n        arg1,\n        arg2,\n        arg3,\n        arg4,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_tcp_reset(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    is_rx: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__tcp_reset = jb_agent_internal__tcp_reset {\n        _rpc_id: 379 as u16,\n        is_rx,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_agent_internal_encode_pulse(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_agent_internal__pulse = jb_agent_internal__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::agent_internal\n//\n// g_type: u8\n// g_size: 8\n// g_shift: 29\n// hash_shift: 23\n// hash_mask: 63\n// n_keys: 29\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const AGENT_INTERNAL_HASH_SIZE: u32 = 64u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 8] = [1, 0, 0, 1, 0, 0, 1, 1];\n\n#[inline]\n#[allow(dead_code)]\npub fn agent_internal_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 29) as usize] as u32;\n    (k >> 23).wrapping_add(g) & 63u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for dns_packet\npub struct dns_packet {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub pkt: ::std::string::String,\n    pub total_len: u16,\n    pub is_rx: u8,\n}\n\nimpl dns_packet {\n    pub const RPC_ID: u16 = 331u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let total_len = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let is_rx = body[6usize];\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pkt = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            pkt: pkt,\n            total_len: total_len,\n            is_rx: is_rx,\n        })\n    }\n}\n// Parsed struct for reset_tcp_counters\npub struct reset_tcp_counters {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub bytes_acked: u64,\n    pub packets_delivered: u32,\n    pub packets_retrans: u32,\n    pub bytes_received: u64,\n    pub pid: u32,\n}\n\nimpl reset_tcp_counters {\n    pub const RPC_ID: u16 = 332u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 40usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let bytes_acked = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let packets_delivered = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let packets_retrans = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let bytes_received = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            bytes_acked: bytes_acked,\n            packets_delivered: packets_delivered,\n            packets_retrans: packets_retrans,\n            bytes_received: bytes_received,\n            pid: pid,\n        })\n    }\n}\n// Parsed struct for new_sock_created\npub struct new_sock_created {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk: u64,\n}\n\nimpl new_sock_created {\n    pub const RPC_ID: u16 = 333u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for udp_new_socket\npub struct udp_new_socket {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk: u64,\n    pub laddr: [u8; 16],\n    pub lport: u16,\n}\n\nimpl udp_new_socket {\n    pub const RPC_ID: u16 = 334u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let laddr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 16usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let lport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            sk: sk,\n            laddr: laddr,\n            lport: lport,\n        })\n    }\n}\n// Parsed struct for udp_destroy_socket\npub struct udp_destroy_socket {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl udp_destroy_socket {\n    pub const RPC_ID: u16 = 335u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for udp_stats\npub struct udp_stats {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub raddr: [u8; 16],\n    pub packets: u32,\n    pub bytes: u32,\n    pub changed_af: u8,\n    pub rport: u16,\n    pub is_rx: u8,\n    pub laddr: [u8; 16],\n    pub lport: u16,\n    pub drops: u32,\n}\n\nimpl udp_stats {\n    pub const RPC_ID: u16 = 336u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 60usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let raddr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 26usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let packets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let bytes = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let changed_af = body[42usize];\n        let rport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let is_rx = body[43usize];\n        let laddr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 44usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let lport = u16::from_ne_bytes(body[24usize..24usize + 2].try_into().unwrap());\n        let drops = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            raddr: raddr,\n            packets: packets,\n            bytes: bytes,\n            changed_af: changed_af,\n            rport: rport,\n            is_rx: is_rx,\n            laddr: laddr,\n            lport: lport,\n            drops: drops,\n        })\n    }\n}\n// Parsed struct for pid_info\npub struct pid_info {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n    pub cgroup: u64,\n    pub parent_pid: i32,\n}\n\nimpl pid_info {\n    pub const RPC_ID: u16 = 337u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 36usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let cgroup = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let parent_pid = i32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n            cgroup: cgroup,\n            parent_pid: parent_pid,\n        })\n    }\n}\n// Parsed struct for pid_close\npub struct pid_close {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl pid_close {\n    pub const RPC_ID: u16 = 338u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for pid_set_comm\npub struct pid_set_comm {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl pid_set_comm {\n    pub const RPC_ID: u16 = 371u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for set_state_ipv4\npub struct set_state_ipv4 {\n    pub _rpc_id: u16,\n    pub dest: u32,\n    pub src: u32,\n    pub dport: u16,\n    pub sport: u16,\n    pub sk: u64,\n    pub tx_rx: u32,\n}\n\nimpl set_state_ipv4 {\n    pub const RPC_ID: u16 = 339u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 26usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let dest = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let src = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let dport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let sport = u16::from_ne_bytes(body[24usize..24usize + 2].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let tx_rx = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            dest: dest,\n            src: src,\n            dport: dport,\n            sport: sport,\n            sk: sk,\n            tx_rx: tx_rx,\n        })\n    }\n}\n// Parsed struct for set_state_ipv6\npub struct set_state_ipv6 {\n    pub _rpc_id: u16,\n    pub dest: [u8; 16],\n    pub src: [u8; 16],\n    pub dport: u16,\n    pub sport: u16,\n    pub sk: u64,\n    pub tx_rx: u32,\n}\n\nimpl set_state_ipv6 {\n    pub const RPC_ID: u16 = 340u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 50usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let dest = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 18usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let src = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 34usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let dport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let sport = u16::from_ne_bytes(body[16usize..16usize + 2].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let tx_rx = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            dest: dest,\n            src: src,\n            dport: dport,\n            sport: sport,\n            sk: sk,\n            tx_rx: tx_rx,\n        })\n    }\n}\n// Parsed struct for rtt_estimator\npub struct rtt_estimator {\n    pub _rpc_id: u16,\n    pub srtt: u32,\n    pub snd_cwnd: u32,\n    pub bytes_acked: u64,\n    pub ca_state: u8,\n    pub sk: u64,\n    pub packets_in_flight: u32,\n    pub packets_delivered: u32,\n    pub packets_retrans: u32,\n    pub rcv_holes: u32,\n    pub bytes_received: u64,\n    pub rcv_delivered: u32,\n    pub rcv_rtt: u32,\n}\n\nimpl rtt_estimator {\n    pub const RPC_ID: u16 = 361u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 60usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let srtt = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let snd_cwnd = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let bytes_acked = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let ca_state = body[2usize];\n        let sk = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let packets_in_flight = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let packets_delivered = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let packets_retrans = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let rcv_holes = u32::from_ne_bytes(body[48usize..48usize + 4].try_into().unwrap());\n        let bytes_received = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let rcv_delivered = u32::from_ne_bytes(body[52usize..52usize + 4].try_into().unwrap());\n        let rcv_rtt = u32::from_ne_bytes(body[56usize..56usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            srtt: srtt,\n            snd_cwnd: snd_cwnd,\n            bytes_acked: bytes_acked,\n            ca_state: ca_state,\n            sk: sk,\n            packets_in_flight: packets_in_flight,\n            packets_delivered: packets_delivered,\n            packets_retrans: packets_retrans,\n            rcv_holes: rcv_holes,\n            bytes_received: bytes_received,\n            rcv_delivered: rcv_delivered,\n            rcv_rtt: rcv_rtt,\n        })\n    }\n}\n// Parsed struct for close_sock_info\npub struct close_sock_info {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl close_sock_info {\n    pub const RPC_ID: u16 = 362u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for kill_css\npub struct kill_css {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n    pub name: [u8; 256],\n}\n\nimpl kill_css {\n    pub const RPC_ID: u16 = 363u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 280usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[264usize..264usize + 8].try_into().unwrap());\n        let cgroup_parent = u64::from_ne_bytes(body[272usize..272usize + 8].try_into().unwrap());\n        let name = {\n            let mut tmp = [0u8; 256];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 256usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cgroup_parent: cgroup_parent,\n            name: name,\n        })\n    }\n}\n// Parsed struct for css_populate_dir\npub struct css_populate_dir {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n    pub name: [u8; 256],\n}\n\nimpl css_populate_dir {\n    pub const RPC_ID: u16 = 364u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 280usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[264usize..264usize + 8].try_into().unwrap());\n        let cgroup_parent = u64::from_ne_bytes(body[272usize..272usize + 8].try_into().unwrap());\n        let name = {\n            let mut tmp = [0u8; 256];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 256usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cgroup_parent: cgroup_parent,\n            name: name,\n        })\n    }\n}\n// Parsed struct for existing_cgroup_probe\npub struct existing_cgroup_probe {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n    pub name: [u8; 256],\n}\n\nimpl existing_cgroup_probe {\n    pub const RPC_ID: u16 = 365u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 280usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[264usize..264usize + 8].try_into().unwrap());\n        let cgroup_parent = u64::from_ne_bytes(body[272usize..272usize + 8].try_into().unwrap());\n        let name = {\n            let mut tmp = [0u8; 256];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 256usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cgroup_parent: cgroup_parent,\n            name: name,\n        })\n    }\n}\n// Parsed struct for cgroup_attach_task\npub struct cgroup_attach_task {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl cgroup_attach_task {\n    pub const RPC_ID: u16 = 366u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for nf_conntrack_alter_reply\npub struct nf_conntrack_alter_reply {\n    pub _rpc_id: u16,\n    pub ct: u64,\n    pub src_ip: u32,\n    pub src_port: u16,\n    pub dst_ip: u32,\n    pub dst_port: u16,\n    pub proto: u8,\n    pub nat_src_ip: u32,\n    pub nat_src_port: u16,\n    pub nat_dst_ip: u32,\n    pub nat_dst_port: u16,\n    pub nat_proto: u8,\n}\n\nimpl nf_conntrack_alter_reply {\n    pub const RPC_ID: u16 = 367u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 36usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let ct = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let src_ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let src_port = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let dst_ip = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let dst_port = u16::from_ne_bytes(body[28usize..28usize + 2].try_into().unwrap());\n        let proto = body[34usize];\n        let nat_src_ip = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let nat_src_port = u16::from_ne_bytes(body[30usize..30usize + 2].try_into().unwrap());\n        let nat_dst_ip = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let nat_dst_port = u16::from_ne_bytes(body[32usize..32usize + 2].try_into().unwrap());\n        let nat_proto = body[35usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            ct: ct,\n            src_ip: src_ip,\n            src_port: src_port,\n            dst_ip: dst_ip,\n            dst_port: dst_port,\n            proto: proto,\n            nat_src_ip: nat_src_ip,\n            nat_src_port: nat_src_port,\n            nat_dst_ip: nat_dst_ip,\n            nat_dst_port: nat_dst_port,\n            nat_proto: nat_proto,\n        })\n    }\n}\n// Parsed struct for nf_nat_cleanup_conntrack\npub struct nf_nat_cleanup_conntrack {\n    pub _rpc_id: u16,\n    pub ct: u64,\n    pub src_ip: u32,\n    pub src_port: u16,\n    pub dst_ip: u32,\n    pub dst_port: u16,\n    pub proto: u8,\n}\n\nimpl nf_nat_cleanup_conntrack {\n    pub const RPC_ID: u16 = 368u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 23usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let ct = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let src_ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let src_port = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let dst_ip = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let dst_port = u16::from_ne_bytes(body[20usize..20usize + 2].try_into().unwrap());\n        let proto = body[22usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            ct: ct,\n            src_ip: src_ip,\n            src_port: src_port,\n            dst_ip: dst_ip,\n            dst_port: dst_port,\n            proto: proto,\n        })\n    }\n}\n// Parsed struct for existing_conntrack_tuple\npub struct existing_conntrack_tuple {\n    pub _rpc_id: u16,\n    pub ct: u64,\n    pub src_ip: u32,\n    pub src_port: u16,\n    pub dst_ip: u32,\n    pub dst_port: u16,\n    pub proto: u8,\n    pub dir: u8,\n}\n\nimpl existing_conntrack_tuple {\n    pub const RPC_ID: u16 = 369u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let ct = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let src_ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let src_port = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let dst_ip = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let dst_port = u16::from_ne_bytes(body[20usize..20usize + 2].try_into().unwrap());\n        let proto = body[22usize];\n        let dir = body[23usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            ct: ct,\n            src_ip: src_ip,\n            src_port: src_port,\n            dst_ip: dst_ip,\n            dst_port: dst_port,\n            proto: proto,\n            dir: dir,\n        })\n    }\n}\n// Parsed struct for tcp_syn_timeout\npub struct tcp_syn_timeout {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl tcp_syn_timeout {\n    pub const RPC_ID: u16 = 370u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for http_response\npub struct http_response {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub pid: u32,\n    pub code: u16,\n    pub latency_ns: u64,\n    pub client_server: u8,\n}\n\nimpl http_response {\n    pub const RPC_ID: u16 = 372u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 25usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let code = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let latency_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let client_server = body[24usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            pid: pid,\n            code: code,\n            latency_ns: latency_ns,\n            client_server: client_server,\n        })\n    }\n}\n// Parsed struct for bpf_log\npub struct bpf_log {\n    pub _rpc_id: u16,\n    pub filelineid: u64,\n    pub code: u64,\n    pub arg0: u64,\n    pub arg1: u64,\n    pub arg2: u64,\n}\n\nimpl bpf_log {\n    pub const RPC_ID: u16 = 373u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let filelineid = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let code = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let arg0 = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let arg1 = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        let arg2 = u64::from_ne_bytes(body[40usize..40usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            filelineid: filelineid,\n            code: code,\n            arg0: arg0,\n            arg1: arg1,\n            arg2: arg2,\n        })\n    }\n}\n// Parsed struct for stack_trace\npub struct stack_trace {\n    pub _rpc_id: u16,\n    pub kernel_stack_id: i32,\n    pub user_stack_id: i32,\n    pub tgid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl stack_trace {\n    pub const RPC_ID: u16 = 374u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let kernel_stack_id = i32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let user_stack_id = i32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let tgid = u32::from_ne_bytes(body[28usize..28usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            kernel_stack_id: kernel_stack_id,\n            user_stack_id: user_stack_id,\n            tgid: tgid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for tcp_data\npub struct tcp_data {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub pid: u32,\n    pub length: u32,\n    pub offset: u64,\n    pub stream_type: u8,\n    pub client_server: u8,\n}\n\nimpl tcp_data {\n    pub const RPC_ID: u16 = 375u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 28usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let length = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let offset = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let stream_type = body[2usize];\n        let client_server = body[3usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            pid: pid,\n            length: length,\n            offset: offset,\n            stream_type: stream_type,\n            client_server: client_server,\n        })\n    }\n}\n// Parsed struct for pid_exit\npub struct pid_exit {\n    pub _rpc_id: u16,\n    pub tgid: u64,\n    pub pid: u32,\n    pub exit_code: i32,\n}\n\nimpl pid_exit {\n    pub const RPC_ID: u16 = 377u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 20usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let tgid = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let exit_code = i32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            tgid: tgid,\n            pid: pid,\n            exit_code: exit_code,\n        })\n    }\n}\n// Parsed struct for report_debug_event\npub struct report_debug_event {\n    pub _rpc_id: u16,\n    pub event: u16,\n    pub arg1: u64,\n    pub arg2: u64,\n    pub arg3: u64,\n    pub arg4: u64,\n}\n\nimpl report_debug_event {\n    pub const RPC_ID: u16 = 378u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 40usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let event = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let arg1 = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let arg2 = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let arg3 = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let arg4 = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            event: event,\n            arg1: arg1,\n            arg2: arg2,\n            arg3: arg3,\n            arg4: arg4,\n        })\n    }\n}\n// Parsed struct for tcp_reset\npub struct tcp_reset {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub is_rx: u8,\n}\n\nimpl tcp_reset {\n    pub const RPC_ID: u16 = 379u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let is_rx = body[2usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            is_rx: is_rx,\n        })\n    }\n}\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/agent_internal/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__dns_packet {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub total_len: u16,\n    pub is_rx: u8,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__dns_packet {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(331u16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__dns_packet {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_PACKET_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod dns_packet_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__dns_packet>();\n        let align = align_of::<jb_agent_internal__dns_packet>();\n        let padded_raw_size = (DNS_PACKET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__dns_packet, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__dns_packet, _len), 2);\n        assert_eq!(offset_of!(jb_agent_internal__dns_packet, total_len), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__dns_packet, is_rx), 6usize);\n        assert_eq!(offset_of!(jb_agent_internal__dns_packet, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__reset_tcp_counters {\n    pub _rpc_id: u16,\n    pub packets_delivered: u32,\n    pub sk: u64,\n    pub bytes_acked: u64,\n    pub bytes_received: u64,\n    pub packets_retrans: u32,\n    pub pid: u32,\n}\n\nimpl jb_agent_internal__reset_tcp_counters {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(332u16, 40, true)\n    }\n}\n\nimpl Default for jb_agent_internal__reset_tcp_counters {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const RESET_TCP_COUNTERS_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod reset_tcp_counters_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__reset_tcp_counters>();\n        let align = align_of::<jb_agent_internal__reset_tcp_counters>();\n        let padded_raw_size = (RESET_TCP_COUNTERS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, packets_delivered),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, sk),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, bytes_acked),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, bytes_received),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, packets_retrans),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__reset_tcp_counters, pid),\n            36usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__new_sock_created {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__new_sock_created {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(333u16, 16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__new_sock_created {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NEW_SOCK_CREATED_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod new_sock_created_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__new_sock_created>();\n        let align = align_of::<jb_agent_internal__new_sock_created>();\n        let padded_raw_size = (NEW_SOCK_CREATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__new_sock_created, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__new_sock_created, pid), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__new_sock_created, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__udp_new_socket {\n    pub _rpc_id: u16,\n    pub lport: u16,\n    pub pid: u32,\n    pub sk: u64,\n    pub laddr: [u8; 16],\n}\n\nimpl jb_agent_internal__udp_new_socket {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(334u16, 32, true)\n    }\n}\n\nimpl Default for jb_agent_internal__udp_new_socket {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_NEW_SOCKET_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod udp_new_socket_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__udp_new_socket>();\n        let align = align_of::<jb_agent_internal__udp_new_socket>();\n        let padded_raw_size = (UDP_NEW_SOCKET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__udp_new_socket, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__udp_new_socket, lport), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_new_socket, pid), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_new_socket, sk), 8usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__udp_new_socket, laddr),\n            16usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__udp_destroy_socket {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__udp_destroy_socket {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(335u16, 16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__udp_destroy_socket {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_DESTROY_SOCKET_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod udp_destroy_socket_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__udp_destroy_socket>();\n        let align = align_of::<jb_agent_internal__udp_destroy_socket>();\n        let padded_raw_size = (UDP_DESTROY_SOCKET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__udp_destroy_socket, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__udp_destroy_socket, sk),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__udp_stats {\n    pub _rpc_id: u16,\n    pub rport: u16,\n    pub packets: u32,\n    pub sk: u64,\n    pub bytes: u32,\n    pub drops: u32,\n    pub lport: u16,\n    pub raddr: [u8; 16],\n    pub changed_af: u8,\n    pub is_rx: u8,\n    pub laddr: [u8; 16],\n}\n\nimpl jb_agent_internal__udp_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(336u16, 60, true)\n    }\n}\n\nimpl Default for jb_agent_internal__udp_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_STATS_WIRE_SIZE: usize = 60;\n\n#[cfg(test)]\nmod udp_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__udp_stats>();\n        let align = align_of::<jb_agent_internal__udp_stats>();\n        let padded_raw_size = (UDP_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, rport), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, packets), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, sk), 8usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, bytes), 16usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, drops), 20usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, lport), 24usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, raddr), 26usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__udp_stats, changed_af),\n            42usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, is_rx), 43usize);\n        assert_eq!(offset_of!(jb_agent_internal__udp_stats, laddr), 44usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__pid_info {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n    pub cgroup: u64,\n    pub parent_pid: i32,\n}\n\nimpl jb_agent_internal__pid_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(337u16, 36, true)\n    }\n}\n\nimpl Default for jb_agent_internal__pid_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_INFO_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod pid_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__pid_info>();\n        let align = align_of::<jb_agent_internal__pid_info>();\n        let padded_raw_size = (PID_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__pid_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__pid_info, comm), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_info, pid), 20usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_info, cgroup), 24usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_info, parent_pid), 32usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__pid_close {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n}\n\nimpl jb_agent_internal__pid_close {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(338u16, 24, true)\n    }\n}\n\nimpl Default for jb_agent_internal__pid_close {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_CLOSE_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod pid_close_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__pid_close>();\n        let align = align_of::<jb_agent_internal__pid_close>();\n        let padded_raw_size = (PID_CLOSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__pid_close, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__pid_close, comm), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_close, pid), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__pid_set_comm {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n}\n\nimpl jb_agent_internal__pid_set_comm {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(371u16, 24, true)\n    }\n}\n\nimpl Default for jb_agent_internal__pid_set_comm {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_SET_COMM_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod pid_set_comm_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__pid_set_comm>();\n        let align = align_of::<jb_agent_internal__pid_set_comm>();\n        let padded_raw_size = (PID_SET_COMM_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__pid_set_comm, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__pid_set_comm, comm), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_set_comm, pid), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__set_state_ipv4 {\n    pub _rpc_id: u16,\n    pub dport: u16,\n    pub dest: u32,\n    pub sk: u64,\n    pub src: u32,\n    pub tx_rx: u32,\n    pub sport: u16,\n}\n\nimpl jb_agent_internal__set_state_ipv4 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(339u16, 26, true)\n    }\n}\n\nimpl Default for jb_agent_internal__set_state_ipv4 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_STATE_IPV4_WIRE_SIZE: usize = 26;\n\n#[cfg(test)]\nmod set_state_ipv4_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__set_state_ipv4>();\n        let align = align_of::<jb_agent_internal__set_state_ipv4>();\n        let padded_raw_size = (SET_STATE_IPV4_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv4, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv4, dport), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv4, dest), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv4, sk), 8usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv4, src), 16usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__set_state_ipv4, tx_rx),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__set_state_ipv4, sport),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__set_state_ipv6 {\n    pub _rpc_id: u16,\n    pub dport: u16,\n    pub tx_rx: u32,\n    pub sk: u64,\n    pub sport: u16,\n    pub dest: [u8; 16],\n    pub src: [u8; 16],\n}\n\nimpl jb_agent_internal__set_state_ipv6 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(340u16, 50, true)\n    }\n}\n\nimpl Default for jb_agent_internal__set_state_ipv6 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_STATE_IPV6_WIRE_SIZE: usize = 50;\n\n#[cfg(test)]\nmod set_state_ipv6_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__set_state_ipv6>();\n        let align = align_of::<jb_agent_internal__set_state_ipv6>();\n        let padded_raw_size = (SET_STATE_IPV6_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, dport), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, tx_rx), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, sk), 8usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__set_state_ipv6, sport),\n            16usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, dest), 18usize);\n        assert_eq!(offset_of!(jb_agent_internal__set_state_ipv6, src), 34usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__rtt_estimator {\n    pub _rpc_id: u16,\n    pub ca_state: u8,\n    pub srtt: u32,\n    pub bytes_acked: u64,\n    pub sk: u64,\n    pub bytes_received: u64,\n    pub snd_cwnd: u32,\n    pub packets_in_flight: u32,\n    pub packets_delivered: u32,\n    pub packets_retrans: u32,\n    pub rcv_holes: u32,\n    pub rcv_delivered: u32,\n    pub rcv_rtt: u32,\n}\n\nimpl jb_agent_internal__rtt_estimator {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(361u16, 60, true)\n    }\n}\n\nimpl Default for jb_agent_internal__rtt_estimator {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const RTT_ESTIMATOR_WIRE_SIZE: usize = 60;\n\n#[cfg(test)]\nmod rtt_estimator_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__rtt_estimator>();\n        let align = align_of::<jb_agent_internal__rtt_estimator>();\n        let padded_raw_size = (RTT_ESTIMATOR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__rtt_estimator, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, ca_state),\n            2usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__rtt_estimator, srtt), 4usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, bytes_acked),\n            8usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__rtt_estimator, sk), 16usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, bytes_received),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, snd_cwnd),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, packets_in_flight),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, packets_delivered),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, packets_retrans),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, rcv_holes),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, rcv_delivered),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__rtt_estimator, rcv_rtt),\n            56usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__close_sock_info {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__close_sock_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(362u16, 16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__close_sock_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CLOSE_SOCK_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod close_sock_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__close_sock_info>();\n        let align = align_of::<jb_agent_internal__close_sock_info>();\n        let padded_raw_size = (CLOSE_SOCK_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__close_sock_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__close_sock_info, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__kill_css {\n    pub _rpc_id: u16,\n    pub name: [u8; 256],\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n}\n\nimpl jb_agent_internal__kill_css {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(363u16, 280, true)\n    }\n}\n\nimpl Default for jb_agent_internal__kill_css {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const KILL_CSS_WIRE_SIZE: usize = 280;\n\n#[cfg(test)]\nmod kill_css_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__kill_css>();\n        let align = align_of::<jb_agent_internal__kill_css>();\n        let padded_raw_size = (KILL_CSS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__kill_css, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__kill_css, name), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__kill_css, cgroup), 264usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__kill_css, cgroup_parent),\n            272usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__css_populate_dir {\n    pub _rpc_id: u16,\n    pub name: [u8; 256],\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n}\n\nimpl jb_agent_internal__css_populate_dir {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(364u16, 280, true)\n    }\n}\n\nimpl Default for jb_agent_internal__css_populate_dir {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CSS_POPULATE_DIR_WIRE_SIZE: usize = 280;\n\n#[cfg(test)]\nmod css_populate_dir_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__css_populate_dir>();\n        let align = align_of::<jb_agent_internal__css_populate_dir>();\n        let padded_raw_size = (CSS_POPULATE_DIR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__css_populate_dir, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_agent_internal__css_populate_dir, name),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__css_populate_dir, cgroup),\n            264usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__css_populate_dir, cgroup_parent),\n            272usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__existing_cgroup_probe {\n    pub _rpc_id: u16,\n    pub name: [u8; 256],\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n}\n\nimpl jb_agent_internal__existing_cgroup_probe {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(365u16, 280, true)\n    }\n}\n\nimpl Default for jb_agent_internal__existing_cgroup_probe {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const EXISTING_CGROUP_PROBE_WIRE_SIZE: usize = 280;\n\n#[cfg(test)]\nmod existing_cgroup_probe_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__existing_cgroup_probe>();\n        let align = align_of::<jb_agent_internal__existing_cgroup_probe>();\n        let padded_raw_size = (EXISTING_CGROUP_PROBE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_cgroup_probe, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_cgroup_probe, name),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_cgroup_probe, cgroup),\n            264usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_cgroup_probe, cgroup_parent),\n            272usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__cgroup_attach_task {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n    pub cgroup: u64,\n}\n\nimpl jb_agent_internal__cgroup_attach_task {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(366u16, 32, true)\n    }\n}\n\nimpl Default for jb_agent_internal__cgroup_attach_task {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CGROUP_ATTACH_TASK_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod cgroup_attach_task_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__cgroup_attach_task>();\n        let align = align_of::<jb_agent_internal__cgroup_attach_task>();\n        let padded_raw_size = (CGROUP_ATTACH_TASK_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__cgroup_attach_task, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__cgroup_attach_task, comm),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__cgroup_attach_task, pid),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__cgroup_attach_task, cgroup),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__nf_conntrack_alter_reply {\n    pub _rpc_id: u16,\n    pub src_port: u16,\n    pub src_ip: u32,\n    pub ct: u64,\n    pub dst_ip: u32,\n    pub nat_src_ip: u32,\n    pub nat_dst_ip: u32,\n    pub dst_port: u16,\n    pub nat_src_port: u16,\n    pub nat_dst_port: u16,\n    pub proto: u8,\n    pub nat_proto: u8,\n}\n\nimpl jb_agent_internal__nf_conntrack_alter_reply {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(367u16, 36, true)\n    }\n}\n\nimpl Default for jb_agent_internal__nf_conntrack_alter_reply {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NF_CONNTRACK_ALTER_REPLY_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod nf_conntrack_alter_reply_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__nf_conntrack_alter_reply>();\n        let align = align_of::<jb_agent_internal__nf_conntrack_alter_reply>();\n        let padded_raw_size = (NF_CONNTRACK_ALTER_REPLY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, src_port),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, src_ip),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, ct),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, dst_ip),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, nat_src_ip),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, nat_dst_ip),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, dst_port),\n            28usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, nat_src_port),\n            30usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, nat_dst_port),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, proto),\n            34usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_conntrack_alter_reply, nat_proto),\n            35usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__nf_nat_cleanup_conntrack {\n    pub _rpc_id: u16,\n    pub src_port: u16,\n    pub src_ip: u32,\n    pub ct: u64,\n    pub dst_ip: u32,\n    pub dst_port: u16,\n    pub proto: u8,\n}\n\nimpl jb_agent_internal__nf_nat_cleanup_conntrack {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(368u16, 23, true)\n    }\n}\n\nimpl Default for jb_agent_internal__nf_nat_cleanup_conntrack {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NF_NAT_CLEANUP_CONNTRACK_WIRE_SIZE: usize = 23;\n\n#[cfg(test)]\nmod nf_nat_cleanup_conntrack_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__nf_nat_cleanup_conntrack>();\n        let align = align_of::<jb_agent_internal__nf_nat_cleanup_conntrack>();\n        let padded_raw_size = (NF_NAT_CLEANUP_CONNTRACK_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, src_port),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, src_ip),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, ct),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, dst_ip),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, dst_port),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__nf_nat_cleanup_conntrack, proto),\n            22usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__existing_conntrack_tuple {\n    pub _rpc_id: u16,\n    pub src_port: u16,\n    pub src_ip: u32,\n    pub ct: u64,\n    pub dst_ip: u32,\n    pub dst_port: u16,\n    pub proto: u8,\n    pub dir: u8,\n}\n\nimpl jb_agent_internal__existing_conntrack_tuple {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(369u16, 24, true)\n    }\n}\n\nimpl Default for jb_agent_internal__existing_conntrack_tuple {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const EXISTING_CONNTRACK_TUPLE_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod existing_conntrack_tuple_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__existing_conntrack_tuple>();\n        let align = align_of::<jb_agent_internal__existing_conntrack_tuple>();\n        let padded_raw_size = (EXISTING_CONNTRACK_TUPLE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, src_port),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, src_ip),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, ct),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, dst_ip),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, dst_port),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, proto),\n            22usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__existing_conntrack_tuple, dir),\n            23usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__tcp_syn_timeout {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__tcp_syn_timeout {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(370u16, 16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__tcp_syn_timeout {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_SYN_TIMEOUT_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tcp_syn_timeout_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__tcp_syn_timeout>();\n        let align = align_of::<jb_agent_internal__tcp_syn_timeout>();\n        let padded_raw_size = (TCP_SYN_TIMEOUT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__tcp_syn_timeout, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_syn_timeout, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__http_response {\n    pub _rpc_id: u16,\n    pub code: u16,\n    pub pid: u32,\n    pub sk: u64,\n    pub latency_ns: u64,\n    pub client_server: u8,\n}\n\nimpl jb_agent_internal__http_response {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(372u16, 25, true)\n    }\n}\n\nimpl Default for jb_agent_internal__http_response {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const HTTP_RESPONSE_WIRE_SIZE: usize = 25;\n\n#[cfg(test)]\nmod http_response_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__http_response>();\n        let align = align_of::<jb_agent_internal__http_response>();\n        let padded_raw_size = (HTTP_RESPONSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__http_response, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__http_response, code), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__http_response, pid), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__http_response, sk), 8usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__http_response, latency_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__http_response, client_server),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__bpf_log {\n    pub _rpc_id: u16,\n    pub filelineid: u64,\n    pub code: u64,\n    pub arg0: u64,\n    pub arg1: u64,\n    pub arg2: u64,\n}\n\nimpl jb_agent_internal__bpf_log {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(373u16, 48, true)\n    }\n}\n\nimpl Default for jb_agent_internal__bpf_log {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BPF_LOG_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod bpf_log_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__bpf_log>();\n        let align = align_of::<jb_agent_internal__bpf_log>();\n        let padded_raw_size = (BPF_LOG_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, filelineid), 8usize);\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, code), 16usize);\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, arg0), 24usize);\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, arg1), 32usize);\n        assert_eq!(offset_of!(jb_agent_internal__bpf_log, arg2), 40usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__stack_trace {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub kernel_stack_id: i32,\n    pub user_stack_id: i32,\n    pub tgid: u32,\n}\n\nimpl jb_agent_internal__stack_trace {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(374u16, 32, true)\n    }\n}\n\nimpl Default for jb_agent_internal__stack_trace {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const STACK_TRACE_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod stack_trace_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__stack_trace>();\n        let align = align_of::<jb_agent_internal__stack_trace>();\n        let padded_raw_size = (STACK_TRACE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__stack_trace, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__stack_trace, comm), 2usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__stack_trace, kernel_stack_id),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__stack_trace, user_stack_id),\n            24usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__stack_trace, tgid), 28usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__tcp_data {\n    pub _rpc_id: u16,\n    pub stream_type: u8,\n    pub client_server: u8,\n    pub pid: u32,\n    pub sk: u64,\n    pub offset: u64,\n    pub length: u32,\n}\n\nimpl jb_agent_internal__tcp_data {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(375u16, 28, true)\n    }\n}\n\nimpl Default for jb_agent_internal__tcp_data {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_DATA_WIRE_SIZE: usize = 28;\n\n#[cfg(test)]\nmod tcp_data_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__tcp_data>();\n        let align = align_of::<jb_agent_internal__tcp_data>();\n        let padded_raw_size = (TCP_DATA_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, stream_type), 2usize);\n        assert_eq!(\n            offset_of!(jb_agent_internal__tcp_data, client_server),\n            3usize\n        );\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, pid), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, sk), 8usize);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, offset), 16usize);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_data, length), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__pid_exit {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub tgid: u64,\n    pub exit_code: i32,\n}\n\nimpl jb_agent_internal__pid_exit {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(377u16, 20, true)\n    }\n}\n\nimpl Default for jb_agent_internal__pid_exit {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_EXIT_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod pid_exit_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__pid_exit>();\n        let align = align_of::<jb_agent_internal__pid_exit>();\n        let padded_raw_size = (PID_EXIT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__pid_exit, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__pid_exit, pid), 4usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_exit, tgid), 8usize);\n        assert_eq!(offset_of!(jb_agent_internal__pid_exit, exit_code), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__report_debug_event {\n    pub _rpc_id: u16,\n    pub event: u16,\n    pub arg1: u64,\n    pub arg2: u64,\n    pub arg3: u64,\n    pub arg4: u64,\n}\n\nimpl jb_agent_internal__report_debug_event {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(378u16, 40, true)\n    }\n}\n\nimpl Default for jb_agent_internal__report_debug_event {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const REPORT_DEBUG_EVENT_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod report_debug_event_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__report_debug_event>();\n        let align = align_of::<jb_agent_internal__report_debug_event>();\n        let padded_raw_size = (REPORT_DEBUG_EVENT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, event),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, arg1),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, arg2),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, arg3),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_agent_internal__report_debug_event, arg4),\n            32usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__tcp_reset {\n    pub _rpc_id: u16,\n    pub is_rx: u8,\n    pub sk: u64,\n}\n\nimpl jb_agent_internal__tcp_reset {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(379u16, 16, true)\n    }\n}\n\nimpl Default for jb_agent_internal__tcp_reset {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_RESET_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tcp_reset_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__tcp_reset>();\n        let align = align_of::<jb_agent_internal__tcp_reset>();\n        let padded_raw_size = (TCP_RESET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__tcp_reset, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_reset, is_rx), 2usize);\n        assert_eq!(offset_of!(jb_agent_internal__tcp_reset, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_agent_internal__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_agent_internal__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_agent_internal__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_agent_internal__pulse>();\n        let align = align_of::<jb_agent_internal__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_agent_internal__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![\n        jb_agent_internal__dns_packet::metadata(),\n        jb_agent_internal__reset_tcp_counters::metadata(),\n        jb_agent_internal__new_sock_created::metadata(),\n        jb_agent_internal__udp_new_socket::metadata(),\n        jb_agent_internal__udp_destroy_socket::metadata(),\n        jb_agent_internal__udp_stats::metadata(),\n        jb_agent_internal__pid_info::metadata(),\n        jb_agent_internal__pid_close::metadata(),\n        jb_agent_internal__pid_set_comm::metadata(),\n        jb_agent_internal__set_state_ipv4::metadata(),\n        jb_agent_internal__set_state_ipv6::metadata(),\n        jb_agent_internal__rtt_estimator::metadata(),\n        jb_agent_internal__close_sock_info::metadata(),\n        jb_agent_internal__kill_css::metadata(),\n        jb_agent_internal__css_populate_dir::metadata(),\n        jb_agent_internal__existing_cgroup_probe::metadata(),\n        jb_agent_internal__cgroup_attach_task::metadata(),\n        jb_agent_internal__nf_conntrack_alter_reply::metadata(),\n        jb_agent_internal__nf_nat_cleanup_conntrack::metadata(),\n        jb_agent_internal__existing_conntrack_tuple::metadata(),\n        jb_agent_internal__tcp_syn_timeout::metadata(),\n        jb_agent_internal__http_response::metadata(),\n        jb_agent_internal__bpf_log::metadata(),\n        jb_agent_internal__stack_trace::metadata(),\n        jb_agent_internal__tcp_data::metadata(),\n        jb_agent_internal__pid_exit::metadata(),\n        jb_agent_internal__report_debug_event::metadata(),\n        jb_agent_internal__tcp_reset::metadata(),\n        jb_agent_internal__pulse::metadata(),\n    ]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_aggregation\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::aggregation\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_agg_root_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__agg_root_start = jb_aggregation__agg_root_start {\n        _rpc_id: 461 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_agg_root_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__agg_root_end = jb_aggregation__agg_root_end {\n        _rpc_id: 462 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_update_node(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    id: JbBlob,\n    az: JbBlob,\n    role: JbBlob,\n    version: JbBlob,\n    env: JbBlob,\n    ns: JbBlob,\n    node_type: u8,\n    address: JbBlob,\n    process: JbBlob,\n    container: JbBlob,\n    pod_name: JbBlob,\n    role_uid: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 34 as u32;\n    __consumed = __consumed.saturating_add(id.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    __consumed = __consumed.saturating_add(address.len as u32);\n    __consumed = __consumed.saturating_add(process.len as u32);\n    __consumed = __consumed.saturating_add(container.len as u32);\n    __consumed = __consumed.saturating_add(pod_name.len as u32);\n    __consumed = __consumed.saturating_add(role_uid.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_id: &[u8] = unsafe { slice::from_raw_parts(id.buf as *const u8, id.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n    let __sl_address: &[u8] =\n        unsafe { slice::from_raw_parts(address.buf as *const u8, address.len as usize) };\n    let __sl_process: &[u8] =\n        unsafe { slice::from_raw_parts(process.buf as *const u8, process.len as usize) };\n    let __sl_container: &[u8] =\n        unsafe { slice::from_raw_parts(container.buf as *const u8, container.len as usize) };\n    let __sl_pod_name: &[u8] =\n        unsafe { slice::from_raw_parts(pod_name.buf as *const u8, pod_name.len as usize) };\n    let __sl_role_uid: &[u8] =\n        unsafe { slice::from_raw_parts(role_uid.buf as *const u8, role_uid.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__update_node = jb_aggregation__update_node {\n        _rpc_id: 463 as u16,\n        _len: __consumed as u16,\n        id: (__sl_id.len() as u16),\n        az: (__sl_az.len() as u16),\n        _ref,\n        role: (__sl_role.len() as u16),\n        version: (__sl_version.len() as u16),\n        env: (__sl_env.len() as u16),\n        ns: (__sl_ns.len() as u16),\n        address: (__sl_address.len() as u16),\n        process: (__sl_process.len() as u16),\n        container: (__sl_container.len() as u16),\n        pod_name: (__sl_pod_name.len() as u16),\n        side,\n        node_type,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 34 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 34 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_id.is_empty() {\n        let __len = __sl_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_id);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n    if !__sl_address.is_empty() {\n        let __len = __sl_address.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_address);\n        __off += __len;\n    }\n    if !__sl_process.is_empty() {\n        let __len = __sl_process.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_process);\n        __off += __len;\n    }\n    if !__sl_container.is_empty() {\n        let __len = __sl_container.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container);\n        __off += __len;\n    }\n    if !__sl_pod_name.is_empty() {\n        let __len = __sl_pod_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_name);\n        __off += __len;\n    }\n    if !__sl_role_uid.is_empty() {\n        let __len = __sl_role_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role_uid);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_update_tcp_metrics(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    direction: u8,\n    active_sockets: u32,\n    sum_retrans: u32,\n    sum_bytes: u64,\n    sum_srtt: u64,\n    sum_delivered: u64,\n    active_rtts: u32,\n    syn_timeouts: u32,\n    new_sockets: u32,\n    tcp_resets: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 60 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__update_tcp_metrics = jb_aggregation__update_tcp_metrics {\n        _rpc_id: 465 as u16,\n        direction,\n        active_sockets,\n        sum_bytes,\n        sum_srtt,\n        sum_delivered,\n        _ref,\n        sum_retrans,\n        active_rtts,\n        syn_timeouts,\n        new_sockets,\n        tcp_resets,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 60 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 60 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_update_udp_metrics(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    direction: u8,\n    active_sockets: u32,\n    addr_changes: u32,\n    packets: u32,\n    bytes: u64,\n    drops: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 36 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__update_udp_metrics = jb_aggregation__update_udp_metrics {\n        _rpc_id: 466 as u16,\n        direction,\n        active_sockets,\n        bytes,\n        _ref,\n        addr_changes,\n        packets,\n        drops,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_update_http_metrics(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    direction: u8,\n    active_sockets: u32,\n    sum_code_200: u32,\n    sum_code_400: u32,\n    sum_code_500: u32,\n    sum_code_other: u32,\n    sum_total_time_ns: u64,\n    sum_processing_time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__update_http_metrics = jb_aggregation__update_http_metrics {\n        _rpc_id: 467 as u16,\n        direction,\n        active_sockets,\n        sum_total_time_ns,\n        sum_processing_time_ns,\n        _ref,\n        sum_code_200,\n        sum_code_400,\n        sum_code_500,\n        sum_code_other,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_update_dns_metrics(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    direction: u8,\n    active_sockets: u32,\n    requests_a: u32,\n    requests_aaaa: u32,\n    responses: u32,\n    timeouts: u32,\n    sum_total_time_ns: u64,\n    sum_processing_time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__update_dns_metrics = jb_aggregation__update_dns_metrics {\n        _rpc_id: 468 as u16,\n        direction,\n        active_sockets,\n        sum_total_time_ns,\n        sum_processing_time_ns,\n        _ref,\n        requests_a,\n        requests_aaaa,\n        responses,\n        timeouts,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_aggregation_encode_pulse(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_aggregation__pulse = jb_aggregation__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::aggregation\n//\n// g_type: u8\n// g_size: 4\n// g_shift: 30\n// hash_shift: 26\n// hash_mask: 15\n// n_keys: 8\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const AGGREGATION_HASH_SIZE: u32 = 16u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 4] = [0, 9, 0, 0];\n\n#[inline]\n#[allow(dead_code)]\npub fn aggregation_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 30) as usize] as u32;\n    (k >> 26).wrapping_add(g) & 15u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for agg_root_start\npub struct agg_root_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl agg_root_start {\n    pub const RPC_ID: u16 = 461u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for agg_root_end\npub struct agg_root_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl agg_root_end {\n    pub const RPC_ID: u16 = 462u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for update_node\npub struct update_node {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub id: ::std::string::String,\n    pub az: ::std::string::String,\n    pub role: ::std::string::String,\n    pub version: ::std::string::String,\n    pub env: ::std::string::String,\n    pub ns: ::std::string::String,\n    pub node_type: u8,\n    pub address: ::std::string::String,\n    pub process: ::std::string::String,\n    pub container: ::std::string::String,\n    pub pod_name: ::std::string::String,\n    pub role_uid: ::std::string::String,\n}\n\nimpl update_node {\n    pub const RPC_ID: u16 = 463u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[32usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let node_type = body[33usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 34usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let id = if __l_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_id]).into_owned()\n        };\n        __off += __l_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[20usize..20usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[22usize..22usize + 2]);\n        let __l_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __l_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ns]).into_owned()\n        };\n        __off += __l_ns;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_address = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_address > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let address = if __l_address == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_address]).into_owned()\n        };\n        __off += __l_address;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_process = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_process > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let process = if __l_process == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_process]).into_owned()\n        };\n        __off += __l_process;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_container = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_container > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container = if __l_container == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_container]).into_owned()\n        };\n        __off += __l_container;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[30usize..30usize + 2]);\n        let __l_pod_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_name = if __l_pod_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_name]).into_owned()\n        };\n        __off += __l_pod_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role_uid = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            id: id,\n            az: az,\n            role: role,\n            version: version,\n            env: env,\n            ns: ns,\n            node_type: node_type,\n            address: address,\n            process: process,\n            container: container,\n            pod_name: pod_name,\n            role_uid: role_uid,\n        })\n    }\n}\n// Parsed struct for update_tcp_metrics\npub struct update_tcp_metrics {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub sum_retrans: u32,\n    pub sum_bytes: u64,\n    pub sum_srtt: u64,\n    pub sum_delivered: u64,\n    pub active_rtts: u32,\n    pub syn_timeouts: u32,\n    pub new_sockets: u32,\n    pub tcp_resets: u32,\n}\n\nimpl update_tcp_metrics {\n    pub const RPC_ID: u16 = 465u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 60usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        let direction = body[2usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sum_retrans = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let sum_bytes = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_srtt = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let sum_delivered = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let active_rtts = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let syn_timeouts = u32::from_ne_bytes(body[48usize..48usize + 4].try_into().unwrap());\n        let new_sockets = u32::from_ne_bytes(body[52usize..52usize + 4].try_into().unwrap());\n        let tcp_resets = u32::from_ne_bytes(body[56usize..56usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            direction: direction,\n            active_sockets: active_sockets,\n            sum_retrans: sum_retrans,\n            sum_bytes: sum_bytes,\n            sum_srtt: sum_srtt,\n            sum_delivered: sum_delivered,\n            active_rtts: active_rtts,\n            syn_timeouts: syn_timeouts,\n            new_sockets: new_sockets,\n            tcp_resets: tcp_resets,\n        })\n    }\n}\n// Parsed struct for update_udp_metrics\npub struct update_udp_metrics {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub addr_changes: u32,\n    pub packets: u32,\n    pub bytes: u64,\n    pub drops: u32,\n}\n\nimpl update_udp_metrics {\n    pub const RPC_ID: u16 = 466u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 36usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let direction = body[2usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let addr_changes = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let packets = u32::from_ne_bytes(body[28usize..28usize + 4].try_into().unwrap());\n        let bytes = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let drops = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            direction: direction,\n            active_sockets: active_sockets,\n            addr_changes: addr_changes,\n            packets: packets,\n            bytes: bytes,\n            drops: drops,\n        })\n    }\n}\n// Parsed struct for update_http_metrics\npub struct update_http_metrics {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub sum_code_200: u32,\n    pub sum_code_400: u32,\n    pub sum_code_500: u32,\n    pub sum_code_other: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n}\n\nimpl update_http_metrics {\n    pub const RPC_ID: u16 = 467u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let direction = body[2usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sum_code_200 = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let sum_code_400 = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let sum_code_500 = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let sum_code_other = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let sum_total_time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_processing_time_ns =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            direction: direction,\n            active_sockets: active_sockets,\n            sum_code_200: sum_code_200,\n            sum_code_400: sum_code_400,\n            sum_code_500: sum_code_500,\n            sum_code_other: sum_code_other,\n            sum_total_time_ns: sum_total_time_ns,\n            sum_processing_time_ns: sum_processing_time_ns,\n        })\n    }\n}\n// Parsed struct for update_dns_metrics\npub struct update_dns_metrics {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub requests_a: u32,\n    pub requests_aaaa: u32,\n    pub responses: u32,\n    pub timeouts: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n}\n\nimpl update_dns_metrics {\n    pub const RPC_ID: u16 = 468u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let direction = body[2usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let requests_a = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let requests_aaaa = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let responses = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let timeouts = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let sum_total_time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_processing_time_ns =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            direction: direction,\n            active_sockets: active_sockets,\n            requests_a: requests_a,\n            requests_aaaa: requests_aaaa,\n            responses: responses,\n            timeouts: timeouts,\n            sum_total_time_ns: sum_total_time_ns,\n            sum_processing_time_ns: sum_processing_time_ns,\n        })\n    }\n}\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/aggregation/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__agg_root_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_aggregation__agg_root_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(461u16, 16, true)\n    }\n}\n\nimpl Default for jb_aggregation__agg_root_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_ROOT_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agg_root_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__agg_root_start>();\n        let align = align_of::<jb_aggregation__agg_root_start>();\n        let padded_raw_size = (AGG_ROOT_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__agg_root_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_aggregation__agg_root_start, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__agg_root_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_aggregation__agg_root_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(462u16, 16, true)\n    }\n}\n\nimpl Default for jb_aggregation__agg_root_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_ROOT_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agg_root_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__agg_root_end>();\n        let align = align_of::<jb_aggregation__agg_root_end>();\n        let padded_raw_size = (AGG_ROOT_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__agg_root_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_aggregation__agg_root_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__update_node {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub id: u16,\n    pub az: u16,\n    pub _ref: u64,\n    pub role: u16,\n    pub version: u16,\n    pub env: u16,\n    pub ns: u16,\n    pub address: u16,\n    pub process: u16,\n    pub container: u16,\n    pub pod_name: u16,\n    pub side: u8,\n    pub node_type: u8,\n}\n\nimpl jb_aggregation__update_node {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(463u16, true)\n    }\n}\n\nimpl Default for jb_aggregation__update_node {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UPDATE_NODE_WIRE_SIZE: usize = 34;\n\n#[cfg(test)]\nmod update_node_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__update_node>();\n        let align = align_of::<jb_aggregation__update_node>();\n        let padded_raw_size = (UPDATE_NODE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__update_node, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_aggregation__update_node, _len), 2);\n        assert_eq!(offset_of!(jb_aggregation__update_node, id), 4usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, az), 6usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, _ref), 8usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, role), 16usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, version), 18usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, env), 20usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, ns), 22usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, address), 24usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, process), 26usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, container), 28usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, pod_name), 30usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, side), 32usize);\n        assert_eq!(offset_of!(jb_aggregation__update_node, node_type), 33usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__update_tcp_metrics {\n    pub _rpc_id: u16,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub sum_bytes: u64,\n    pub sum_srtt: u64,\n    pub sum_delivered: u64,\n    pub _ref: u64,\n    pub sum_retrans: u32,\n    pub active_rtts: u32,\n    pub syn_timeouts: u32,\n    pub new_sockets: u32,\n    pub tcp_resets: u32,\n}\n\nimpl jb_aggregation__update_tcp_metrics {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(465u16, 60, true)\n    }\n}\n\nimpl Default for jb_aggregation__update_tcp_metrics {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UPDATE_TCP_METRICS_WIRE_SIZE: usize = 60;\n\n#[cfg(test)]\nmod update_tcp_metrics_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__update_tcp_metrics>();\n        let align = align_of::<jb_aggregation__update_tcp_metrics>();\n        let padded_raw_size = (UPDATE_TCP_METRICS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__update_tcp_metrics, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, direction),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, active_sockets),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, sum_bytes),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, sum_srtt),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, sum_delivered),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, _ref),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, sum_retrans),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, active_rtts),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, syn_timeouts),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, new_sockets),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_tcp_metrics, tcp_resets),\n            56usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__update_udp_metrics {\n    pub _rpc_id: u16,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub bytes: u64,\n    pub _ref: u64,\n    pub addr_changes: u32,\n    pub packets: u32,\n    pub drops: u32,\n}\n\nimpl jb_aggregation__update_udp_metrics {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(466u16, 36, true)\n    }\n}\n\nimpl Default for jb_aggregation__update_udp_metrics {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UPDATE_UDP_METRICS_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod update_udp_metrics_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__update_udp_metrics>();\n        let align = align_of::<jb_aggregation__update_udp_metrics>();\n        let padded_raw_size = (UPDATE_UDP_METRICS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__update_udp_metrics, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, direction),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, active_sockets),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, bytes),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, _ref),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, addr_changes),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, packets),\n            28usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_udp_metrics, drops),\n            32usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__update_http_metrics {\n    pub _rpc_id: u16,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub _ref: u64,\n    pub sum_code_200: u32,\n    pub sum_code_400: u32,\n    pub sum_code_500: u32,\n    pub sum_code_other: u32,\n}\n\nimpl jb_aggregation__update_http_metrics {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(467u16, 48, true)\n    }\n}\n\nimpl Default for jb_aggregation__update_http_metrics {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UPDATE_HTTP_METRICS_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod update_http_metrics_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__update_http_metrics>();\n        let align = align_of::<jb_aggregation__update_http_metrics>();\n        let padded_raw_size = (UPDATE_HTTP_METRICS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__update_http_metrics, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, direction),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, active_sockets),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_total_time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_processing_time_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_code_200),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_code_400),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_code_500),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_http_metrics, sum_code_other),\n            44usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__update_dns_metrics {\n    pub _rpc_id: u16,\n    pub direction: u8,\n    pub active_sockets: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub _ref: u64,\n    pub requests_a: u32,\n    pub requests_aaaa: u32,\n    pub responses: u32,\n    pub timeouts: u32,\n}\n\nimpl jb_aggregation__update_dns_metrics {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(468u16, 48, true)\n    }\n}\n\nimpl Default for jb_aggregation__update_dns_metrics {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UPDATE_DNS_METRICS_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod update_dns_metrics_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__update_dns_metrics>();\n        let align = align_of::<jb_aggregation__update_dns_metrics>();\n        let padded_raw_size = (UPDATE_DNS_METRICS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__update_dns_metrics, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, direction),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, active_sockets),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, sum_total_time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, sum_processing_time_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, requests_a),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, requests_aaaa),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, responses),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_aggregation__update_dns_metrics, timeouts),\n            44usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_aggregation__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_aggregation__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_aggregation__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_aggregation__pulse>();\n        let align = align_of::<jb_aggregation__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_aggregation__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![\n        jb_aggregation__agg_root_start::metadata(),\n        jb_aggregation__agg_root_end::metadata(),\n        jb_aggregation__update_node::metadata(),\n        jb_aggregation__update_tcp_metrics::metadata(),\n        jb_aggregation__update_udp_metrics::metadata(),\n        jb_aggregation__update_http_metrics::metadata(),\n        jb_aggregation__update_dns_metrics::metadata(),\n        jb_aggregation__pulse::metadata(),\n    ]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_cloud_collector\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::cloud_collector\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_cloud_collector_encode_pulse(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_cloud_collector__pulse = jb_cloud_collector__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::cloud_collector\n//\n// g_type: u8\n// g_size: 2\n// g_shift: 31\n// hash_shift: 29\n// hash_mask: 3\n// n_keys: 1\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const CLOUD_COLLECTOR_HASH_SIZE: u32 = 4u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 2] = [0, 2];\n\n#[inline]\n#[allow(dead_code)]\npub fn cloud_collector_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 31) as usize] as u32;\n    (k >> 29).wrapping_add(g) & 3u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/cloud_collector/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_cloud_collector__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_cloud_collector__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_cloud_collector__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_cloud_collector__pulse>();\n        let align = align_of::<jb_cloud_collector__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_cloud_collector__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![jb_cloud_collector__pulse::metadata(),]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_ingest\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::ingest\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_info = jb_ingest__pid_info {\n        _rpc_id: 301 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_close_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_close_info = jb_ingest__pid_close_info {\n        _rpc_id: 306 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_info_create_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n    cgroup: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_info_create_deprecated = jb_ingest__pid_info_create_deprecated {\n        _rpc_id: 393 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_info_create(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n    cgroup: u64,\n    parent_pid: i32,\n    cmdline: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 36 as u32;\n    __consumed = __consumed.saturating_add(cmdline.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_cmdline: &[u8] =\n        unsafe { slice::from_raw_parts(cmdline.buf as *const u8, cmdline.len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_info_create = jb_ingest__pid_info_create {\n        _rpc_id: 546 as u16,\n        _len: __consumed as u16,\n        pid,\n        cgroup,\n        parent_pid,\n        comm: __sl_comm.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_cmdline.is_empty() {\n        let __len = __sl_cmdline.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cmdline);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_cgroup_move(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    cgroup: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_cgroup_move = jb_ingest__pid_cgroup_move {\n        _rpc_id: 397 as u16,\n        pid,\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_set_comm(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    comm: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] = unsafe { slice::from_raw_parts(comm, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_set_comm = jb_ingest__pid_set_comm {\n        _rpc_id: 399 as u16,\n        comm: __sl_comm.try_into().unwrap(),\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_set_cmdline(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    cmdline: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 8 as u32;\n    __consumed = __consumed.saturating_add(cmdline.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_cmdline: &[u8] =\n        unsafe { slice::from_raw_parts(cmdline.buf as *const u8, cmdline.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_set_cmdline = jb_ingest__pid_set_cmdline {\n        _rpc_id: 547 as u16,\n        _len: __consumed as u16,\n        pid,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 8 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 8 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_cmdline.is_empty() {\n        let __len = __sl_cmdline.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cmdline);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_tracked_process_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__tracked_process_start = jb_ingest__tracked_process_start {\n        _rpc_id: 500 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_tracked_process_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__tracked_process_end = jb_ingest__tracked_process_end {\n        _rpc_id: 501 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_tgid(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    tgid: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_tgid = jb_ingest__set_tgid {\n        _rpc_id: 502 as u16,\n        tgid,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_cgroup(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    cgroup: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_cgroup = jb_ingest__set_cgroup {\n        _rpc_id: 503 as u16,\n        cgroup,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_command(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    command: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(command.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_command: &[u8] =\n        unsafe { slice::from_raw_parts(command.buf as *const u8, command.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_command = jb_ingest__set_command {\n        _rpc_id: 504 as u16,\n        _len: __consumed as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_command.is_empty() {\n        let __len = __sl_command.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_command);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pid_exit(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    tgid: u64,\n    pid: u32,\n    exit_code: i32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 28 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pid_exit = jb_ingest__pid_exit {\n        _rpc_id: 517 as u16,\n        pid,\n        tgid,\n        _ref,\n        exit_code,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 28 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 28 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_cgroup_create_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cgroup_parent: u64,\n    name: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 88 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] = unsafe { slice::from_raw_parts(name, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__cgroup_create_deprecated = jb_ingest__cgroup_create_deprecated {\n        _rpc_id: 394 as u16,\n        name: __sl_name.try_into().unwrap(),\n        cgroup,\n        cgroup_parent,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_cgroup_create(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cgroup_parent: u64,\n    name: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 280 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] = unsafe { slice::from_raw_parts(name, 256) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__cgroup_create = jb_ingest__cgroup_create {\n        _rpc_id: 544 as u16,\n        name: __sl_name.try_into().unwrap(),\n        cgroup,\n        cgroup_parent,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 280 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 280 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_cgroup_close(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__cgroup_close = jb_ingest__cgroup_close {\n        _rpc_id: 395 as u16,\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_container_metadata(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    id: JbBlob,\n    name: JbBlob,\n    image: JbBlob,\n    ip_addr: JbBlob,\n    cluster: JbBlob,\n    container_name: JbBlob,\n    task_family: JbBlob,\n    task_version: JbBlob,\n    ns: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 28 as u32;\n    __consumed = __consumed.saturating_add(id.len as u32);\n    __consumed = __consumed.saturating_add(name.len as u32);\n    __consumed = __consumed.saturating_add(image.len as u32);\n    __consumed = __consumed.saturating_add(ip_addr.len as u32);\n    __consumed = __consumed.saturating_add(cluster.len as u32);\n    __consumed = __consumed.saturating_add(container_name.len as u32);\n    __consumed = __consumed.saturating_add(task_family.len as u32);\n    __consumed = __consumed.saturating_add(task_version.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_id: &[u8] = unsafe { slice::from_raw_parts(id.buf as *const u8, id.len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n    let __sl_image: &[u8] =\n        unsafe { slice::from_raw_parts(image.buf as *const u8, image.len as usize) };\n    let __sl_ip_addr: &[u8] =\n        unsafe { slice::from_raw_parts(ip_addr.buf as *const u8, ip_addr.len as usize) };\n    let __sl_cluster: &[u8] =\n        unsafe { slice::from_raw_parts(cluster.buf as *const u8, cluster.len as usize) };\n    let __sl_container_name: &[u8] = unsafe {\n        slice::from_raw_parts(container_name.buf as *const u8, container_name.len as usize)\n    };\n    let __sl_task_family: &[u8] =\n        unsafe { slice::from_raw_parts(task_family.buf as *const u8, task_family.len as usize) };\n    let __sl_task_version: &[u8] =\n        unsafe { slice::from_raw_parts(task_version.buf as *const u8, task_version.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__container_metadata = jb_ingest__container_metadata {\n        _rpc_id: 396 as u16,\n        _len: __consumed as u16,\n        id: (__sl_id.len() as u16),\n        name: (__sl_name.len() as u16),\n        cgroup,\n        image: (__sl_image.len() as u16),\n        ip_addr: (__sl_ip_addr.len() as u16),\n        cluster: (__sl_cluster.len() as u16),\n        container_name: (__sl_container_name.len() as u16),\n        task_family: (__sl_task_family.len() as u16),\n        task_version: (__sl_task_version.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 28 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 28 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_id.is_empty() {\n        let __len = __sl_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_id);\n        __off += __len;\n    }\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n    if !__sl_image.is_empty() {\n        let __len = __sl_image.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_image);\n        __off += __len;\n    }\n    if !__sl_ip_addr.is_empty() {\n        let __len = __sl_ip_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ip_addr);\n        __off += __len;\n    }\n    if !__sl_cluster.is_empty() {\n        let __len = __sl_cluster.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cluster);\n        __off += __len;\n    }\n    if !__sl_container_name.is_empty() {\n        let __len = __sl_container_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_name);\n        __off += __len;\n    }\n    if !__sl_task_family.is_empty() {\n        let __len = __sl_task_family.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_task_family);\n        __off += __len;\n    }\n    if !__sl_task_version.is_empty() {\n        let __len = __sl_task_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_task_version);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_name(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    _deprecated_pod_uid: JbBlob,\n    name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(_deprecated_pod_uid.len as u32);\n    __consumed = __consumed.saturating_add(name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl__deprecated_pod_uid: &[u8] = unsafe {\n        slice::from_raw_parts(\n            _deprecated_pod_uid.buf as *const u8,\n            _deprecated_pod_uid.len as usize,\n        )\n    };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_name = jb_ingest__pod_name {\n        _rpc_id: 410 as u16,\n        _len: __consumed as u16,\n        _deprecated_pod_uid: (__sl__deprecated_pod_uid.len() as u16),\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl__deprecated_pod_uid.is_empty() {\n        let __len = __sl__deprecated_pod_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl__deprecated_pod_uid);\n        __off += __len;\n    }\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_nomad_metadata(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    ns: JbBlob,\n    group_name: JbBlob,\n    task_name: JbBlob,\n    job_name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 18 as u32;\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    __consumed = __consumed.saturating_add(group_name.len as u32);\n    __consumed = __consumed.saturating_add(task_name.len as u32);\n    __consumed = __consumed.saturating_add(job_name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n    let __sl_group_name: &[u8] =\n        unsafe { slice::from_raw_parts(group_name.buf as *const u8, group_name.len as usize) };\n    let __sl_task_name: &[u8] =\n        unsafe { slice::from_raw_parts(task_name.buf as *const u8, task_name.len as usize) };\n    let __sl_job_name: &[u8] =\n        unsafe { slice::from_raw_parts(job_name.buf as *const u8, job_name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__nomad_metadata = jb_ingest__nomad_metadata {\n        _rpc_id: 508 as u16,\n        _len: __consumed as u16,\n        ns: (__sl_ns.len() as u16),\n        group_name: (__sl_group_name.len() as u16),\n        cgroup,\n        task_name: (__sl_task_name.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 18 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 18 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n    if !__sl_group_name.is_empty() {\n        let __len = __sl_group_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_group_name);\n        __off += __len;\n    }\n    if !__sl_task_name.is_empty() {\n        let __len = __sl_task_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_task_name);\n        __off += __len;\n    }\n    if !__sl_job_name.is_empty() {\n        let __len = __sl_job_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_job_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_k8s_metadata(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    container_name: JbBlob,\n    pod_name: JbBlob,\n    pod_ns: JbBlob,\n    pod_uid: JbBlob,\n    sandbox_uid: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 20 as u32;\n    __consumed = __consumed.saturating_add(container_name.len as u32);\n    __consumed = __consumed.saturating_add(pod_name.len as u32);\n    __consumed = __consumed.saturating_add(pod_ns.len as u32);\n    __consumed = __consumed.saturating_add(pod_uid.len as u32);\n    __consumed = __consumed.saturating_add(sandbox_uid.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_container_name: &[u8] = unsafe {\n        slice::from_raw_parts(container_name.buf as *const u8, container_name.len as usize)\n    };\n    let __sl_pod_name: &[u8] =\n        unsafe { slice::from_raw_parts(pod_name.buf as *const u8, pod_name.len as usize) };\n    let __sl_pod_ns: &[u8] =\n        unsafe { slice::from_raw_parts(pod_ns.buf as *const u8, pod_ns.len as usize) };\n    let __sl_pod_uid: &[u8] =\n        unsafe { slice::from_raw_parts(pod_uid.buf as *const u8, pod_uid.len as usize) };\n    let __sl_sandbox_uid: &[u8] =\n        unsafe { slice::from_raw_parts(sandbox_uid.buf as *const u8, sandbox_uid.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__k8s_metadata = jb_ingest__k8s_metadata {\n        _rpc_id: 512 as u16,\n        _len: __consumed as u16,\n        container_name: (__sl_container_name.len() as u16),\n        pod_name: (__sl_pod_name.len() as u16),\n        cgroup,\n        pod_ns: (__sl_pod_ns.len() as u16),\n        pod_uid: (__sl_pod_uid.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_container_name.is_empty() {\n        let __len = __sl_container_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_name);\n        __off += __len;\n    }\n    if !__sl_pod_name.is_empty() {\n        let __len = __sl_pod_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_name);\n        __off += __len;\n    }\n    if !__sl_pod_ns.is_empty() {\n        let __len = __sl_pod_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_ns);\n        __off += __len;\n    }\n    if !__sl_pod_uid.is_empty() {\n        let __len = __sl_pod_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_uid);\n        __off += __len;\n    }\n    if !__sl_sandbox_uid.is_empty() {\n        let __len = __sl_sandbox_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_sandbox_uid);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_k8s_metadata_port(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    port: u16,\n    protocol: u8,\n    name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__k8s_metadata_port = jb_ingest__k8s_metadata_port {\n        _rpc_id: 513 as u16,\n        _len: __consumed as u16,\n        port,\n        protocol,\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_container_resource_limits_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cpu_shares: u16,\n    cpu_period: u16,\n    cpu_quota: u16,\n    memory_swappiness: u8,\n    memory_limit: u64,\n    memory_soft_limit: u64,\n    total_memory_limit: i64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 41 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__container_resource_limits_deprecated =\n        jb_ingest__container_resource_limits_deprecated {\n            _rpc_id: 514 as u16,\n            cpu_shares,\n            cpu_period,\n            cpu_quota,\n            cgroup,\n            memory_limit,\n            memory_soft_limit,\n            total_memory_limit,\n            memory_swappiness,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 41 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 41 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_container_resource_limits(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    cpu_shares: u16,\n    cpu_period: u32,\n    cpu_quota: u32,\n    memory_swappiness: u8,\n    memory_limit: u64,\n    memory_soft_limit: u64,\n    total_memory_limit: i64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 45 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__container_resource_limits = jb_ingest__container_resource_limits {\n        _rpc_id: 518 as u16,\n        cpu_shares,\n        cpu_period,\n        cgroup,\n        memory_limit,\n        memory_soft_limit,\n        total_memory_limit,\n        cpu_quota,\n        memory_swappiness,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 45 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 45 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_container_annotation(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cgroup: u64,\n    key: JbBlob,\n    value: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(key.len as u32);\n    __consumed = __consumed.saturating_add(value.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_key: &[u8] = unsafe { slice::from_raw_parts(key.buf as *const u8, key.len as usize) };\n    let __sl_value: &[u8] =\n        unsafe { slice::from_raw_parts(value.buf as *const u8, value.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__container_annotation = jb_ingest__container_annotation {\n        _rpc_id: 538 as u16,\n        _len: __consumed as u16,\n        key: (__sl_key.len() as u16),\n        cgroup,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_key.is_empty() {\n        let __len = __sl_key.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_key);\n        __off += __len;\n    }\n    if !__sl_value.is_empty() {\n        let __len = __sl_value.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_value);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_new_sock_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__new_sock_info = jb_ingest__new_sock_info {\n        _rpc_id: 302 as u16,\n        pid,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_state_ipv4(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    dest: u32,\n    src: u32,\n    dport: u16,\n    sport: u16,\n    sk: u64,\n    tx_rx: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 26 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_state_ipv4 = jb_ingest__set_state_ipv4 {\n        _rpc_id: 303 as u16,\n        dport,\n        dest,\n        sk,\n        src,\n        tx_rx,\n        sport,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 26 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 26 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_state_ipv6(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    dest: *const u8,\n    src: *const u8,\n    dport: u16,\n    sport: u16,\n    sk: u64,\n    tx_rx: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 50 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_dest: &[u8] = unsafe { slice::from_raw_parts(dest, 16) };\n    let __sl_src: &[u8] = unsafe { slice::from_raw_parts(src, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_state_ipv6 = jb_ingest__set_state_ipv6 {\n        _rpc_id: 304 as u16,\n        dport,\n        tx_rx,\n        sk,\n        sport,\n        dest: __sl_dest.try_into().unwrap(),\n        src: __sl_src.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 50 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 50 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_socket_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    diff_bytes: u64,\n    diff_delivered: u32,\n    diff_retrans: u32,\n    max_srtt: u32,\n    is_rx: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__socket_stats = jb_ingest__socket_stats {\n        _rpc_id: 326 as u16,\n        is_rx,\n        diff_delivered,\n        sk,\n        diff_bytes,\n        diff_retrans,\n        max_srtt,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_nat_remapping(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    src: u32,\n    dst: u32,\n    sport: u16,\n    dport: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 22 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__nat_remapping = jb_ingest__nat_remapping {\n        _rpc_id: 360 as u16,\n        sport,\n        src,\n        sk,\n        dst,\n        dport,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 22 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 22 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_close_sock_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__close_sock_info = jb_ingest__close_sock_info {\n        _rpc_id: 308 as u16,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_syn_timeout(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__syn_timeout = jb_ingest__syn_timeout {\n        _rpc_id: 398 as u16,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_http_response(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    pid: u32,\n    code: u16,\n    latency_ns: u64,\n    client_server: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 25 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__http_response = jb_ingest__http_response {\n        _rpc_id: 401 as u16,\n        code,\n        pid,\n        sk,\n        latency_ns,\n        client_server,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 25 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 25 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_tcp_reset(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk: u64,\n    is_rx: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__tcp_reset = jb_ingest__tcp_reset {\n        _rpc_id: 519 as u16,\n        is_rx,\n        sk,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_process_steady_state(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    time: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__process_steady_state = jb_ingest__process_steady_state {\n        _rpc_id: 307 as u16,\n        time,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_socket_steady_state(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    time: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__socket_steady_state = jb_ingest__socket_steady_state {\n        _rpc_id: 309 as u16,\n        time,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_version_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    major: u32,\n    minor: u32,\n    patch: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__version_info = jb_ingest__version_info {\n        _rpc_id: 310 as u16,\n        major,\n        minor,\n        patch,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_node_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    az: JbBlob,\n    role: JbBlob,\n    instance_id: JbBlob,\n    instance_type: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 10 as u32;\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(instance_id.len as u32);\n    __consumed = __consumed.saturating_add(instance_type.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_instance_id: &[u8] =\n        unsafe { slice::from_raw_parts(instance_id.buf as *const u8, instance_id.len as usize) };\n    let __sl_instance_type: &[u8] = unsafe {\n        slice::from_raw_parts(instance_type.buf as *const u8, instance_type.len as usize)\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_node_info = jb_ingest__set_node_info {\n        _rpc_id: 415 as u16,\n        _len: __consumed as u16,\n        az: (__sl_az.len() as u16),\n        role: (__sl_role.len() as u16),\n        instance_id: (__sl_instance_id.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 10 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 10 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_instance_id.is_empty() {\n        let __len = __sl_instance_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_id);\n        __off += __len;\n    }\n    if !__sl_instance_type.is_empty() {\n        let __len = __sl_instance_type.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_type);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_config_label(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    key: JbBlob,\n    value: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 6 as u32;\n    __consumed = __consumed.saturating_add(key.len as u32);\n    __consumed = __consumed.saturating_add(value.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_key: &[u8] = unsafe { slice::from_raw_parts(key.buf as *const u8, key.len as usize) };\n    let __sl_value: &[u8] =\n        unsafe { slice::from_raw_parts(value.buf as *const u8, value.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_config_label = jb_ingest__set_config_label {\n        _rpc_id: 416 as u16,\n        _len: __consumed as u16,\n        key: (__sl_key.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 6 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 6 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_key.is_empty() {\n        let __len = __sl_key.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_key);\n        __off += __len;\n    }\n    if !__sl_value.is_empty() {\n        let __len = __sl_value.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_value);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_availability_zone_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    retcode: u8,\n    az: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 19 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_availability_zone_deprecated =\n        jb_ingest__set_availability_zone_deprecated {\n            _rpc_id: 321 as u16,\n            retcode,\n            az: __sl_az.try_into().unwrap(),\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 19 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 19 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_iam_role_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    retcode: u8,\n    role: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 67 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_role: &[u8] = unsafe { slice::from_raw_parts(role, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_iam_role_deprecated = jb_ingest__set_iam_role_deprecated {\n        _rpc_id: 322 as u16,\n        retcode,\n        role: __sl_role.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 67 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 67 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_instance_id_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    retcode: u8,\n    id: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 20 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_id: &[u8] = unsafe { slice::from_raw_parts(id, 17) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_instance_id_deprecated = jb_ingest__set_instance_id_deprecated {\n        _rpc_id: 323 as u16,\n        retcode,\n        id: __sl_id.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_instance_type_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    retcode: u8,\n    val: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 20 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_val: &[u8] = unsafe { slice::from_raw_parts(val, 17) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_instance_type_deprecated = jb_ingest__set_instance_type_deprecated {\n        _rpc_id: 324 as u16,\n        retcode,\n        val: __sl_val.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_dns_response_fake(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    total_dn_len: u16,\n    ips: JbBlob,\n    domain_name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 8 as u32;\n    __consumed = __consumed.saturating_add(ips.len as u32);\n    __consumed = __consumed.saturating_add(domain_name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_ips: &[u8] = unsafe { slice::from_raw_parts(ips.buf as *const u8, ips.len as usize) };\n    let __sl_domain_name: &[u8] =\n        unsafe { slice::from_raw_parts(domain_name.buf as *const u8, domain_name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__dns_response_fake = jb_ingest__dns_response_fake {\n        _rpc_id: 325 as u16,\n        _len: __consumed as u16,\n        total_dn_len,\n        ips: (__sl_ips.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 8 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 8 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_ips.is_empty() {\n        let __len = __sl_ips.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ips);\n        __off += __len;\n    }\n    if !__sl_domain_name.is_empty() {\n        let __len = __sl_domain_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_domain_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_dns_response_dep_a_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    total_dn_len: u16,\n    domain_name: JbBlob,\n    ipv4_addrs: JbBlob,\n    ipv6_addrs: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 10 as u32;\n    __consumed = __consumed.saturating_add(domain_name.len as u32);\n    __consumed = __consumed.saturating_add(ipv4_addrs.len as u32);\n    __consumed = __consumed.saturating_add(ipv6_addrs.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_domain_name: &[u8] =\n        unsafe { slice::from_raw_parts(domain_name.buf as *const u8, domain_name.len as usize) };\n    let __sl_ipv4_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv4_addrs.buf as *const u8, ipv4_addrs.len as usize) };\n    let __sl_ipv6_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv6_addrs.buf as *const u8, ipv6_addrs.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__dns_response_dep_a_deprecated =\n        jb_ingest__dns_response_dep_a_deprecated {\n            _rpc_id: 391 as u16,\n            _len: __consumed as u16,\n            total_dn_len,\n            domain_name: (__sl_domain_name.len() as u16),\n            ipv4_addrs: (__sl_ipv4_addrs.len() as u16),\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 10 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 10 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_domain_name.is_empty() {\n        let __len = __sl_domain_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_domain_name);\n        __off += __len;\n    }\n    if !__sl_ipv4_addrs.is_empty() {\n        let __len = __sl_ipv4_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv4_addrs);\n        __off += __len;\n    }\n    if !__sl_ipv6_addrs.is_empty() {\n        let __len = __sl_ipv6_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv6_addrs);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_set_config_label_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    key: *const u8,\n    val: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 62 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_key: &[u8] = unsafe { slice::from_raw_parts(key, 20) };\n    let __sl_val: &[u8] = unsafe { slice::from_raw_parts(val, 40) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__set_config_label_deprecated = jb_ingest__set_config_label_deprecated {\n        _rpc_id: 327 as u16,\n        key: __sl_key.try_into().unwrap(),\n        val: __sl_val.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 62 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 62 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_api_key(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    tenant: *const u8,\n    api_key: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 86 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_tenant: &[u8] = unsafe { slice::from_raw_parts(tenant, 20) };\n    let __sl_api_key: &[u8] = unsafe { slice::from_raw_parts(api_key, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__api_key = jb_ingest__api_key {\n        _rpc_id: 352 as u16,\n        tenant: __sl_tenant.try_into().unwrap(),\n        api_key: __sl_api_key.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 86 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 86 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_private_ipv4_addr(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    addr: u32,\n    vpc_id: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 28 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_vpc_id: &[u8] = unsafe { slice::from_raw_parts(vpc_id, 22) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__private_ipv4_addr = jb_ingest__private_ipv4_addr {\n        _rpc_id: 353 as u16,\n        vpc_id: __sl_vpc_id.try_into().unwrap(),\n        addr,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 28 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 28 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_ipv6_addr(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    addr: *const u8,\n    vpc_id: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 40 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_addr: &[u8] = unsafe { slice::from_raw_parts(addr, 16) };\n    let __sl_vpc_id: &[u8] = unsafe { slice::from_raw_parts(vpc_id, 22) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__ipv6_addr = jb_ingest__ipv6_addr {\n        _rpc_id: 354 as u16,\n        addr: __sl_addr.try_into().unwrap(),\n        vpc_id: __sl_vpc_id.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_public_to_private_ipv4(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    public_addr: u32,\n    private_addr: u32,\n    vpc_id: *const u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_vpc_id: &[u8] = unsafe { slice::from_raw_parts(vpc_id, 22) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__public_to_private_ipv4 = jb_ingest__public_to_private_ipv4 {\n        _rpc_id: 355 as u16,\n        vpc_id: __sl_vpc_id.try_into().unwrap(),\n        public_addr,\n        private_addr,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_metadata_complete(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    time: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__metadata_complete = jb_ingest__metadata_complete {\n        _rpc_id: 356 as u16,\n        time,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_bpf_lost_samples(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    count: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__bpf_lost_samples = jb_ingest__bpf_lost_samples {\n        _rpc_id: 357 as u16,\n        count,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_new_legacy(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n    ip: u32,\n    owner_name: JbBlob,\n    owner_kind: u8,\n    owner_uid: JbBlob,\n    is_host_network: u8,\n    ns: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    __consumed = __consumed.saturating_add(owner_name.len as u32);\n    __consumed = __consumed.saturating_add(owner_uid.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n    let __sl_owner_name: &[u8] =\n        unsafe { slice::from_raw_parts(owner_name.buf as *const u8, owner_name.len as usize) };\n    let __sl_owner_uid: &[u8] =\n        unsafe { slice::from_raw_parts(owner_uid.buf as *const u8, owner_uid.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_new_legacy = jb_ingest__pod_new_legacy {\n        _rpc_id: 358 as u16,\n        _len: __consumed as u16,\n        ip,\n        uid: (__sl_uid.len() as u16),\n        owner_name: (__sl_owner_name.len() as u16),\n        owner_uid: (__sl_owner_uid.len() as u16),\n        owner_kind,\n        is_host_network,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n    if !__sl_owner_name.is_empty() {\n        let __len = __sl_owner_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_name);\n        __off += __len;\n    }\n    if !__sl_owner_uid.is_empty() {\n        let __len = __sl_owner_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_uid);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_new_legacy2(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n    ip: u32,\n    owner_name: JbBlob,\n    owner_kind: u8,\n    owner_uid: JbBlob,\n    is_host_network: u8,\n    ns: JbBlob,\n    version: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 18 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    __consumed = __consumed.saturating_add(owner_name.len as u32);\n    __consumed = __consumed.saturating_add(owner_uid.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n    let __sl_owner_name: &[u8] =\n        unsafe { slice::from_raw_parts(owner_name.buf as *const u8, owner_name.len as usize) };\n    let __sl_owner_uid: &[u8] =\n        unsafe { slice::from_raw_parts(owner_uid.buf as *const u8, owner_uid.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_new_legacy2 = jb_ingest__pod_new_legacy2 {\n        _rpc_id: 414 as u16,\n        _len: __consumed as u16,\n        ip,\n        uid: (__sl_uid.len() as u16),\n        owner_name: (__sl_owner_name.len() as u16),\n        owner_uid: (__sl_owner_uid.len() as u16),\n        ns: (__sl_ns.len() as u16),\n        owner_kind,\n        is_host_network,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 18 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 18 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n    if !__sl_owner_name.is_empty() {\n        let __len = __sl_owner_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_name);\n        __off += __len;\n    }\n    if !__sl_owner_uid.is_empty() {\n        let __len = __sl_owner_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_uid);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_new_with_name(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n    ip: u32,\n    owner_name: JbBlob,\n    pod_name: JbBlob,\n    owner_kind: u8,\n    owner_uid: JbBlob,\n    is_host_network: u8,\n    ns: JbBlob,\n    version: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 20 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    __consumed = __consumed.saturating_add(owner_name.len as u32);\n    __consumed = __consumed.saturating_add(pod_name.len as u32);\n    __consumed = __consumed.saturating_add(owner_uid.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n    let __sl_owner_name: &[u8] =\n        unsafe { slice::from_raw_parts(owner_name.buf as *const u8, owner_name.len as usize) };\n    let __sl_pod_name: &[u8] =\n        unsafe { slice::from_raw_parts(pod_name.buf as *const u8, pod_name.len as usize) };\n    let __sl_owner_uid: &[u8] =\n        unsafe { slice::from_raw_parts(owner_uid.buf as *const u8, owner_uid.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_new_with_name = jb_ingest__pod_new_with_name {\n        _rpc_id: 515 as u16,\n        _len: __consumed as u16,\n        ip,\n        uid: (__sl_uid.len() as u16),\n        owner_name: (__sl_owner_name.len() as u16),\n        pod_name: (__sl_pod_name.len() as u16),\n        owner_uid: (__sl_owner_uid.len() as u16),\n        ns: (__sl_ns.len() as u16),\n        owner_kind,\n        is_host_network,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n    if !__sl_owner_name.is_empty() {\n        let __len = __sl_owner_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_name);\n        __off += __len;\n    }\n    if !__sl_pod_name.is_empty() {\n        let __len = __sl_pod_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_name);\n        __off += __len;\n    }\n    if !__sl_owner_uid.is_empty() {\n        let __len = __sl_owner_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_uid);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_container_legacy(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n    container_id: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 6 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    __consumed = __consumed.saturating_add(container_id.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n    let __sl_container_id: &[u8] =\n        unsafe { slice::from_raw_parts(container_id.buf as *const u8, container_id.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_container_legacy = jb_ingest__pod_container_legacy {\n        _rpc_id: 400 as u16,\n        _len: __consumed as u16,\n        uid: (__sl_uid.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 6 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 6 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n    if !__sl_container_id.is_empty() {\n        let __len = __sl_container_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_id);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_container(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n    container_id: JbBlob,\n    container_name: JbBlob,\n    container_image: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 10 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    __consumed = __consumed.saturating_add(container_id.len as u32);\n    __consumed = __consumed.saturating_add(container_name.len as u32);\n    __consumed = __consumed.saturating_add(container_image.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n    let __sl_container_id: &[u8] =\n        unsafe { slice::from_raw_parts(container_id.buf as *const u8, container_id.len as usize) };\n    let __sl_container_name: &[u8] = unsafe {\n        slice::from_raw_parts(container_name.buf as *const u8, container_name.len as usize)\n    };\n    let __sl_container_image: &[u8] = unsafe {\n        slice::from_raw_parts(\n            container_image.buf as *const u8,\n            container_image.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_container = jb_ingest__pod_container {\n        _rpc_id: 494 as u16,\n        _len: __consumed as u16,\n        uid: (__sl_uid.len() as u16),\n        container_id: (__sl_container_id.len() as u16),\n        container_name: (__sl_container_name.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 10 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 10 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n    if !__sl_container_id.is_empty() {\n        let __len = __sl_container_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_id);\n        __off += __len;\n    }\n    if !__sl_container_name.is_empty() {\n        let __len = __sl_container_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_name);\n        __off += __len;\n    }\n    if !__sl_container_image.is_empty() {\n        let __len = __sl_container_image.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_container_image);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_delete(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    uid: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 4 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_delete = jb_ingest__pod_delete {\n        _rpc_id: 359 as u16,\n        _len: __consumed as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 4 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 4 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pod_resync(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    resync_count: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pod_resync = jb_ingest__pod_resync {\n        _rpc_id: 390 as u16,\n        resync_count,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_span_duration_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    duration: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__span_duration_info = jb_ingest__span_duration_info {\n        _rpc_id: 351 as u16,\n        duration,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_heartbeat(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__heartbeat = jb_ingest__heartbeat {\n        _rpc_id: 392 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_connect(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    collector_type: u8,\n    hostname: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 5 as u32;\n    __consumed = __consumed.saturating_add(hostname.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_hostname: &[u8] =\n        unsafe { slice::from_raw_parts(hostname.buf as *const u8, hostname.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__connect = jb_ingest__connect {\n        _rpc_id: 548 as u16,\n        _len: __consumed as u16,\n        collector_type,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 5 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 5 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_hostname.is_empty() {\n        let __len = __sl_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_hostname);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_health_check(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    client_type: u8,\n    origin: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 5 as u32;\n    __consumed = __consumed.saturating_add(origin.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_origin: &[u8] =\n        unsafe { slice::from_raw_parts(origin.buf as *const u8, origin.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__health_check = jb_ingest__health_check {\n        _rpc_id: 409 as u16,\n        _len: __consumed as u16,\n        client_type,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 5 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 5 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_origin.is_empty() {\n        let __len = __sl_origin.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_origin);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_log_message(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    log_level: u8,\n    message: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 5 as u32;\n    __consumed = __consumed.saturating_add(message.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_message: &[u8] =\n        unsafe { slice::from_raw_parts(message.buf as *const u8, message.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__log_message = jb_ingest__log_message {\n        _rpc_id: 411 as u16,\n        _len: __consumed as u16,\n        log_level,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 5 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 5 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_message.is_empty() {\n        let __len = __sl_message.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_message);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_agent_resource_usage(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    user_mode_time_us: u64,\n    kernel_mode_time_us: u64,\n    max_resident_set_size: u64,\n    minor_page_faults: u32,\n    major_page_faults: u32,\n    block_input_count: u32,\n    block_output_count: u32,\n    voluntary_context_switch_count: u32,\n    involuntary_context_switch_count: u32,\n    cpu_usage_by_agent: u16,\n    cpu_idle: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 54 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__agent_resource_usage = jb_ingest__agent_resource_usage {\n        _rpc_id: 412 as u16,\n        cpu_usage_by_agent,\n        minor_page_faults,\n        user_mode_time_us,\n        kernel_mode_time_us,\n        max_resident_set_size,\n        major_page_faults,\n        block_input_count,\n        block_output_count,\n        voluntary_context_switch_count,\n        involuntary_context_switch_count,\n        cpu_idle,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 54 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 54 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_cloud_platform(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cloud_platform: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 4 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__cloud_platform = jb_ingest__cloud_platform {\n        _rpc_id: 413 as u16,\n        cloud_platform,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 4 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 4 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_os_info_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    os: u8,\n    flavor: u8,\n    kernel_version: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 6 as u32;\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__os_info_deprecated = jb_ingest__os_info_deprecated {\n        _rpc_id: 419 as u16,\n        _len: __consumed as u16,\n        os,\n        flavor,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 6 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 6 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_os_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    os: u8,\n    flavor: u8,\n    os_version: JbBlob,\n    kernel_version: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 8 as u32;\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__os_info = jb_ingest__os_info {\n        _rpc_id: 545 as u16,\n        _len: __consumed as u16,\n        os_version: (__sl_os_version.len() as u16),\n        os,\n        flavor,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 8 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 8 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_kernel_headers_source(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    source: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 3 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__kernel_headers_source = jb_ingest__kernel_headers_source {\n        _rpc_id: 420 as u16,\n        source,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 3 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 3 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_entrypoint_error(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    error: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 3 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__entrypoint_error = jb_ingest__entrypoint_error {\n        _rpc_id: 491 as u16,\n        error,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 3 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 3 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_bpf_compiled(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__bpf_compiled = jb_ingest__bpf_compiled {\n        _rpc_id: 492 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_begin_telemetry(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__begin_telemetry = jb_ingest__begin_telemetry {\n        _rpc_id: 493 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_cloud_platform_account_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    account_id: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 4 as u32;\n    __consumed = __consumed.saturating_add(account_id.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_account_id: &[u8] =\n        unsafe { slice::from_raw_parts(account_id.buf as *const u8, account_id.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__cloud_platform_account_info = jb_ingest__cloud_platform_account_info {\n        _rpc_id: 495 as u16,\n        _len: __consumed as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 4 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 4 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_account_id.is_empty() {\n        let __len = __sl_account_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_account_id);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_collector_health(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    status: u16,\n    detail: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 6 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__collector_health = jb_ingest__collector_health {\n        _rpc_id: 496 as u16,\n        status,\n        detail,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 6 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 6 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_system_wide_process_settings(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    clock_ticks_per_second: u64,\n    memory_page_bytes: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__system_wide_process_settings = jb_ingest__system_wide_process_settings {\n        _rpc_id: 498 as u16,\n        clock_ticks_per_second,\n        memory_page_bytes,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_collect_blob(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    blob_type: u16,\n    subtype: u64,\n    metadata: JbBlob,\n    blob: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(metadata.len as u32);\n    __consumed = __consumed.saturating_add(blob.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_metadata: &[u8] =\n        unsafe { slice::from_raw_parts(metadata.buf as *const u8, metadata.len as usize) };\n    let __sl_blob: &[u8] =\n        unsafe { slice::from_raw_parts(blob.buf as *const u8, blob.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__collect_blob = jb_ingest__collect_blob {\n        _rpc_id: 511 as u16,\n        _len: __consumed as u16,\n        blob_type,\n        metadata: (__sl_metadata.len() as u16),\n        subtype,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_metadata.is_empty() {\n        let __len = __sl_metadata.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_metadata);\n        __off += __len;\n    }\n    if !__sl_blob.is_empty() {\n        let __len = __sl_blob.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_blob);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_report_cpu_cores(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    cpu_core_count: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 8 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__report_cpu_cores = jb_ingest__report_cpu_cores {\n        _rpc_id: 536 as u16,\n        cpu_core_count,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 8 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 8 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_bpf_log(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    filename: JbBlob,\n    line: u32,\n    code: u64,\n    arg0: u64,\n    arg1: u64,\n    arg2: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 40 as u32;\n    __consumed = __consumed.saturating_add(filename.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_filename: &[u8] =\n        unsafe { slice::from_raw_parts(filename.buf as *const u8, filename.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__bpf_log = jb_ingest__bpf_log {\n        _rpc_id: 537 as u16,\n        _len: __consumed as u16,\n        line,\n        code,\n        arg0,\n        arg1,\n        arg2,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_filename.is_empty() {\n        let __len = __sl_filename.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_filename);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_aws_network_interface_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    ip: u128,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__aws_network_interface_start = jb_ingest__aws_network_interface_start {\n        _rpc_id: 406 as u16,\n        _ref,\n        ip,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_aws_network_interface_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__aws_network_interface_end = jb_ingest__aws_network_interface_end {\n        _rpc_id: 407 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_network_interface_info_deprecated(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    ip_owner_id: *const u8,\n    vpc_id: *const u8,\n    az: *const u8,\n    interface_id: JbBlob,\n    interface_type: u16,\n    instance_id: JbBlob,\n    instance_owner_id: JbBlob,\n    public_dns_name: JbBlob,\n    private_dns_name: JbBlob,\n    interface_description: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 80 as u32;\n    __consumed = __consumed.saturating_add(interface_id.len as u32);\n    __consumed = __consumed.saturating_add(instance_id.len as u32);\n    __consumed = __consumed.saturating_add(instance_owner_id.len as u32);\n    __consumed = __consumed.saturating_add(public_dns_name.len as u32);\n    __consumed = __consumed.saturating_add(private_dns_name.len as u32);\n    __consumed = __consumed.saturating_add(interface_description.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_interface_id: &[u8] =\n        unsafe { slice::from_raw_parts(interface_id.buf as *const u8, interface_id.len as usize) };\n    let __sl_instance_id: &[u8] =\n        unsafe { slice::from_raw_parts(instance_id.buf as *const u8, instance_id.len as usize) };\n    let __sl_instance_owner_id: &[u8] = unsafe {\n        slice::from_raw_parts(\n            instance_owner_id.buf as *const u8,\n            instance_owner_id.len as usize,\n        )\n    };\n    let __sl_public_dns_name: &[u8] = unsafe {\n        slice::from_raw_parts(\n            public_dns_name.buf as *const u8,\n            public_dns_name.len as usize,\n        )\n    };\n    let __sl_private_dns_name: &[u8] = unsafe {\n        slice::from_raw_parts(\n            private_dns_name.buf as *const u8,\n            private_dns_name.len as usize,\n        )\n    };\n    let __sl_interface_description: &[u8] = unsafe {\n        slice::from_raw_parts(\n            interface_description.buf as *const u8,\n            interface_description.len as usize,\n        )\n    };\n    let __sl_ip_owner_id: &[u8] = unsafe { slice::from_raw_parts(ip_owner_id, 18) };\n    let __sl_vpc_id: &[u8] = unsafe { slice::from_raw_parts(vpc_id, 22) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__network_interface_info_deprecated =\n        jb_ingest__network_interface_info_deprecated {\n            _rpc_id: 408 as u16,\n            _len: __consumed as u16,\n            interface_id: (__sl_interface_id.len() as u16),\n            interface_type,\n            _ref,\n            instance_id: (__sl_instance_id.len() as u16),\n            instance_owner_id: (__sl_instance_owner_id.len() as u16),\n            public_dns_name: (__sl_public_dns_name.len() as u16),\n            private_dns_name: (__sl_private_dns_name.len() as u16),\n            ip_owner_id: __sl_ip_owner_id.try_into().unwrap(),\n            vpc_id: __sl_vpc_id.try_into().unwrap(),\n            az: __sl_az.try_into().unwrap(),\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 80 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 80 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_interface_id.is_empty() {\n        let __len = __sl_interface_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_interface_id);\n        __off += __len;\n    }\n    if !__sl_instance_id.is_empty() {\n        let __len = __sl_instance_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_id);\n        __off += __len;\n    }\n    if !__sl_instance_owner_id.is_empty() {\n        let __len = __sl_instance_owner_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_owner_id);\n        __off += __len;\n    }\n    if !__sl_public_dns_name.is_empty() {\n        let __len = __sl_public_dns_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_public_dns_name);\n        __off += __len;\n    }\n    if !__sl_private_dns_name.is_empty() {\n        let __len = __sl_private_dns_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_private_dns_name);\n        __off += __len;\n    }\n    if !__sl_interface_description.is_empty() {\n        let __len = __sl_interface_description.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_interface_description);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_network_interface_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    ip_owner_id: JbBlob,\n    vpc_id: JbBlob,\n    az: JbBlob,\n    interface_id: JbBlob,\n    interface_type: u16,\n    instance_id: JbBlob,\n    instance_owner_id: JbBlob,\n    public_dns_name: JbBlob,\n    private_dns_name: JbBlob,\n    interface_description: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 30 as u32;\n    __consumed = __consumed.saturating_add(ip_owner_id.len as u32);\n    __consumed = __consumed.saturating_add(vpc_id.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(interface_id.len as u32);\n    __consumed = __consumed.saturating_add(instance_id.len as u32);\n    __consumed = __consumed.saturating_add(instance_owner_id.len as u32);\n    __consumed = __consumed.saturating_add(public_dns_name.len as u32);\n    __consumed = __consumed.saturating_add(private_dns_name.len as u32);\n    __consumed = __consumed.saturating_add(interface_description.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_ip_owner_id: &[u8] =\n        unsafe { slice::from_raw_parts(ip_owner_id.buf as *const u8, ip_owner_id.len as usize) };\n    let __sl_vpc_id: &[u8] =\n        unsafe { slice::from_raw_parts(vpc_id.buf as *const u8, vpc_id.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_interface_id: &[u8] =\n        unsafe { slice::from_raw_parts(interface_id.buf as *const u8, interface_id.len as usize) };\n    let __sl_instance_id: &[u8] =\n        unsafe { slice::from_raw_parts(instance_id.buf as *const u8, instance_id.len as usize) };\n    let __sl_instance_owner_id: &[u8] = unsafe {\n        slice::from_raw_parts(\n            instance_owner_id.buf as *const u8,\n            instance_owner_id.len as usize,\n        )\n    };\n    let __sl_public_dns_name: &[u8] = unsafe {\n        slice::from_raw_parts(\n            public_dns_name.buf as *const u8,\n            public_dns_name.len as usize,\n        )\n    };\n    let __sl_private_dns_name: &[u8] = unsafe {\n        slice::from_raw_parts(\n            private_dns_name.buf as *const u8,\n            private_dns_name.len as usize,\n        )\n    };\n    let __sl_interface_description: &[u8] = unsafe {\n        slice::from_raw_parts(\n            interface_description.buf as *const u8,\n            interface_description.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__network_interface_info = jb_ingest__network_interface_info {\n        _rpc_id: 417 as u16,\n        _len: __consumed as u16,\n        ip_owner_id: (__sl_ip_owner_id.len() as u16),\n        vpc_id: (__sl_vpc_id.len() as u16),\n        _ref,\n        az: (__sl_az.len() as u16),\n        interface_id: (__sl_interface_id.len() as u16),\n        interface_type,\n        instance_id: (__sl_instance_id.len() as u16),\n        instance_owner_id: (__sl_instance_owner_id.len() as u16),\n        public_dns_name: (__sl_public_dns_name.len() as u16),\n        private_dns_name: (__sl_private_dns_name.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 30 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 30 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_ip_owner_id.is_empty() {\n        let __len = __sl_ip_owner_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ip_owner_id);\n        __off += __len;\n    }\n    if !__sl_vpc_id.is_empty() {\n        let __len = __sl_vpc_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_vpc_id);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_interface_id.is_empty() {\n        let __len = __sl_interface_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_interface_id);\n        __off += __len;\n    }\n    if !__sl_instance_id.is_empty() {\n        let __len = __sl_instance_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_id);\n        __off += __len;\n    }\n    if !__sl_instance_owner_id.is_empty() {\n        let __len = __sl_instance_owner_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_instance_owner_id);\n        __off += __len;\n    }\n    if !__sl_public_dns_name.is_empty() {\n        let __len = __sl_public_dns_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_public_dns_name);\n        __off += __len;\n    }\n    if !__sl_private_dns_name.is_empty() {\n        let __len = __sl_private_dns_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_private_dns_name);\n        __off += __len;\n    }\n    if !__sl_interface_description.is_empty() {\n        let __len = __sl_interface_description.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_interface_description);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_new_socket(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    pid: u32,\n    sk_id: u32,\n    laddr: *const u8,\n    lport: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 28 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_laddr: &[u8] = unsafe { slice::from_raw_parts(laddr, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_new_socket = jb_ingest__udp_new_socket {\n        _rpc_id: 328 as u16,\n        lport,\n        pid,\n        sk_id,\n        laddr: __sl_laddr.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 28 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 28 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_stats_addr_unchanged(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    is_rx: u8,\n    packets: u32,\n    bytes: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_stats_addr_unchanged = jb_ingest__udp_stats_addr_unchanged {\n        _rpc_id: 330 as u16,\n        is_rx,\n        sk_id,\n        packets,\n        bytes,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_stats_addr_changed_v4(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    is_rx: u8,\n    packets: u32,\n    bytes: u32,\n    raddr: u32,\n    rport: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 21 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_stats_addr_changed_v4 = jb_ingest__udp_stats_addr_changed_v4 {\n        _rpc_id: 341 as u16,\n        rport,\n        sk_id,\n        packets,\n        bytes,\n        raddr,\n        is_rx,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 21 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 21 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_stats_addr_changed_v6(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    is_rx: u8,\n    packets: u32,\n    bytes: u32,\n    raddr: *const u8,\n    rport: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 33 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_raddr: &[u8] = unsafe { slice::from_raw_parts(raddr, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_stats_addr_changed_v6 = jb_ingest__udp_stats_addr_changed_v6 {\n        _rpc_id: 350 as u16,\n        rport,\n        sk_id,\n        packets,\n        bytes,\n        is_rx,\n        raddr: __sl_raddr.try_into().unwrap(),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 33 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 33 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_dns_response_dep_b(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    total_dn_len: u16,\n    domain_name: JbBlob,\n    ipv4_addrs: JbBlob,\n    ipv6_addrs: JbBlob,\n    latency_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 22 as u32;\n    __consumed = __consumed.saturating_add(domain_name.len as u32);\n    __consumed = __consumed.saturating_add(ipv4_addrs.len as u32);\n    __consumed = __consumed.saturating_add(ipv6_addrs.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_domain_name: &[u8] =\n        unsafe { slice::from_raw_parts(domain_name.buf as *const u8, domain_name.len as usize) };\n    let __sl_ipv4_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv4_addrs.buf as *const u8, ipv4_addrs.len as usize) };\n    let __sl_ipv6_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv6_addrs.buf as *const u8, ipv6_addrs.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__dns_response_dep_b = jb_ingest__dns_response_dep_b {\n        _rpc_id: 402 as u16,\n        _len: __consumed as u16,\n        sk_id,\n        latency_ns,\n        total_dn_len,\n        domain_name: (__sl_domain_name.len() as u16),\n        ipv4_addrs: (__sl_ipv4_addrs.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 22 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 22 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_domain_name.is_empty() {\n        let __len = __sl_domain_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_domain_name);\n        __off += __len;\n    }\n    if !__sl_ipv4_addrs.is_empty() {\n        let __len = __sl_ipv4_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv4_addrs);\n        __off += __len;\n    }\n    if !__sl_ipv6_addrs.is_empty() {\n        let __len = __sl_ipv6_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv6_addrs);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_dns_timeout(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    total_dn_len: u16,\n    domain_name: JbBlob,\n    timeout_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 18 as u32;\n    __consumed = __consumed.saturating_add(domain_name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_domain_name: &[u8] =\n        unsafe { slice::from_raw_parts(domain_name.buf as *const u8, domain_name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__dns_timeout = jb_ingest__dns_timeout {\n        _rpc_id: 403 as u16,\n        _len: __consumed as u16,\n        sk_id,\n        timeout_ns,\n        total_dn_len,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 18 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 18 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_domain_name.is_empty() {\n        let __len = __sl_domain_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_domain_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_stats_drops_changed(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    drops: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 12 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_stats_drops_changed = jb_ingest__udp_stats_drops_changed {\n        _rpc_id: 405 as u16,\n        sk_id,\n        drops,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 12 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 12 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_dns_response(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n    total_dn_len: u16,\n    domain_name: JbBlob,\n    ipv4_addrs: JbBlob,\n    ipv6_addrs: JbBlob,\n    latency_ns: u64,\n    client_server: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 23 as u32;\n    __consumed = __consumed.saturating_add(domain_name.len as u32);\n    __consumed = __consumed.saturating_add(ipv4_addrs.len as u32);\n    __consumed = __consumed.saturating_add(ipv6_addrs.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_domain_name: &[u8] =\n        unsafe { slice::from_raw_parts(domain_name.buf as *const u8, domain_name.len as usize) };\n    let __sl_ipv4_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv4_addrs.buf as *const u8, ipv4_addrs.len as usize) };\n    let __sl_ipv6_addrs: &[u8] =\n        unsafe { slice::from_raw_parts(ipv6_addrs.buf as *const u8, ipv6_addrs.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__dns_response = jb_ingest__dns_response {\n        _rpc_id: 418 as u16,\n        _len: __consumed as u16,\n        sk_id,\n        latency_ns,\n        total_dn_len,\n        domain_name: (__sl_domain_name.len() as u16),\n        ipv4_addrs: (__sl_ipv4_addrs.len() as u16),\n        client_server,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 23 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 23 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_domain_name.is_empty() {\n        let __len = __sl_domain_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_domain_name);\n        __off += __len;\n    }\n    if !__sl_ipv4_addrs.is_empty() {\n        let __len = __sl_ipv4_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv4_addrs);\n        __off += __len;\n    }\n    if !__sl_ipv6_addrs.is_empty() {\n        let __len = __sl_ipv6_addrs.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ipv6_addrs);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_udp_destroy_socket(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    sk_id: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 8 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__udp_destroy_socket = jb_ingest__udp_destroy_socket {\n        _rpc_id: 329 as u16,\n        sk_id,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 8 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 8 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_ingest_encode_pulse(__dest: *mut u8, __dest_len: u32, __tstamp: u64) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_ingest__pulse = jb_ingest__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::ingest\n//\n// g_type: u8\n// g_size: 32\n// g_shift: 27\n// hash_shift: 20\n// hash_mask: 127\n// n_keys: 91\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const INGEST_HASH_SIZE: u32 = 128u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 32] = [\n    0, 10, 9, 0, 63, 1, 11, 0, 0, 1, 4, 31, 4, 21, 14, 0, 5, 4, 4, 1, 0, 14, 3, 1, 4, 118, 0, 0,\n    17, 1, 1, 0,\n];\n\n#[inline]\n#[allow(dead_code)]\npub fn ingest_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 27) as usize] as u32;\n    (k >> 20).wrapping_add(g) & 127u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for pid_info\npub struct pid_info {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl pid_info {\n    pub const RPC_ID: u16 = 301u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for pid_close_info\npub struct pid_close_info {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl pid_close_info {\n    pub const RPC_ID: u16 = 306u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for pid_info_create_deprecated\npub struct pid_info_create_deprecated {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n    pub cgroup: u64,\n}\n\nimpl pid_info_create_deprecated {\n    pub const RPC_ID: u16 = 393u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let cgroup = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n            cgroup: cgroup,\n        })\n    }\n}\n// Parsed struct for pid_info_create\npub struct pid_info_create {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n    pub cgroup: u64,\n    pub parent_pid: i32,\n    pub cmdline: ::std::string::String,\n}\n\nimpl pid_info_create {\n    pub const RPC_ID: u16 = 546u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 20usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let parent_pid = i32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 36usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cmdline = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n            cgroup: cgroup,\n            parent_pid: parent_pid,\n            cmdline: cmdline,\n        })\n    }\n}\n// Parsed struct for pid_cgroup_move\npub struct pid_cgroup_move {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub cgroup: u64,\n}\n\nimpl pid_cgroup_move {\n    pub const RPC_ID: u16 = 397u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            cgroup: cgroup,\n        })\n    }\n}\n// Parsed struct for pid_set_comm\npub struct pid_set_comm {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub comm: [u8; 16],\n}\n\nimpl pid_set_comm {\n    pub const RPC_ID: u16 = 399u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n        let comm = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            comm: comm,\n        })\n    }\n}\n// Parsed struct for pid_set_cmdline\npub struct pid_set_cmdline {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub cmdline: ::std::string::String,\n}\n\nimpl pid_set_cmdline {\n    pub const RPC_ID: u16 = 547u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 8usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cmdline = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            cmdline: cmdline,\n        })\n    }\n}\n// Parsed struct for tracked_process_start\npub struct tracked_process_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl tracked_process_start {\n    pub const RPC_ID: u16 = 500u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for tracked_process_end\npub struct tracked_process_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl tracked_process_end {\n    pub const RPC_ID: u16 = 501u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for set_tgid\npub struct set_tgid {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub tgid: u32,\n}\n\nimpl set_tgid {\n    pub const RPC_ID: u16 = 502u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let tgid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            tgid: tgid,\n        })\n    }\n}\n// Parsed struct for set_cgroup\npub struct set_cgroup {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub cgroup: u64,\n}\n\nimpl set_cgroup {\n    pub const RPC_ID: u16 = 503u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            cgroup: cgroup,\n        })\n    }\n}\n// Parsed struct for set_command\npub struct set_command {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub command: ::std::string::String,\n}\n\nimpl set_command {\n    pub const RPC_ID: u16 = 504u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let command = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            command: command,\n        })\n    }\n}\n// Parsed struct for pid_exit\npub struct pid_exit {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub tgid: u64,\n    pub pid: u32,\n    pub exit_code: i32,\n}\n\nimpl pid_exit {\n    pub const RPC_ID: u16 = 517u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 28usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let tgid = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let exit_code = i32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            tgid: tgid,\n            pid: pid,\n            exit_code: exit_code,\n        })\n    }\n}\n// Parsed struct for cgroup_create_deprecated\npub struct cgroup_create_deprecated {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n    pub name: [u8; 64],\n}\n\nimpl cgroup_create_deprecated {\n    pub const RPC_ID: u16 = 394u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 88usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n        let cgroup_parent = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let name = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cgroup_parent: cgroup_parent,\n            name: name,\n        })\n    }\n}\n// Parsed struct for cgroup_create\npub struct cgroup_create {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n    pub name: [u8; 256],\n}\n\nimpl cgroup_create {\n    pub const RPC_ID: u16 = 544u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 280usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[264usize..264usize + 8].try_into().unwrap());\n        let cgroup_parent = u64::from_ne_bytes(body[272usize..272usize + 8].try_into().unwrap());\n        let name = {\n            let mut tmp = [0u8; 256];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 256usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cgroup_parent: cgroup_parent,\n            name: name,\n        })\n    }\n}\n// Parsed struct for cgroup_close\npub struct cgroup_close {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n}\n\nimpl cgroup_close {\n    pub const RPC_ID: u16 = 395u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n        })\n    }\n}\n// Parsed struct for container_metadata\npub struct container_metadata {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub id: ::std::string::String,\n    pub name: ::std::string::String,\n    pub image: ::std::string::String,\n    pub ip_addr: ::std::string::String,\n    pub cluster: ::std::string::String,\n    pub container_name: ::std::string::String,\n    pub task_family: ::std::string::String,\n    pub task_version: ::std::string::String,\n    pub ns: ::std::string::String,\n}\n\nimpl container_metadata {\n    pub const RPC_ID: u16 = 396u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 28usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let id = if __l_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_id]).into_owned()\n        };\n        __off += __l_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __l_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_name]).into_owned()\n        };\n        __off += __l_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_image = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_image > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let image = if __l_image == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_image]).into_owned()\n        };\n        __off += __l_image;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_ip_addr = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ip_addr > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ip_addr = if __l_ip_addr == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ip_addr]).into_owned()\n        };\n        __off += __l_ip_addr;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[20usize..20usize + 2]);\n        let __l_cluster = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cluster > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cluster = if __l_cluster == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cluster]).into_owned()\n        };\n        __off += __l_cluster;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[22usize..22usize + 2]);\n        let __l_container_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_container_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_name = if __l_container_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_container_name])\n                .into_owned()\n        };\n        __off += __l_container_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_task_family = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_task_family > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let task_family = if __l_task_family == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_task_family])\n                .into_owned()\n        };\n        __off += __l_task_family;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_task_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_task_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let task_version = if __l_task_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_task_version])\n                .into_owned()\n        };\n        __off += __l_task_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            id: id,\n            name: name,\n            image: image,\n            ip_addr: ip_addr,\n            cluster: cluster,\n            container_name: container_name,\n            task_family: task_family,\n            task_version: task_version,\n            ns: ns,\n        })\n    }\n}\n// Parsed struct for pod_name\npub struct pod_name {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub _deprecated_pod_uid: ::std::string::String,\n    pub name: ::std::string::String,\n}\n\nimpl pod_name {\n    pub const RPC_ID: u16 = 410u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l__deprecated_pod_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l__deprecated_pod_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let _deprecated_pod_uid = if __l__deprecated_pod_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l__deprecated_pod_uid])\n                .into_owned()\n        };\n        __off += __l__deprecated_pod_uid;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            _deprecated_pod_uid: _deprecated_pod_uid,\n            name: name,\n        })\n    }\n}\n// Parsed struct for nomad_metadata\npub struct nomad_metadata {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub ns: ::std::string::String,\n    pub group_name: ::std::string::String,\n    pub task_name: ::std::string::String,\n    pub job_name: ::std::string::String,\n}\n\nimpl nomad_metadata {\n    pub const RPC_ID: u16 = 508u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 18usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __l_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ns]).into_owned()\n        };\n        __off += __l_ns;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_group_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_group_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let group_name = if __l_group_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_group_name])\n                .into_owned()\n        };\n        __off += __l_group_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_task_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_task_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let task_name = if __l_task_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_task_name]).into_owned()\n        };\n        __off += __l_task_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let job_name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            ns: ns,\n            group_name: group_name,\n            task_name: task_name,\n            job_name: job_name,\n        })\n    }\n}\n// Parsed struct for k8s_metadata\npub struct k8s_metadata {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub container_name: ::std::string::String,\n    pub pod_name: ::std::string::String,\n    pub pod_ns: ::std::string::String,\n    pub pod_uid: ::std::string::String,\n    pub sandbox_uid: ::std::string::String,\n}\n\nimpl k8s_metadata {\n    pub const RPC_ID: u16 = 512u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 20usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_container_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_container_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_name = if __l_container_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_container_name])\n                .into_owned()\n        };\n        __off += __l_container_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_pod_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_name = if __l_pod_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_name]).into_owned()\n        };\n        __off += __l_pod_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_pod_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_ns = if __l_pod_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_ns]).into_owned()\n        };\n        __off += __l_pod_ns;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_pod_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_uid = if __l_pod_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_uid]).into_owned()\n        };\n        __off += __l_pod_uid;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let sandbox_uid = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            container_name: container_name,\n            pod_name: pod_name,\n            pod_ns: pod_ns,\n            pod_uid: pod_uid,\n            sandbox_uid: sandbox_uid,\n        })\n    }\n}\n// Parsed struct for k8s_metadata_port\npub struct k8s_metadata_port {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub port: u16,\n    pub protocol: u8,\n    pub name: ::std::string::String,\n}\n\nimpl k8s_metadata_port {\n    pub const RPC_ID: u16 = 513u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let port = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let protocol = body[6usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            port: port,\n            protocol: protocol,\n            name: name,\n        })\n    }\n}\n// Parsed struct for container_resource_limits_deprecated\npub struct container_resource_limits_deprecated {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cpu_shares: u16,\n    pub cpu_period: u16,\n    pub cpu_quota: u16,\n    pub memory_swappiness: u8,\n    pub memory_limit: u64,\n    pub memory_soft_limit: u64,\n    pub total_memory_limit: i64,\n}\n\nimpl container_resource_limits_deprecated {\n    pub const RPC_ID: u16 = 514u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 41usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let cpu_shares = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let cpu_period = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let cpu_quota = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        let memory_swappiness = body[40usize];\n        let memory_limit = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let memory_soft_limit = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let total_memory_limit = i64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cpu_shares: cpu_shares,\n            cpu_period: cpu_period,\n            cpu_quota: cpu_quota,\n            memory_swappiness: memory_swappiness,\n            memory_limit: memory_limit,\n            memory_soft_limit: memory_soft_limit,\n            total_memory_limit: total_memory_limit,\n        })\n    }\n}\n// Parsed struct for container_resource_limits\npub struct container_resource_limits {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub cpu_shares: u16,\n    pub cpu_period: u32,\n    pub cpu_quota: u32,\n    pub memory_swappiness: u8,\n    pub memory_limit: u64,\n    pub memory_soft_limit: u64,\n    pub total_memory_limit: i64,\n}\n\nimpl container_resource_limits {\n    pub const RPC_ID: u16 = 518u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 45usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let cpu_shares = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let cpu_period = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let cpu_quota = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let memory_swappiness = body[44usize];\n        let memory_limit = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let memory_soft_limit = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let total_memory_limit = i64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            cpu_shares: cpu_shares,\n            cpu_period: cpu_period,\n            cpu_quota: cpu_quota,\n            memory_swappiness: memory_swappiness,\n            memory_limit: memory_limit,\n            memory_soft_limit: memory_soft_limit,\n            total_memory_limit: total_memory_limit,\n        })\n    }\n}\n// Parsed struct for container_annotation\npub struct container_annotation {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub key: ::std::string::String,\n    pub value: ::std::string::String,\n}\n\nimpl container_annotation {\n    pub const RPC_ID: u16 = 538u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_key = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_key > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let key = if __l_key == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_key]).into_owned()\n        };\n        __off += __l_key;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let value = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cgroup: cgroup,\n            key: key,\n            value: value,\n        })\n    }\n}\n// Parsed struct for new_sock_info\npub struct new_sock_info {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk: u64,\n}\n\nimpl new_sock_info {\n    pub const RPC_ID: u16 = 302u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for set_state_ipv4\npub struct set_state_ipv4 {\n    pub _rpc_id: u16,\n    pub dest: u32,\n    pub src: u32,\n    pub dport: u16,\n    pub sport: u16,\n    pub sk: u64,\n    pub tx_rx: u32,\n}\n\nimpl set_state_ipv4 {\n    pub const RPC_ID: u16 = 303u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 26usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let dest = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let src = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let dport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let sport = u16::from_ne_bytes(body[24usize..24usize + 2].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let tx_rx = u32::from_ne_bytes(body[20usize..20usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            dest: dest,\n            src: src,\n            dport: dport,\n            sport: sport,\n            sk: sk,\n            tx_rx: tx_rx,\n        })\n    }\n}\n// Parsed struct for set_state_ipv6\npub struct set_state_ipv6 {\n    pub _rpc_id: u16,\n    pub dest: [u8; 16],\n    pub src: [u8; 16],\n    pub dport: u16,\n    pub sport: u16,\n    pub sk: u64,\n    pub tx_rx: u32,\n}\n\nimpl set_state_ipv6 {\n    pub const RPC_ID: u16 = 304u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 50usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let dest = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 18usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let src = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 34usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let dport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let sport = u16::from_ne_bytes(body[16usize..16usize + 2].try_into().unwrap());\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let tx_rx = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            dest: dest,\n            src: src,\n            dport: dport,\n            sport: sport,\n            sk: sk,\n            tx_rx: tx_rx,\n        })\n    }\n}\n// Parsed struct for socket_stats\npub struct socket_stats {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub diff_bytes: u64,\n    pub diff_delivered: u32,\n    pub diff_retrans: u32,\n    pub max_srtt: u32,\n    pub is_rx: u8,\n}\n\nimpl socket_stats {\n    pub const RPC_ID: u16 = 326u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let diff_bytes = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let diff_delivered = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let diff_retrans = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let max_srtt = u32::from_ne_bytes(body[28usize..28usize + 4].try_into().unwrap());\n        let is_rx = body[2usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            diff_bytes: diff_bytes,\n            diff_delivered: diff_delivered,\n            diff_retrans: diff_retrans,\n            max_srtt: max_srtt,\n            is_rx: is_rx,\n        })\n    }\n}\n// Parsed struct for nat_remapping\npub struct nat_remapping {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub src: u32,\n    pub dst: u32,\n    pub sport: u16,\n    pub dport: u16,\n}\n\nimpl nat_remapping {\n    pub const RPC_ID: u16 = 360u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 22usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let src = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let dst = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let sport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let dport = u16::from_ne_bytes(body[20usize..20usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            src: src,\n            dst: dst,\n            sport: sport,\n            dport: dport,\n        })\n    }\n}\n// Parsed struct for close_sock_info\npub struct close_sock_info {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl close_sock_info {\n    pub const RPC_ID: u16 = 308u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for syn_timeout\npub struct syn_timeout {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl syn_timeout {\n    pub const RPC_ID: u16 = 398u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n        })\n    }\n}\n// Parsed struct for http_response\npub struct http_response {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub pid: u32,\n    pub code: u16,\n    pub latency_ns: u64,\n    pub client_server: u8,\n}\n\nimpl http_response {\n    pub const RPC_ID: u16 = 401u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 25usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let code = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let latency_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let client_server = body[24usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            pid: pid,\n            code: code,\n            latency_ns: latency_ns,\n            client_server: client_server,\n        })\n    }\n}\n// Parsed struct for tcp_reset\npub struct tcp_reset {\n    pub _rpc_id: u16,\n    pub sk: u64,\n    pub is_rx: u8,\n}\n\nimpl tcp_reset {\n    pub const RPC_ID: u16 = 519u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let is_rx = body[2usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk: sk,\n            is_rx: is_rx,\n        })\n    }\n}\n// Parsed struct for process_steady_state\npub struct process_steady_state {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl process_steady_state {\n    pub const RPC_ID: u16 = 307u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let time = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            time: time,\n        })\n    }\n}\n// Parsed struct for socket_steady_state\npub struct socket_steady_state {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl socket_steady_state {\n    pub const RPC_ID: u16 = 309u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let time = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            time: time,\n        })\n    }\n}\n// Parsed struct for version_info\npub struct version_info {\n    pub _rpc_id: u16,\n    pub major: u32,\n    pub minor: u32,\n    pub patch: u32,\n}\n\nimpl version_info {\n    pub const RPC_ID: u16 = 310u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let major = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let minor = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n        let patch = u32::from_ne_bytes(body[12usize..12usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            major: major,\n            minor: minor,\n            patch: patch,\n        })\n    }\n}\n// Parsed struct for set_node_info\npub struct set_node_info {\n    pub _rpc_id: u16,\n    pub az: ::std::string::String,\n    pub role: ::std::string::String,\n    pub instance_id: ::std::string::String,\n    pub instance_type: ::std::string::String,\n}\n\nimpl set_node_info {\n    pub const RPC_ID: u16 = 415u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 10usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_instance_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_instance_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_id = if __l_instance_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_instance_id])\n                .into_owned()\n        };\n        __off += __l_instance_id;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_type = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            az: az,\n            role: role,\n            instance_id: instance_id,\n            instance_type: instance_type,\n        })\n    }\n}\n// Parsed struct for set_config_label\npub struct set_config_label {\n    pub _rpc_id: u16,\n    pub key: ::std::string::String,\n    pub value: ::std::string::String,\n}\n\nimpl set_config_label {\n    pub const RPC_ID: u16 = 416u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 6usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_key = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_key > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let key = if __l_key == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_key]).into_owned()\n        };\n        __off += __l_key;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let value = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            key: key,\n            value: value,\n        })\n    }\n}\n// Parsed struct for set_availability_zone_deprecated\npub struct set_availability_zone_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub az: [u8; 16],\n}\n\nimpl set_availability_zone_deprecated {\n    pub const RPC_ID: u16 = 321u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 19usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let retcode = body[2usize];\n        let az = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 3usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            retcode: retcode,\n            az: az,\n        })\n    }\n}\n// Parsed struct for set_iam_role_deprecated\npub struct set_iam_role_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub role: [u8; 64],\n}\n\nimpl set_iam_role_deprecated {\n    pub const RPC_ID: u16 = 322u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 67usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let retcode = body[2usize];\n        let role = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 3usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            retcode: retcode,\n            role: role,\n        })\n    }\n}\n// Parsed struct for set_instance_id_deprecated\npub struct set_instance_id_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub id: [u8; 17],\n}\n\nimpl set_instance_id_deprecated {\n    pub const RPC_ID: u16 = 323u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 20usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let retcode = body[2usize];\n        let id = {\n            let mut tmp = [0u8; 17];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 17usize {\n                let off = 3usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            retcode: retcode,\n            id: id,\n        })\n    }\n}\n// Parsed struct for set_instance_type_deprecated\npub struct set_instance_type_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub val: [u8; 17],\n}\n\nimpl set_instance_type_deprecated {\n    pub const RPC_ID: u16 = 324u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 20usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let retcode = body[2usize];\n        let val = {\n            let mut tmp = [0u8; 17];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 17usize {\n                let off = 3usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            retcode: retcode,\n            val: val,\n        })\n    }\n}\n// Parsed struct for dns_response_fake\npub struct dns_response_fake {\n    pub _rpc_id: u16,\n    pub total_dn_len: u16,\n    pub ips: ::std::string::String,\n    pub domain_name: ::std::string::String,\n}\n\nimpl dns_response_fake {\n    pub const RPC_ID: u16 = 325u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let total_dn_len = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 8usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_ips = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ips > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ips = if __l_ips == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ips]).into_owned()\n        };\n        __off += __l_ips;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let domain_name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            total_dn_len: total_dn_len,\n            ips: ips,\n            domain_name: domain_name,\n        })\n    }\n}\n// Parsed struct for dns_response_dep_a_deprecated\npub struct dns_response_dep_a_deprecated {\n    pub _rpc_id: u16,\n    pub total_dn_len: u16,\n    pub domain_name: ::std::string::String,\n    pub ipv4_addrs: ::std::string::String,\n    pub ipv6_addrs: ::std::string::String,\n}\n\nimpl dns_response_dep_a_deprecated {\n    pub const RPC_ID: u16 = 391u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let total_dn_len = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 10usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_domain_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_domain_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let domain_name = if __l_domain_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_domain_name])\n                .into_owned()\n        };\n        __off += __l_domain_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_ipv4_addrs = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ipv4_addrs > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv4_addrs = if __l_ipv4_addrs == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ipv4_addrs])\n                .into_owned()\n        };\n        __off += __l_ipv4_addrs;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv6_addrs = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            total_dn_len: total_dn_len,\n            domain_name: domain_name,\n            ipv4_addrs: ipv4_addrs,\n            ipv6_addrs: ipv6_addrs,\n        })\n    }\n}\n// Parsed struct for set_config_label_deprecated\npub struct set_config_label_deprecated {\n    pub _rpc_id: u16,\n    pub key: [u8; 20],\n    pub val: [u8; 40],\n}\n\nimpl set_config_label_deprecated {\n    pub const RPC_ID: u16 = 327u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 62usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let key = {\n            let mut tmp = [0u8; 20];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 20usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let val = {\n            let mut tmp = [0u8; 40];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 40usize {\n                let off = 22usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            key: key,\n            val: val,\n        })\n    }\n}\n// Parsed struct for api_key\npub struct api_key {\n    pub _rpc_id: u16,\n    pub tenant: [u8; 20],\n    pub api_key: [u8; 64],\n}\n\nimpl api_key {\n    pub const RPC_ID: u16 = 352u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 86usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let tenant = {\n            let mut tmp = [0u8; 20];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 20usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let api_key = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 22usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            tenant: tenant,\n            api_key: api_key,\n        })\n    }\n}\n// Parsed struct for private_ipv4_addr\npub struct private_ipv4_addr {\n    pub _rpc_id: u16,\n    pub addr: u32,\n    pub vpc_id: [u8; 22],\n}\n\nimpl private_ipv4_addr {\n    pub const RPC_ID: u16 = 353u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 28usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let addr = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let vpc_id = {\n            let mut tmp = [0u8; 22];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 22usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            addr: addr,\n            vpc_id: vpc_id,\n        })\n    }\n}\n// Parsed struct for ipv6_addr\npub struct ipv6_addr {\n    pub _rpc_id: u16,\n    pub addr: [u8; 16],\n    pub vpc_id: [u8; 22],\n}\n\nimpl ipv6_addr {\n    pub const RPC_ID: u16 = 354u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 40usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let addr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let vpc_id = {\n            let mut tmp = [0u8; 22];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 22usize {\n                let off = 18usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            addr: addr,\n            vpc_id: vpc_id,\n        })\n    }\n}\n// Parsed struct for public_to_private_ipv4\npub struct public_to_private_ipv4 {\n    pub _rpc_id: u16,\n    pub public_addr: u32,\n    pub private_addr: u32,\n    pub vpc_id: [u8; 22],\n}\n\nimpl public_to_private_ipv4 {\n    pub const RPC_ID: u16 = 355u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let public_addr = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let private_addr = u32::from_ne_bytes(body[28usize..28usize + 4].try_into().unwrap());\n        let vpc_id = {\n            let mut tmp = [0u8; 22];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 22usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            public_addr: public_addr,\n            private_addr: private_addr,\n            vpc_id: vpc_id,\n        })\n    }\n}\n// Parsed struct for metadata_complete\npub struct metadata_complete {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl metadata_complete {\n    pub const RPC_ID: u16 = 356u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let time = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            time: time,\n        })\n    }\n}\n// Parsed struct for bpf_lost_samples\npub struct bpf_lost_samples {\n    pub _rpc_id: u16,\n    pub count: u64,\n}\n\nimpl bpf_lost_samples {\n    pub const RPC_ID: u16 = 357u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let count = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            count: count,\n        })\n    }\n}\n// Parsed struct for pod_new_legacy\npub struct pod_new_legacy {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n    pub ip: u32,\n    pub owner_name: ::std::string::String,\n    pub owner_kind: u8,\n    pub owner_uid: ::std::string::String,\n    pub is_host_network: u8,\n    pub ns: ::std::string::String,\n}\n\nimpl pod_new_legacy {\n    pub const RPC_ID: u16 = 358u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        let ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let owner_kind = body[14usize];\n        // dynamic string; decode later from payload\n        let is_host_network = body[15usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __l_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_uid]).into_owned()\n        };\n        __off += __l_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[10usize..10usize + 2]);\n        let __l_owner_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_name = if __l_owner_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_name])\n                .into_owned()\n        };\n        __off += __l_owner_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[12usize..12usize + 2]);\n        let __l_owner_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_uid = if __l_owner_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_uid]).into_owned()\n        };\n        __off += __l_owner_uid;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n            ip: ip,\n            owner_name: owner_name,\n            owner_kind: owner_kind,\n            owner_uid: owner_uid,\n            is_host_network: is_host_network,\n            ns: ns,\n        })\n    }\n}\n// Parsed struct for pod_new_legacy2\npub struct pod_new_legacy2 {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n    pub ip: u32,\n    pub owner_name: ::std::string::String,\n    pub owner_kind: u8,\n    pub owner_uid: ::std::string::String,\n    pub is_host_network: u8,\n    pub ns: ::std::string::String,\n    pub version: ::std::string::String,\n}\n\nimpl pod_new_legacy2 {\n    pub const RPC_ID: u16 = 414u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        let ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let owner_kind = body[16usize];\n        // dynamic string; decode later from payload\n        let is_host_network = body[17usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 18usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __l_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_uid]).into_owned()\n        };\n        __off += __l_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[10usize..10usize + 2]);\n        let __l_owner_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_name = if __l_owner_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_name])\n                .into_owned()\n        };\n        __off += __l_owner_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[12usize..12usize + 2]);\n        let __l_owner_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_uid = if __l_owner_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_uid]).into_owned()\n        };\n        __off += __l_owner_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[14usize..14usize + 2]);\n        let __l_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __l_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ns]).into_owned()\n        };\n        __off += __l_ns;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n            ip: ip,\n            owner_name: owner_name,\n            owner_kind: owner_kind,\n            owner_uid: owner_uid,\n            is_host_network: is_host_network,\n            ns: ns,\n            version: version,\n        })\n    }\n}\n// Parsed struct for pod_new_with_name\npub struct pod_new_with_name {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n    pub ip: u32,\n    pub owner_name: ::std::string::String,\n    pub pod_name: ::std::string::String,\n    pub owner_kind: u8,\n    pub owner_uid: ::std::string::String,\n    pub is_host_network: u8,\n    pub ns: ::std::string::String,\n    pub version: ::std::string::String,\n}\n\nimpl pod_new_with_name {\n    pub const RPC_ID: u16 = 515u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        let ip = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let owner_kind = body[18usize];\n        // dynamic string; decode later from payload\n        let is_host_network = body[19usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 20usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __l_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_uid]).into_owned()\n        };\n        __off += __l_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[10usize..10usize + 2]);\n        let __l_owner_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_name = if __l_owner_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_name])\n                .into_owned()\n        };\n        __off += __l_owner_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[12usize..12usize + 2]);\n        let __l_pod_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_name = if __l_pod_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_name]).into_owned()\n        };\n        __off += __l_pod_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[14usize..14usize + 2]);\n        let __l_owner_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_uid = if __l_owner_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_uid]).into_owned()\n        };\n        __off += __l_owner_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __l_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ns]).into_owned()\n        };\n        __off += __l_ns;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n            ip: ip,\n            owner_name: owner_name,\n            pod_name: pod_name,\n            owner_kind: owner_kind,\n            owner_uid: owner_uid,\n            is_host_network: is_host_network,\n            ns: ns,\n            version: version,\n        })\n    }\n}\n// Parsed struct for pod_container_legacy\npub struct pod_container_legacy {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n    pub container_id: ::std::string::String,\n}\n\nimpl pod_container_legacy {\n    pub const RPC_ID: u16 = 400u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 6usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __l_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_uid]).into_owned()\n        };\n        __off += __l_uid;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_id = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n            container_id: container_id,\n        })\n    }\n}\n// Parsed struct for pod_container\npub struct pod_container {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n    pub container_id: ::std::string::String,\n    pub container_name: ::std::string::String,\n    pub container_image: ::std::string::String,\n}\n\nimpl pod_container {\n    pub const RPC_ID: u16 = 494u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 10usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_uid = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_uid > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __l_uid == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_uid]).into_owned()\n        };\n        __off += __l_uid;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_container_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_container_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_id = if __l_container_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_container_id])\n                .into_owned()\n        };\n        __off += __l_container_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[8usize..8usize + 2]);\n        let __l_container_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_container_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_name = if __l_container_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_container_name])\n                .into_owned()\n        };\n        __off += __l_container_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let container_image = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n            container_id: container_id,\n            container_name: container_name,\n            container_image: container_image,\n        })\n    }\n}\n// Parsed struct for pod_delete\npub struct pod_delete {\n    pub _rpc_id: u16,\n    pub uid: ::std::string::String,\n}\n\nimpl pod_delete {\n    pub const RPC_ID: u16 = 359u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 4usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            uid: uid,\n        })\n    }\n}\n// Parsed struct for pod_resync\npub struct pod_resync {\n    pub _rpc_id: u16,\n    pub resync_count: u64,\n}\n\nimpl pod_resync {\n    pub const RPC_ID: u16 = 390u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let resync_count = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            resync_count: resync_count,\n        })\n    }\n}\n// Parsed struct for span_duration_info\npub struct span_duration_info {\n    pub _rpc_id: u16,\n    pub duration: u64,\n}\n\nimpl span_duration_info {\n    pub const RPC_ID: u16 = 351u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let duration = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            duration: duration,\n        })\n    }\n}\n// Parsed struct for heartbeat\npub struct heartbeat {\n    pub _rpc_id: u16,\n}\n\nimpl heartbeat {\n    pub const RPC_ID: u16 = 392u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n// Parsed struct for connect\npub struct connect {\n    pub _rpc_id: u16,\n    pub collector_type: u8,\n    pub hostname: ::std::string::String,\n}\n\nimpl connect {\n    pub const RPC_ID: u16 = 548u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let collector_type = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 5usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let hostname = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            collector_type: collector_type,\n            hostname: hostname,\n        })\n    }\n}\n// Parsed struct for health_check\npub struct health_check {\n    pub _rpc_id: u16,\n    pub client_type: u8,\n    pub origin: ::std::string::String,\n}\n\nimpl health_check {\n    pub const RPC_ID: u16 = 409u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let client_type = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 5usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let origin = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            client_type: client_type,\n            origin: origin,\n        })\n    }\n}\n// Parsed struct for log_message\npub struct log_message {\n    pub _rpc_id: u16,\n    pub log_level: u8,\n    pub message: ::std::string::String,\n}\n\nimpl log_message {\n    pub const RPC_ID: u16 = 411u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let log_level = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 5usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let message = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            log_level: log_level,\n            message: message,\n        })\n    }\n}\n// Parsed struct for agent_resource_usage\npub struct agent_resource_usage {\n    pub _rpc_id: u16,\n    pub user_mode_time_us: u64,\n    pub kernel_mode_time_us: u64,\n    pub max_resident_set_size: u64,\n    pub minor_page_faults: u32,\n    pub major_page_faults: u32,\n    pub block_input_count: u32,\n    pub block_output_count: u32,\n    pub voluntary_context_switch_count: u32,\n    pub involuntary_context_switch_count: u32,\n    pub cpu_usage_by_agent: u16,\n    pub cpu_idle: u16,\n}\n\nimpl agent_resource_usage {\n    pub const RPC_ID: u16 = 412u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 54usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let user_mode_time_us = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let kernel_mode_time_us =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let max_resident_set_size =\n            u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let minor_page_faults = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let major_page_faults = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let block_input_count = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let block_output_count = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let voluntary_context_switch_count =\n            u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let involuntary_context_switch_count =\n            u32::from_ne_bytes(body[48usize..48usize + 4].try_into().unwrap());\n        let cpu_usage_by_agent = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let cpu_idle = u16::from_ne_bytes(body[52usize..52usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            user_mode_time_us: user_mode_time_us,\n            kernel_mode_time_us: kernel_mode_time_us,\n            max_resident_set_size: max_resident_set_size,\n            minor_page_faults: minor_page_faults,\n            major_page_faults: major_page_faults,\n            block_input_count: block_input_count,\n            block_output_count: block_output_count,\n            voluntary_context_switch_count: voluntary_context_switch_count,\n            involuntary_context_switch_count: involuntary_context_switch_count,\n            cpu_usage_by_agent: cpu_usage_by_agent,\n            cpu_idle: cpu_idle,\n        })\n    }\n}\n// Parsed struct for cloud_platform\npub struct cloud_platform {\n    pub _rpc_id: u16,\n    pub cloud_platform: u16,\n}\n\nimpl cloud_platform {\n    pub const RPC_ID: u16 = 413u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cloud_platform = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cloud_platform: cloud_platform,\n        })\n    }\n}\n// Parsed struct for os_info_deprecated\npub struct os_info_deprecated {\n    pub _rpc_id: u16,\n    pub os: u8,\n    pub flavor: u8,\n    pub kernel_version: ::std::string::String,\n}\n\nimpl os_info_deprecated {\n    pub const RPC_ID: u16 = 419u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let os = body[4usize];\n        let flavor = body[5usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 6usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            os: os,\n            flavor: flavor,\n            kernel_version: kernel_version,\n        })\n    }\n}\n// Parsed struct for os_info\npub struct os_info {\n    pub _rpc_id: u16,\n    pub os: u8,\n    pub flavor: u8,\n    pub os_version: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n}\n\nimpl os_info {\n    pub const RPC_ID: u16 = 545u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let os = body[6usize];\n        let flavor = body[7usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 8usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            os: os,\n            flavor: flavor,\n            os_version: os_version,\n            kernel_version: kernel_version,\n        })\n    }\n}\n// Parsed struct for kernel_headers_source\npub struct kernel_headers_source {\n    pub _rpc_id: u16,\n    pub source: u8,\n}\n\nimpl kernel_headers_source {\n    pub const RPC_ID: u16 = 420u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 3usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let source = body[2usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            source: source,\n        })\n    }\n}\n// Parsed struct for entrypoint_error\npub struct entrypoint_error {\n    pub _rpc_id: u16,\n    pub error: u8,\n}\n\nimpl entrypoint_error {\n    pub const RPC_ID: u16 = 491u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 3usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let error = body[2usize];\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            error: error,\n        })\n    }\n}\n// Parsed struct for bpf_compiled\npub struct bpf_compiled {\n    pub _rpc_id: u16,\n}\n\nimpl bpf_compiled {\n    pub const RPC_ID: u16 = 492u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n// Parsed struct for begin_telemetry\npub struct begin_telemetry {\n    pub _rpc_id: u16,\n}\n\nimpl begin_telemetry {\n    pub const RPC_ID: u16 = 493u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n// Parsed struct for cloud_platform_account_info\npub struct cloud_platform_account_info {\n    pub _rpc_id: u16,\n    pub account_id: ::std::string::String,\n}\n\nimpl cloud_platform_account_info {\n    pub const RPC_ID: u16 = 495u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 4usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let account_id = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            account_id: account_id,\n        })\n    }\n}\n// Parsed struct for collector_health\npub struct collector_health {\n    pub _rpc_id: u16,\n    pub status: u16,\n    pub detail: u16,\n}\n\nimpl collector_health {\n    pub const RPC_ID: u16 = 496u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 6usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let status = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let detail = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            status: status,\n            detail: detail,\n        })\n    }\n}\n// Parsed struct for system_wide_process_settings\npub struct system_wide_process_settings {\n    pub _rpc_id: u16,\n    pub clock_ticks_per_second: u64,\n    pub memory_page_bytes: u64,\n}\n\nimpl system_wide_process_settings {\n    pub const RPC_ID: u16 = 498u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let clock_ticks_per_second =\n            u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let memory_page_bytes = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            clock_ticks_per_second: clock_ticks_per_second,\n            memory_page_bytes: memory_page_bytes,\n        })\n    }\n}\n// Parsed struct for collect_blob\npub struct collect_blob {\n    pub _rpc_id: u16,\n    pub blob_type: u16,\n    pub subtype: u64,\n    pub metadata: ::std::string::String,\n    pub blob: ::std::string::String,\n}\n\nimpl collect_blob {\n    pub const RPC_ID: u16 = 511u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let blob_type = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let subtype = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_metadata = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_metadata > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let metadata = if __l_metadata == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_metadata]).into_owned()\n        };\n        __off += __l_metadata;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let blob = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            blob_type: blob_type,\n            subtype: subtype,\n            metadata: metadata,\n            blob: blob,\n        })\n    }\n}\n// Parsed struct for report_cpu_cores\npub struct report_cpu_cores {\n    pub _rpc_id: u16,\n    pub cpu_core_count: u32,\n}\n\nimpl report_cpu_cores {\n    pub const RPC_ID: u16 = 536u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 8usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let cpu_core_count = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            cpu_core_count: cpu_core_count,\n        })\n    }\n}\n// Parsed struct for bpf_log\npub struct bpf_log {\n    pub _rpc_id: u16,\n    pub filename: ::std::string::String,\n    pub line: u32,\n    pub code: u64,\n    pub arg0: u64,\n    pub arg1: u64,\n    pub arg2: u64,\n}\n\nimpl bpf_log {\n    pub const RPC_ID: u16 = 537u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        // dynamic string; decode later from payload\n        let line = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let code = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let arg0 = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let arg1 = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let arg2 = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 40usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let filename = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            filename: filename,\n            line: line,\n            code: code,\n            arg0: arg0,\n            arg1: arg1,\n            arg2: arg2,\n        })\n    }\n}\n// Parsed struct for aws_network_interface_start\npub struct aws_network_interface_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip: u128,\n}\n\nimpl aws_network_interface_start {\n    pub const RPC_ID: u16 = 406u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let ip = u128::from_ne_bytes(body[16usize..16usize + 16].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            ip: ip,\n        })\n    }\n}\n// Parsed struct for aws_network_interface_end\npub struct aws_network_interface_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl aws_network_interface_end {\n    pub const RPC_ID: u16 = 407u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for network_interface_info_deprecated\npub struct network_interface_info_deprecated {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip_owner_id: [u8; 18],\n    pub vpc_id: [u8; 22],\n    pub az: [u8; 16],\n    pub interface_id: ::std::string::String,\n    pub interface_type: u16,\n    pub instance_id: ::std::string::String,\n    pub instance_owner_id: ::std::string::String,\n    pub public_dns_name: ::std::string::String,\n    pub private_dns_name: ::std::string::String,\n    pub interface_description: ::std::string::String,\n}\n\nimpl network_interface_info_deprecated {\n    pub const RPC_ID: u16 = 408u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let ip_owner_id = {\n            let mut tmp = [0u8; 18];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 18usize {\n                let off = 24usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let vpc_id = {\n            let mut tmp = [0u8; 22];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 22usize {\n                let off = 42usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let az = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 64usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        // dynamic string; decode later from payload\n        let interface_type = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 80usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_interface_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_interface_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let interface_id = if __l_interface_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_interface_id])\n                .into_owned()\n        };\n        __off += __l_interface_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_instance_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_instance_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_id = if __l_instance_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_instance_id])\n                .into_owned()\n        };\n        __off += __l_instance_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_instance_owner_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_instance_owner_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_owner_id = if __l_instance_owner_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_instance_owner_id])\n                .into_owned()\n        };\n        __off += __l_instance_owner_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[20usize..20usize + 2]);\n        let __l_public_dns_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_public_dns_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let public_dns_name = if __l_public_dns_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_public_dns_name])\n                .into_owned()\n        };\n        __off += __l_public_dns_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[22usize..22usize + 2]);\n        let __l_private_dns_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_private_dns_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let private_dns_name = if __l_private_dns_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_private_dns_name])\n                .into_owned()\n        };\n        __off += __l_private_dns_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let interface_description = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            ip_owner_id: ip_owner_id,\n            vpc_id: vpc_id,\n            az: az,\n            interface_id: interface_id,\n            interface_type: interface_type,\n            instance_id: instance_id,\n            instance_owner_id: instance_owner_id,\n            public_dns_name: public_dns_name,\n            private_dns_name: private_dns_name,\n            interface_description: interface_description,\n        })\n    }\n}\n// Parsed struct for network_interface_info\npub struct network_interface_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip_owner_id: ::std::string::String,\n    pub vpc_id: ::std::string::String,\n    pub az: ::std::string::String,\n    pub interface_id: ::std::string::String,\n    pub interface_type: u16,\n    pub instance_id: ::std::string::String,\n    pub instance_owner_id: ::std::string::String,\n    pub public_dns_name: ::std::string::String,\n    pub private_dns_name: ::std::string::String,\n    pub interface_description: ::std::string::String,\n}\n\nimpl network_interface_info {\n    pub const RPC_ID: u16 = 417u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let interface_type = u16::from_ne_bytes(body[20usize..20usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 30usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_ip_owner_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ip_owner_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ip_owner_id = if __l_ip_owner_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ip_owner_id])\n                .into_owned()\n        };\n        __off += __l_ip_owner_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_vpc_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_vpc_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let vpc_id = if __l_vpc_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_vpc_id]).into_owned()\n        };\n        __off += __l_vpc_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_interface_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_interface_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let interface_id = if __l_interface_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_interface_id])\n                .into_owned()\n        };\n        __off += __l_interface_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[22usize..22usize + 2]);\n        let __l_instance_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_instance_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_id = if __l_instance_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_instance_id])\n                .into_owned()\n        };\n        __off += __l_instance_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_instance_owner_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_instance_owner_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let instance_owner_id = if __l_instance_owner_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_instance_owner_id])\n                .into_owned()\n        };\n        __off += __l_instance_owner_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_public_dns_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_public_dns_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let public_dns_name = if __l_public_dns_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_public_dns_name])\n                .into_owned()\n        };\n        __off += __l_public_dns_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_private_dns_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_private_dns_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let private_dns_name = if __l_private_dns_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_private_dns_name])\n                .into_owned()\n        };\n        __off += __l_private_dns_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let interface_description = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            ip_owner_id: ip_owner_id,\n            vpc_id: vpc_id,\n            az: az,\n            interface_id: interface_id,\n            interface_type: interface_type,\n            instance_id: instance_id,\n            instance_owner_id: instance_owner_id,\n            public_dns_name: public_dns_name,\n            private_dns_name: private_dns_name,\n            interface_description: interface_description,\n        })\n    }\n}\n// Parsed struct for udp_new_socket\npub struct udp_new_socket {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk_id: u32,\n    pub laddr: [u8; 16],\n    pub lport: u16,\n}\n\nimpl udp_new_socket {\n    pub const RPC_ID: u16 = 328u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 28usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sk_id = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n        let laddr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 12usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let lport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            pid: pid,\n            sk_id: sk_id,\n            laddr: laddr,\n            lport: lport,\n        })\n    }\n}\n// Parsed struct for udp_stats_addr_unchanged\npub struct udp_stats_addr_unchanged {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub is_rx: u8,\n    pub packets: u32,\n    pub bytes: u32,\n}\n\nimpl udp_stats_addr_unchanged {\n    pub const RPC_ID: u16 = 330u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let is_rx = body[2usize];\n        let packets = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n        let bytes = u32::from_ne_bytes(body[12usize..12usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            is_rx: is_rx,\n            packets: packets,\n            bytes: bytes,\n        })\n    }\n}\n// Parsed struct for udp_stats_addr_changed_v4\npub struct udp_stats_addr_changed_v4 {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub is_rx: u8,\n    pub packets: u32,\n    pub bytes: u32,\n    pub raddr: u32,\n    pub rport: u16,\n}\n\nimpl udp_stats_addr_changed_v4 {\n    pub const RPC_ID: u16 = 341u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 21usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let is_rx = body[20usize];\n        let packets = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n        let bytes = u32::from_ne_bytes(body[12usize..12usize + 4].try_into().unwrap());\n        let raddr = u32::from_ne_bytes(body[16usize..16usize + 4].try_into().unwrap());\n        let rport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            is_rx: is_rx,\n            packets: packets,\n            bytes: bytes,\n            raddr: raddr,\n            rport: rport,\n        })\n    }\n}\n// Parsed struct for udp_stats_addr_changed_v6\npub struct udp_stats_addr_changed_v6 {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub is_rx: u8,\n    pub packets: u32,\n    pub bytes: u32,\n    pub raddr: [u8; 16],\n    pub rport: u16,\n}\n\nimpl udp_stats_addr_changed_v6 {\n    pub const RPC_ID: u16 = 350u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 33usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let is_rx = body[16usize];\n        let packets = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n        let bytes = u32::from_ne_bytes(body[12usize..12usize + 4].try_into().unwrap());\n        let raddr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 17usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let rport = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            is_rx: is_rx,\n            packets: packets,\n            bytes: bytes,\n            raddr: raddr,\n            rport: rport,\n        })\n    }\n}\n// Parsed struct for dns_response_dep_b\npub struct dns_response_dep_b {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub total_dn_len: u16,\n    pub domain_name: ::std::string::String,\n    pub ipv4_addrs: ::std::string::String,\n    pub ipv6_addrs: ::std::string::String,\n    pub latency_ns: u64,\n}\n\nimpl dns_response_dep_b {\n    pub const RPC_ID: u16 = 402u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let total_dn_len = u16::from_ne_bytes(body[16usize..16usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let latency_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 22usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_domain_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_domain_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let domain_name = if __l_domain_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_domain_name])\n                .into_owned()\n        };\n        __off += __l_domain_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[20usize..20usize + 2]);\n        let __l_ipv4_addrs = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ipv4_addrs > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv4_addrs = if __l_ipv4_addrs == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ipv4_addrs])\n                .into_owned()\n        };\n        __off += __l_ipv4_addrs;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv6_addrs = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            total_dn_len: total_dn_len,\n            domain_name: domain_name,\n            ipv4_addrs: ipv4_addrs,\n            ipv6_addrs: ipv6_addrs,\n            latency_ns: latency_ns,\n        })\n    }\n}\n// Parsed struct for dns_timeout\npub struct dns_timeout {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub total_dn_len: u16,\n    pub domain_name: ::std::string::String,\n    pub timeout_ns: u64,\n}\n\nimpl dns_timeout {\n    pub const RPC_ID: u16 = 403u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let total_dn_len = u16::from_ne_bytes(body[16usize..16usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let timeout_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 18usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let domain_name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            total_dn_len: total_dn_len,\n            domain_name: domain_name,\n            timeout_ns: timeout_ns,\n        })\n    }\n}\n// Parsed struct for udp_stats_drops_changed\npub struct udp_stats_drops_changed {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub drops: u32,\n}\n\nimpl udp_stats_drops_changed {\n    pub const RPC_ID: u16 = 405u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 12usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let drops = u32::from_ne_bytes(body[8usize..8usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            drops: drops,\n        })\n    }\n}\n// Parsed struct for dns_response\npub struct dns_response {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub total_dn_len: u16,\n    pub domain_name: ::std::string::String,\n    pub ipv4_addrs: ::std::string::String,\n    pub ipv6_addrs: ::std::string::String,\n    pub latency_ns: u64,\n    pub client_server: u8,\n}\n\nimpl dns_response {\n    pub const RPC_ID: u16 = 418u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let total_dn_len = u16::from_ne_bytes(body[16usize..16usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let latency_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_server = body[22usize];\n\n        // Decode dynamic payload strings\n        let mut __off = 23usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_domain_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_domain_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let domain_name = if __l_domain_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_domain_name])\n                .into_owned()\n        };\n        __off += __l_domain_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[20usize..20usize + 2]);\n        let __l_ipv4_addrs = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ipv4_addrs > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv4_addrs = if __l_ipv4_addrs == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ipv4_addrs])\n                .into_owned()\n        };\n        __off += __l_ipv4_addrs;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ipv6_addrs = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n            total_dn_len: total_dn_len,\n            domain_name: domain_name,\n            ipv4_addrs: ipv4_addrs,\n            ipv6_addrs: ipv6_addrs,\n            latency_ns: latency_ns,\n            client_server: client_server,\n        })\n    }\n}\n// Parsed struct for udp_destroy_socket\npub struct udp_destroy_socket {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n}\n\nimpl udp_destroy_socket {\n    pub const RPC_ID: u16 = 329u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 8usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let sk_id = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            sk_id: sk_id,\n        })\n    }\n}\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/ingest/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_info {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n}\n\nimpl jb_ingest__pid_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(301u16, 24, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_INFO_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod pid_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_info>();\n        let align = align_of::<jb_ingest__pid_info>();\n        let padded_raw_size = (PID_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_info, comm), 2usize);\n        assert_eq!(offset_of!(jb_ingest__pid_info, pid), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_close_info {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n}\n\nimpl jb_ingest__pid_close_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(306u16, 24, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_close_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_CLOSE_INFO_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod pid_close_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_close_info>();\n        let align = align_of::<jb_ingest__pid_close_info>();\n        let padded_raw_size = (PID_CLOSE_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_close_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_close_info, comm), 2usize);\n        assert_eq!(offset_of!(jb_ingest__pid_close_info, pid), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_info_create_deprecated {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__pid_info_create_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(393u16, 32, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_info_create_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_INFO_CREATE_DEPRECATED_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod pid_info_create_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_info_create_deprecated>();\n        let align = align_of::<jb_ingest__pid_info_create_deprecated>();\n        let padded_raw_size = (PID_INFO_CREATE_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__pid_info_create_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__pid_info_create_deprecated, comm),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__pid_info_create_deprecated, pid),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__pid_info_create_deprecated, cgroup),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_info_create {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub pid: u32,\n    pub cgroup: u64,\n    pub parent_pid: i32,\n    pub comm: [u8; 16],\n}\n\nimpl jb_ingest__pid_info_create {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(546u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_info_create {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_INFO_CREATE_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod pid_info_create_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_info_create>();\n        let align = align_of::<jb_ingest__pid_info_create>();\n        let padded_raw_size = (PID_INFO_CREATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, parent_pid), 16usize);\n        assert_eq!(offset_of!(jb_ingest__pid_info_create, comm), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_cgroup_move {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__pid_cgroup_move {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(397u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_cgroup_move {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_CGROUP_MOVE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod pid_cgroup_move_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_cgroup_move>();\n        let align = align_of::<jb_ingest__pid_cgroup_move>();\n        let padded_raw_size = (PID_CGROUP_MOVE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_cgroup_move, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_cgroup_move, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pid_cgroup_move, cgroup), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_set_comm {\n    pub _rpc_id: u16,\n    pub comm: [u8; 16],\n    pub pid: u32,\n}\n\nimpl jb_ingest__pid_set_comm {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(399u16, 24, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_set_comm {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_SET_COMM_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod pid_set_comm_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_set_comm>();\n        let align = align_of::<jb_ingest__pid_set_comm>();\n        let padded_raw_size = (PID_SET_COMM_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_set_comm, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_set_comm, comm), 2usize);\n        assert_eq!(offset_of!(jb_ingest__pid_set_comm, pid), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_set_cmdline {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub pid: u32,\n}\n\nimpl jb_ingest__pid_set_cmdline {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(547u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_set_cmdline {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_SET_CMDLINE_WIRE_SIZE: usize = 8;\n\n#[cfg(test)]\nmod pid_set_cmdline_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_set_cmdline>();\n        let align = align_of::<jb_ingest__pid_set_cmdline>();\n        let padded_raw_size = (PID_SET_CMDLINE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_set_cmdline, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_set_cmdline, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pid_set_cmdline, pid), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__tracked_process_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__tracked_process_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(500u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__tracked_process_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TRACKED_PROCESS_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tracked_process_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__tracked_process_start>();\n        let align = align_of::<jb_ingest__tracked_process_start>();\n        let padded_raw_size = (TRACKED_PROCESS_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__tracked_process_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__tracked_process_start, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__tracked_process_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__tracked_process_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(501u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__tracked_process_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TRACKED_PROCESS_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tracked_process_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__tracked_process_end>();\n        let align = align_of::<jb_ingest__tracked_process_end>();\n        let padded_raw_size = (TRACKED_PROCESS_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__tracked_process_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__tracked_process_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_tgid {\n    pub _rpc_id: u16,\n    pub tgid: u32,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__set_tgid {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(502u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__set_tgid {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_TGID_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod set_tgid_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_tgid>();\n        let align = align_of::<jb_ingest__set_tgid>();\n        let padded_raw_size = (SET_TGID_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_tgid, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_tgid, tgid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__set_tgid, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_cgroup {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__set_cgroup {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(503u16, 24, true)\n    }\n}\n\nimpl Default for jb_ingest__set_cgroup {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_CGROUP_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod set_cgroup_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_cgroup>();\n        let align = align_of::<jb_ingest__set_cgroup>();\n        let padded_raw_size = (SET_CGROUP_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_cgroup, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_cgroup, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_ingest__set_cgroup, _ref), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_command {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__set_command {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(504u16, true)\n    }\n}\n\nimpl Default for jb_ingest__set_command {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_COMMAND_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod set_command_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_command>();\n        let align = align_of::<jb_ingest__set_command>();\n        let padded_raw_size = (SET_COMMAND_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_command, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_command, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__set_command, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pid_exit {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub tgid: u64,\n    pub _ref: u64,\n    pub exit_code: i32,\n}\n\nimpl jb_ingest__pid_exit {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(517u16, 28, true)\n    }\n}\n\nimpl Default for jb_ingest__pid_exit {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PID_EXIT_WIRE_SIZE: usize = 28;\n\n#[cfg(test)]\nmod pid_exit_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pid_exit>();\n        let align = align_of::<jb_ingest__pid_exit>();\n        let padded_raw_size = (PID_EXIT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pid_exit, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pid_exit, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pid_exit, tgid), 8usize);\n        assert_eq!(offset_of!(jb_ingest__pid_exit, _ref), 16usize);\n        assert_eq!(offset_of!(jb_ingest__pid_exit, exit_code), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__cgroup_create_deprecated {\n    pub _rpc_id: u16,\n    pub name: [u8; 64],\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n}\n\nimpl jb_ingest__cgroup_create_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(394u16, 88, true)\n    }\n}\n\nimpl Default for jb_ingest__cgroup_create_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CGROUP_CREATE_DEPRECATED_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod cgroup_create_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__cgroup_create_deprecated>();\n        let align = align_of::<jb_ingest__cgroup_create_deprecated>();\n        let padded_raw_size = (CGROUP_CREATE_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__cgroup_create_deprecated, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__cgroup_create_deprecated, name),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__cgroup_create_deprecated, cgroup),\n            72usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__cgroup_create_deprecated, cgroup_parent),\n            80usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__cgroup_create {\n    pub _rpc_id: u16,\n    pub name: [u8; 256],\n    pub cgroup: u64,\n    pub cgroup_parent: u64,\n}\n\nimpl jb_ingest__cgroup_create {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(544u16, 280, true)\n    }\n}\n\nimpl Default for jb_ingest__cgroup_create {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CGROUP_CREATE_WIRE_SIZE: usize = 280;\n\n#[cfg(test)]\nmod cgroup_create_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__cgroup_create>();\n        let align = align_of::<jb_ingest__cgroup_create>();\n        let padded_raw_size = (CGROUP_CREATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__cgroup_create, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__cgroup_create, name), 2usize);\n        assert_eq!(offset_of!(jb_ingest__cgroup_create, cgroup), 264usize);\n        assert_eq!(\n            offset_of!(jb_ingest__cgroup_create, cgroup_parent),\n            272usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__cgroup_close {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__cgroup_close {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(395u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__cgroup_close {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CGROUP_CLOSE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod cgroup_close_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__cgroup_close>();\n        let align = align_of::<jb_ingest__cgroup_close>();\n        let padded_raw_size = (CGROUP_CLOSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__cgroup_close, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__cgroup_close, cgroup), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__container_metadata {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub id: u16,\n    pub name: u16,\n    pub cgroup: u64,\n    pub image: u16,\n    pub ip_addr: u16,\n    pub cluster: u16,\n    pub container_name: u16,\n    pub task_family: u16,\n    pub task_version: u16,\n}\n\nimpl jb_ingest__container_metadata {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(396u16, true)\n    }\n}\n\nimpl Default for jb_ingest__container_metadata {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONTAINER_METADATA_WIRE_SIZE: usize = 28;\n\n#[cfg(test)]\nmod container_metadata_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__container_metadata>();\n        let align = align_of::<jb_ingest__container_metadata>();\n        let padded_raw_size = (CONTAINER_METADATA_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__container_metadata, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, id), 4usize);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, name), 6usize);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, image), 16usize);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, ip_addr), 18usize);\n        assert_eq!(offset_of!(jb_ingest__container_metadata, cluster), 20usize);\n        assert_eq!(\n            offset_of!(jb_ingest__container_metadata, container_name),\n            22usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_metadata, task_family),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_metadata, task_version),\n            26usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_name {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub _deprecated_pod_uid: u16,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__pod_name {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(410u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_name {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_NAME_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod pod_name_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_name>();\n        let align = align_of::<jb_ingest__pod_name>();\n        let padded_raw_size = (POD_NAME_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_name, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_name, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_name, _deprecated_pod_uid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pod_name, cgroup), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__nomad_metadata {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub ns: u16,\n    pub group_name: u16,\n    pub cgroup: u64,\n    pub task_name: u16,\n}\n\nimpl jb_ingest__nomad_metadata {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(508u16, true)\n    }\n}\n\nimpl Default for jb_ingest__nomad_metadata {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NOMAD_METADATA_WIRE_SIZE: usize = 18;\n\n#[cfg(test)]\nmod nomad_metadata_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__nomad_metadata>();\n        let align = align_of::<jb_ingest__nomad_metadata>();\n        let padded_raw_size = (NOMAD_METADATA_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, ns), 4usize);\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, group_name), 6usize);\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_ingest__nomad_metadata, task_name), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__k8s_metadata {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub container_name: u16,\n    pub pod_name: u16,\n    pub cgroup: u64,\n    pub pod_ns: u16,\n    pub pod_uid: u16,\n}\n\nimpl jb_ingest__k8s_metadata {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(512u16, true)\n    }\n}\n\nimpl Default for jb_ingest__k8s_metadata {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_METADATA_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod k8s_metadata_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__k8s_metadata>();\n        let align = align_of::<jb_ingest__k8s_metadata>();\n        let padded_raw_size = (K8S_METADATA_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, container_name), 4usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, pod_name), 6usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, pod_ns), 16usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata, pod_uid), 18usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__k8s_metadata_port {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub port: u16,\n    pub protocol: u8,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__k8s_metadata_port {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(513u16, true)\n    }\n}\n\nimpl Default for jb_ingest__k8s_metadata_port {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_METADATA_PORT_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod k8s_metadata_port_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__k8s_metadata_port>();\n        let align = align_of::<jb_ingest__k8s_metadata_port>();\n        let padded_raw_size = (K8S_METADATA_PORT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata_port, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata_port, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata_port, port), 4usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata_port, protocol), 6usize);\n        assert_eq!(offset_of!(jb_ingest__k8s_metadata_port, cgroup), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__container_resource_limits_deprecated {\n    pub _rpc_id: u16,\n    pub cpu_shares: u16,\n    pub cpu_period: u16,\n    pub cpu_quota: u16,\n    pub cgroup: u64,\n    pub memory_limit: u64,\n    pub memory_soft_limit: u64,\n    pub total_memory_limit: i64,\n    pub memory_swappiness: u8,\n}\n\nimpl jb_ingest__container_resource_limits_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(514u16, 41, true)\n    }\n}\n\nimpl Default for jb_ingest__container_resource_limits_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONTAINER_RESOURCE_LIMITS_DEPRECATED_WIRE_SIZE: usize = 41;\n\n#[cfg(test)]\nmod container_resource_limits_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__container_resource_limits_deprecated>();\n        let align = align_of::<jb_ingest__container_resource_limits_deprecated>();\n        let padded_raw_size =\n            (CONTAINER_RESOURCE_LIMITS_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits_deprecated, cpu_shares),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits_deprecated, cpu_period),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits_deprecated, cpu_quota),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits_deprecated, cgroup),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__container_resource_limits_deprecated,\n                memory_limit\n            ),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__container_resource_limits_deprecated,\n                memory_soft_limit\n            ),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__container_resource_limits_deprecated,\n                total_memory_limit\n            ),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__container_resource_limits_deprecated,\n                memory_swappiness\n            ),\n            40usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__container_resource_limits {\n    pub _rpc_id: u16,\n    pub cpu_shares: u16,\n    pub cpu_period: u32,\n    pub cgroup: u64,\n    pub memory_limit: u64,\n    pub memory_soft_limit: u64,\n    pub total_memory_limit: i64,\n    pub cpu_quota: u32,\n    pub memory_swappiness: u8,\n}\n\nimpl jb_ingest__container_resource_limits {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(518u16, 45, true)\n    }\n}\n\nimpl Default for jb_ingest__container_resource_limits {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONTAINER_RESOURCE_LIMITS_WIRE_SIZE: usize = 45;\n\n#[cfg(test)]\nmod container_resource_limits_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__container_resource_limits>();\n        let align = align_of::<jb_ingest__container_resource_limits>();\n        let padded_raw_size = (CONTAINER_RESOURCE_LIMITS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__container_resource_limits, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, cpu_shares),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, cpu_period),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, cgroup),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, memory_limit),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, memory_soft_limit),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, total_memory_limit),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, cpu_quota),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__container_resource_limits, memory_swappiness),\n            44usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__container_annotation {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub key: u16,\n    pub cgroup: u64,\n}\n\nimpl jb_ingest__container_annotation {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(538u16, true)\n    }\n}\n\nimpl Default for jb_ingest__container_annotation {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONTAINER_ANNOTATION_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod container_annotation_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__container_annotation>();\n        let align = align_of::<jb_ingest__container_annotation>();\n        let padded_raw_size = (CONTAINER_ANNOTATION_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__container_annotation, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__container_annotation, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__container_annotation, key), 4usize);\n        assert_eq!(offset_of!(jb_ingest__container_annotation, cgroup), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__new_sock_info {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub sk: u64,\n}\n\nimpl jb_ingest__new_sock_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(302u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__new_sock_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NEW_SOCK_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod new_sock_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__new_sock_info>();\n        let align = align_of::<jb_ingest__new_sock_info>();\n        let padded_raw_size = (NEW_SOCK_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__new_sock_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__new_sock_info, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__new_sock_info, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_state_ipv4 {\n    pub _rpc_id: u16,\n    pub dport: u16,\n    pub dest: u32,\n    pub sk: u64,\n    pub src: u32,\n    pub tx_rx: u32,\n    pub sport: u16,\n}\n\nimpl jb_ingest__set_state_ipv4 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(303u16, 26, true)\n    }\n}\n\nimpl Default for jb_ingest__set_state_ipv4 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_STATE_IPV4_WIRE_SIZE: usize = 26;\n\n#[cfg(test)]\nmod set_state_ipv4_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_state_ipv4>();\n        let align = align_of::<jb_ingest__set_state_ipv4>();\n        let padded_raw_size = (SET_STATE_IPV4_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, dport), 2usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, dest), 4usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, sk), 8usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, src), 16usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, tx_rx), 20usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv4, sport), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_state_ipv6 {\n    pub _rpc_id: u16,\n    pub dport: u16,\n    pub tx_rx: u32,\n    pub sk: u64,\n    pub sport: u16,\n    pub dest: [u8; 16],\n    pub src: [u8; 16],\n}\n\nimpl jb_ingest__set_state_ipv6 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(304u16, 50, true)\n    }\n}\n\nimpl Default for jb_ingest__set_state_ipv6 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_STATE_IPV6_WIRE_SIZE: usize = 50;\n\n#[cfg(test)]\nmod set_state_ipv6_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_state_ipv6>();\n        let align = align_of::<jb_ingest__set_state_ipv6>();\n        let padded_raw_size = (SET_STATE_IPV6_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, dport), 2usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, tx_rx), 4usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, sk), 8usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, sport), 16usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, dest), 18usize);\n        assert_eq!(offset_of!(jb_ingest__set_state_ipv6, src), 34usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__socket_stats {\n    pub _rpc_id: u16,\n    pub is_rx: u8,\n    pub diff_delivered: u32,\n    pub sk: u64,\n    pub diff_bytes: u64,\n    pub diff_retrans: u32,\n    pub max_srtt: u32,\n}\n\nimpl jb_ingest__socket_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(326u16, 32, true)\n    }\n}\n\nimpl Default for jb_ingest__socket_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SOCKET_STATS_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod socket_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__socket_stats>();\n        let align = align_of::<jb_ingest__socket_stats>();\n        let padded_raw_size = (SOCKET_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__socket_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, is_rx), 2usize);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, diff_delivered), 4usize);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, sk), 8usize);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, diff_bytes), 16usize);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, diff_retrans), 24usize);\n        assert_eq!(offset_of!(jb_ingest__socket_stats, max_srtt), 28usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__nat_remapping {\n    pub _rpc_id: u16,\n    pub sport: u16,\n    pub src: u32,\n    pub sk: u64,\n    pub dst: u32,\n    pub dport: u16,\n}\n\nimpl jb_ingest__nat_remapping {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(360u16, 22, true)\n    }\n}\n\nimpl Default for jb_ingest__nat_remapping {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NAT_REMAPPING_WIRE_SIZE: usize = 22;\n\n#[cfg(test)]\nmod nat_remapping_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__nat_remapping>();\n        let align = align_of::<jb_ingest__nat_remapping>();\n        let padded_raw_size = (NAT_REMAPPING_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, sport), 2usize);\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, src), 4usize);\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, sk), 8usize);\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, dst), 16usize);\n        assert_eq!(offset_of!(jb_ingest__nat_remapping, dport), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__close_sock_info {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl jb_ingest__close_sock_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(308u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__close_sock_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CLOSE_SOCK_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod close_sock_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__close_sock_info>();\n        let align = align_of::<jb_ingest__close_sock_info>();\n        let padded_raw_size = (CLOSE_SOCK_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__close_sock_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__close_sock_info, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__syn_timeout {\n    pub _rpc_id: u16,\n    pub sk: u64,\n}\n\nimpl jb_ingest__syn_timeout {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(398u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__syn_timeout {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SYN_TIMEOUT_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod syn_timeout_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__syn_timeout>();\n        let align = align_of::<jb_ingest__syn_timeout>();\n        let padded_raw_size = (SYN_TIMEOUT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__syn_timeout, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__syn_timeout, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__http_response {\n    pub _rpc_id: u16,\n    pub code: u16,\n    pub pid: u32,\n    pub sk: u64,\n    pub latency_ns: u64,\n    pub client_server: u8,\n}\n\nimpl jb_ingest__http_response {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(401u16, 25, true)\n    }\n}\n\nimpl Default for jb_ingest__http_response {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const HTTP_RESPONSE_WIRE_SIZE: usize = 25;\n\n#[cfg(test)]\nmod http_response_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__http_response>();\n        let align = align_of::<jb_ingest__http_response>();\n        let padded_raw_size = (HTTP_RESPONSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__http_response, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__http_response, code), 2usize);\n        assert_eq!(offset_of!(jb_ingest__http_response, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__http_response, sk), 8usize);\n        assert_eq!(offset_of!(jb_ingest__http_response, latency_ns), 16usize);\n        assert_eq!(offset_of!(jb_ingest__http_response, client_server), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__tcp_reset {\n    pub _rpc_id: u16,\n    pub is_rx: u8,\n    pub sk: u64,\n}\n\nimpl jb_ingest__tcp_reset {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(519u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__tcp_reset {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_RESET_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tcp_reset_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__tcp_reset>();\n        let align = align_of::<jb_ingest__tcp_reset>();\n        let padded_raw_size = (TCP_RESET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__tcp_reset, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__tcp_reset, is_rx), 2usize);\n        assert_eq!(offset_of!(jb_ingest__tcp_reset, sk), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__process_steady_state {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl jb_ingest__process_steady_state {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(307u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__process_steady_state {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PROCESS_STEADY_STATE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod process_steady_state_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__process_steady_state>();\n        let align = align_of::<jb_ingest__process_steady_state>();\n        let padded_raw_size = (PROCESS_STEADY_STATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__process_steady_state, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__process_steady_state, time), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__socket_steady_state {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl jb_ingest__socket_steady_state {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(309u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__socket_steady_state {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SOCKET_STEADY_STATE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod socket_steady_state_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__socket_steady_state>();\n        let align = align_of::<jb_ingest__socket_steady_state>();\n        let padded_raw_size = (SOCKET_STEADY_STATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__socket_steady_state, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__socket_steady_state, time), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__version_info {\n    pub _rpc_id: u16,\n    pub major: u32,\n    pub minor: u32,\n    pub patch: u32,\n}\n\nimpl jb_ingest__version_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(310u16, 16, false)\n    }\n}\n\nimpl Default for jb_ingest__version_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const VERSION_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod version_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__version_info>();\n        let align = align_of::<jb_ingest__version_info>();\n        let padded_raw_size = (VERSION_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__version_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__version_info, major), 4usize);\n        assert_eq!(offset_of!(jb_ingest__version_info, minor), 8usize);\n        assert_eq!(offset_of!(jb_ingest__version_info, patch), 12usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_node_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub az: u16,\n    pub role: u16,\n    pub instance_id: u16,\n}\n\nimpl jb_ingest__set_node_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(415u16, true)\n    }\n}\n\nimpl Default for jb_ingest__set_node_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_NODE_INFO_WIRE_SIZE: usize = 10;\n\n#[cfg(test)]\nmod set_node_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_node_info>();\n        let align = align_of::<jb_ingest__set_node_info>();\n        let padded_raw_size = (SET_NODE_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_node_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_node_info, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__set_node_info, az), 4usize);\n        assert_eq!(offset_of!(jb_ingest__set_node_info, role), 6usize);\n        assert_eq!(offset_of!(jb_ingest__set_node_info, instance_id), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_config_label {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub key: u16,\n}\n\nimpl jb_ingest__set_config_label {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(416u16, true)\n    }\n}\n\nimpl Default for jb_ingest__set_config_label {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_CONFIG_LABEL_WIRE_SIZE: usize = 6;\n\n#[cfg(test)]\nmod set_config_label_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_config_label>();\n        let align = align_of::<jb_ingest__set_config_label>();\n        let padded_raw_size = (SET_CONFIG_LABEL_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_config_label, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__set_config_label, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__set_config_label, key), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_availability_zone_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub az: [u8; 16],\n}\n\nimpl jb_ingest__set_availability_zone_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(321u16, 19, true)\n    }\n}\n\nimpl Default for jb_ingest__set_availability_zone_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_AVAILABILITY_ZONE_DEPRECATED_WIRE_SIZE: usize = 19;\n\n#[cfg(test)]\nmod set_availability_zone_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_availability_zone_deprecated>();\n        let align = align_of::<jb_ingest__set_availability_zone_deprecated>();\n        let padded_raw_size =\n            (SET_AVAILABILITY_ZONE_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__set_availability_zone_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_availability_zone_deprecated, retcode),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_availability_zone_deprecated, az),\n            3usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_iam_role_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub role: [u8; 64],\n}\n\nimpl jb_ingest__set_iam_role_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(322u16, 67, true)\n    }\n}\n\nimpl Default for jb_ingest__set_iam_role_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_IAM_ROLE_DEPRECATED_WIRE_SIZE: usize = 67;\n\n#[cfg(test)]\nmod set_iam_role_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_iam_role_deprecated>();\n        let align = align_of::<jb_ingest__set_iam_role_deprecated>();\n        let padded_raw_size = (SET_IAM_ROLE_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__set_iam_role_deprecated, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__set_iam_role_deprecated, retcode),\n            2usize\n        );\n        assert_eq!(offset_of!(jb_ingest__set_iam_role_deprecated, role), 3usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_instance_id_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub id: [u8; 17],\n}\n\nimpl jb_ingest__set_instance_id_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(323u16, 20, true)\n    }\n}\n\nimpl Default for jb_ingest__set_instance_id_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_INSTANCE_ID_DEPRECATED_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod set_instance_id_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_instance_id_deprecated>();\n        let align = align_of::<jb_ingest__set_instance_id_deprecated>();\n        let padded_raw_size = (SET_INSTANCE_ID_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_id_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_id_deprecated, retcode),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_id_deprecated, id),\n            3usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_instance_type_deprecated {\n    pub _rpc_id: u16,\n    pub retcode: u8,\n    pub val: [u8; 17],\n}\n\nimpl jb_ingest__set_instance_type_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(324u16, 20, true)\n    }\n}\n\nimpl Default for jb_ingest__set_instance_type_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_INSTANCE_TYPE_DEPRECATED_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod set_instance_type_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_instance_type_deprecated>();\n        let align = align_of::<jb_ingest__set_instance_type_deprecated>();\n        let padded_raw_size = (SET_INSTANCE_TYPE_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_type_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_type_deprecated, retcode),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_instance_type_deprecated, val),\n            3usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__dns_response_fake {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub total_dn_len: u16,\n    pub ips: u16,\n}\n\nimpl jb_ingest__dns_response_fake {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(325u16, true)\n    }\n}\n\nimpl Default for jb_ingest__dns_response_fake {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_RESPONSE_FAKE_WIRE_SIZE: usize = 8;\n\n#[cfg(test)]\nmod dns_response_fake_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__dns_response_fake>();\n        let align = align_of::<jb_ingest__dns_response_fake>();\n        let padded_raw_size = (DNS_RESPONSE_FAKE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__dns_response_fake, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__dns_response_fake, _len), 2);\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_fake, total_dn_len),\n            4usize\n        );\n        assert_eq!(offset_of!(jb_ingest__dns_response_fake, ips), 6usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__dns_response_dep_a_deprecated {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub total_dn_len: u16,\n    pub domain_name: u16,\n    pub ipv4_addrs: u16,\n}\n\nimpl jb_ingest__dns_response_dep_a_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(391u16, true)\n    }\n}\n\nimpl Default for jb_ingest__dns_response_dep_a_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_RESPONSE_DEP_A_DEPRECATED_WIRE_SIZE: usize = 10;\n\n#[cfg(test)]\nmod dns_response_dep_a_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__dns_response_dep_a_deprecated>();\n        let align = align_of::<jb_ingest__dns_response_dep_a_deprecated>();\n        let padded_raw_size = (DNS_RESPONSE_DEP_A_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_a_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_a_deprecated, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_a_deprecated, total_dn_len),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_a_deprecated, domain_name),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_a_deprecated, ipv4_addrs),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__set_config_label_deprecated {\n    pub _rpc_id: u16,\n    pub key: [u8; 20],\n    pub val: [u8; 40],\n}\n\nimpl jb_ingest__set_config_label_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(327u16, 62, true)\n    }\n}\n\nimpl Default for jb_ingest__set_config_label_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_CONFIG_LABEL_DEPRECATED_WIRE_SIZE: usize = 62;\n\n#[cfg(test)]\nmod set_config_label_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__set_config_label_deprecated>();\n        let align = align_of::<jb_ingest__set_config_label_deprecated>();\n        let padded_raw_size = (SET_CONFIG_LABEL_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__set_config_label_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_config_label_deprecated, key),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__set_config_label_deprecated, val),\n            22usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__api_key {\n    pub _rpc_id: u16,\n    pub tenant: [u8; 20],\n    pub api_key: [u8; 64],\n}\n\nimpl jb_ingest__api_key {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(352u16, 86, false)\n    }\n}\n\nimpl Default for jb_ingest__api_key {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const API_KEY_WIRE_SIZE: usize = 86;\n\n#[cfg(test)]\nmod api_key_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__api_key>();\n        let align = align_of::<jb_ingest__api_key>();\n        let padded_raw_size = (API_KEY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__api_key, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__api_key, tenant), 2usize);\n        assert_eq!(offset_of!(jb_ingest__api_key, api_key), 22usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__private_ipv4_addr {\n    pub _rpc_id: u16,\n    pub vpc_id: [u8; 22],\n    pub addr: u32,\n}\n\nimpl jb_ingest__private_ipv4_addr {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(353u16, 28, true)\n    }\n}\n\nimpl Default for jb_ingest__private_ipv4_addr {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PRIVATE_IPV4_ADDR_WIRE_SIZE: usize = 28;\n\n#[cfg(test)]\nmod private_ipv4_addr_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__private_ipv4_addr>();\n        let align = align_of::<jb_ingest__private_ipv4_addr>();\n        let padded_raw_size = (PRIVATE_IPV4_ADDR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__private_ipv4_addr, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__private_ipv4_addr, vpc_id), 2usize);\n        assert_eq!(offset_of!(jb_ingest__private_ipv4_addr, addr), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__ipv6_addr {\n    pub _rpc_id: u16,\n    pub addr: [u8; 16],\n    pub vpc_id: [u8; 22],\n}\n\nimpl jb_ingest__ipv6_addr {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(354u16, 40, true)\n    }\n}\n\nimpl Default for jb_ingest__ipv6_addr {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const IPV6_ADDR_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod ipv6_addr_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__ipv6_addr>();\n        let align = align_of::<jb_ingest__ipv6_addr>();\n        let padded_raw_size = (IPV6_ADDR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__ipv6_addr, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__ipv6_addr, addr), 2usize);\n        assert_eq!(offset_of!(jb_ingest__ipv6_addr, vpc_id), 18usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__public_to_private_ipv4 {\n    pub _rpc_id: u16,\n    pub vpc_id: [u8; 22],\n    pub public_addr: u32,\n    pub private_addr: u32,\n}\n\nimpl jb_ingest__public_to_private_ipv4 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(355u16, 32, true)\n    }\n}\n\nimpl Default for jb_ingest__public_to_private_ipv4 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PUBLIC_TO_PRIVATE_IPV4_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod public_to_private_ipv4_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__public_to_private_ipv4>();\n        let align = align_of::<jb_ingest__public_to_private_ipv4>();\n        let padded_raw_size = (PUBLIC_TO_PRIVATE_IPV4_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__public_to_private_ipv4, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__public_to_private_ipv4, vpc_id),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__public_to_private_ipv4, public_addr),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__public_to_private_ipv4, private_addr),\n            28usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__metadata_complete {\n    pub _rpc_id: u16,\n    pub time: u64,\n}\n\nimpl jb_ingest__metadata_complete {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(356u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__metadata_complete {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const METADATA_COMPLETE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod metadata_complete_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__metadata_complete>();\n        let align = align_of::<jb_ingest__metadata_complete>();\n        let padded_raw_size = (METADATA_COMPLETE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__metadata_complete, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__metadata_complete, time), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__bpf_lost_samples {\n    pub _rpc_id: u16,\n    pub count: u64,\n}\n\nimpl jb_ingest__bpf_lost_samples {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(357u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__bpf_lost_samples {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BPF_LOST_SAMPLES_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod bpf_lost_samples_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__bpf_lost_samples>();\n        let align = align_of::<jb_ingest__bpf_lost_samples>();\n        let padded_raw_size = (BPF_LOST_SAMPLES_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__bpf_lost_samples, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__bpf_lost_samples, count), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_new_legacy {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub ip: u32,\n    pub uid: u16,\n    pub owner_name: u16,\n    pub owner_uid: u16,\n    pub owner_kind: u8,\n    pub is_host_network: u8,\n}\n\nimpl jb_ingest__pod_new_legacy {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(358u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_new_legacy {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_NEW_LEGACY_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod pod_new_legacy_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_new_legacy>();\n        let align = align_of::<jb_ingest__pod_new_legacy>();\n        let padded_raw_size = (POD_NEW_LEGACY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, ip), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, uid), 8usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, owner_name), 10usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, owner_uid), 12usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy, owner_kind), 14usize);\n        assert_eq!(\n            offset_of!(jb_ingest__pod_new_legacy, is_host_network),\n            15usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_new_legacy2 {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub ip: u32,\n    pub uid: u16,\n    pub owner_name: u16,\n    pub owner_uid: u16,\n    pub ns: u16,\n    pub owner_kind: u8,\n    pub is_host_network: u8,\n}\n\nimpl jb_ingest__pod_new_legacy2 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(414u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_new_legacy2 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_NEW_LEGACY2_WIRE_SIZE: usize = 18;\n\n#[cfg(test)]\nmod pod_new_legacy2_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_new_legacy2>();\n        let align = align_of::<jb_ingest__pod_new_legacy2>();\n        let padded_raw_size = (POD_NEW_LEGACY2_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, ip), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, uid), 8usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, owner_name), 10usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, owner_uid), 12usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, ns), 14usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_legacy2, owner_kind), 16usize);\n        assert_eq!(\n            offset_of!(jb_ingest__pod_new_legacy2, is_host_network),\n            17usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_new_with_name {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub ip: u32,\n    pub uid: u16,\n    pub owner_name: u16,\n    pub pod_name: u16,\n    pub owner_uid: u16,\n    pub ns: u16,\n    pub owner_kind: u8,\n    pub is_host_network: u8,\n}\n\nimpl jb_ingest__pod_new_with_name {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(515u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_new_with_name {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_NEW_WITH_NAME_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod pod_new_with_name_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_new_with_name>();\n        let align = align_of::<jb_ingest__pod_new_with_name>();\n        let padded_raw_size = (POD_NEW_WITH_NAME_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, ip), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, uid), 8usize);\n        assert_eq!(\n            offset_of!(jb_ingest__pod_new_with_name, owner_name),\n            10usize\n        );\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, pod_name), 12usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, owner_uid), 14usize);\n        assert_eq!(offset_of!(jb_ingest__pod_new_with_name, ns), 16usize);\n        assert_eq!(\n            offset_of!(jb_ingest__pod_new_with_name, owner_kind),\n            18usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__pod_new_with_name, is_host_network),\n            19usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_container_legacy {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub uid: u16,\n}\n\nimpl jb_ingest__pod_container_legacy {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(400u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_container_legacy {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_CONTAINER_LEGACY_WIRE_SIZE: usize = 6;\n\n#[cfg(test)]\nmod pod_container_legacy_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_container_legacy>();\n        let align = align_of::<jb_ingest__pod_container_legacy>();\n        let padded_raw_size = (POD_CONTAINER_LEGACY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_container_legacy, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_container_legacy, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_container_legacy, uid), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_container {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub uid: u16,\n    pub container_id: u16,\n    pub container_name: u16,\n}\n\nimpl jb_ingest__pod_container {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(494u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_container {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_CONTAINER_WIRE_SIZE: usize = 10;\n\n#[cfg(test)]\nmod pod_container_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_container>();\n        let align = align_of::<jb_ingest__pod_container>();\n        let padded_raw_size = (POD_CONTAINER_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_container, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_container, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__pod_container, uid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__pod_container, container_id), 6usize);\n        assert_eq!(offset_of!(jb_ingest__pod_container, container_name), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_delete {\n    pub _rpc_id: u16,\n    pub _len: u16,\n}\n\nimpl jb_ingest__pod_delete {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(359u16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_delete {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_DELETE_WIRE_SIZE: usize = 4;\n\n#[cfg(test)]\nmod pod_delete_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_delete>();\n        let align = align_of::<jb_ingest__pod_delete>();\n        let padded_raw_size = (POD_DELETE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_delete, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_delete, _len), 2);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pod_resync {\n    pub _rpc_id: u16,\n    pub resync_count: u64,\n}\n\nimpl jb_ingest__pod_resync {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(390u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__pod_resync {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_RESYNC_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod pod_resync_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pod_resync>();\n        let align = align_of::<jb_ingest__pod_resync>();\n        let padded_raw_size = (POD_RESYNC_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pod_resync, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__pod_resync, resync_count), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__span_duration_info {\n    pub _rpc_id: u16,\n    pub duration: u64,\n}\n\nimpl jb_ingest__span_duration_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(351u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__span_duration_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SPAN_DURATION_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod span_duration_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__span_duration_info>();\n        let align = align_of::<jb_ingest__span_duration_info>();\n        let padded_raw_size = (SPAN_DURATION_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__span_duration_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__span_duration_info, duration), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__heartbeat {\n    pub _rpc_id: u16,\n}\n\nimpl jb_ingest__heartbeat {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(392u16, 2, true)\n    }\n}\n\nimpl Default for jb_ingest__heartbeat {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const HEARTBEAT_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod heartbeat_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__heartbeat>();\n        let align = align_of::<jb_ingest__heartbeat>();\n        let padded_raw_size = (HEARTBEAT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__heartbeat, _rpc_id), 0);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__connect {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub collector_type: u8,\n}\n\nimpl jb_ingest__connect {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(548u16, false)\n    }\n}\n\nimpl Default for jb_ingest__connect {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONNECT_WIRE_SIZE: usize = 5;\n\n#[cfg(test)]\nmod connect_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__connect>();\n        let align = align_of::<jb_ingest__connect>();\n        let padded_raw_size = (CONNECT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__connect, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__connect, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__connect, collector_type), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__health_check {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_type: u8,\n}\n\nimpl jb_ingest__health_check {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(409u16, false)\n    }\n}\n\nimpl Default for jb_ingest__health_check {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const HEALTH_CHECK_WIRE_SIZE: usize = 5;\n\n#[cfg(test)]\nmod health_check_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__health_check>();\n        let align = align_of::<jb_ingest__health_check>();\n        let padded_raw_size = (HEALTH_CHECK_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__health_check, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__health_check, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__health_check, client_type), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__log_message {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub log_level: u8,\n}\n\nimpl jb_ingest__log_message {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(411u16, true)\n    }\n}\n\nimpl Default for jb_ingest__log_message {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const LOG_MESSAGE_WIRE_SIZE: usize = 5;\n\n#[cfg(test)]\nmod log_message_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__log_message>();\n        let align = align_of::<jb_ingest__log_message>();\n        let padded_raw_size = (LOG_MESSAGE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__log_message, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__log_message, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__log_message, log_level), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__agent_resource_usage {\n    pub _rpc_id: u16,\n    pub cpu_usage_by_agent: u16,\n    pub minor_page_faults: u32,\n    pub user_mode_time_us: u64,\n    pub kernel_mode_time_us: u64,\n    pub max_resident_set_size: u64,\n    pub major_page_faults: u32,\n    pub block_input_count: u32,\n    pub block_output_count: u32,\n    pub voluntary_context_switch_count: u32,\n    pub involuntary_context_switch_count: u32,\n    pub cpu_idle: u16,\n}\n\nimpl jb_ingest__agent_resource_usage {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(412u16, 54, true)\n    }\n}\n\nimpl Default for jb_ingest__agent_resource_usage {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_RESOURCE_USAGE_WIRE_SIZE: usize = 54;\n\n#[cfg(test)]\nmod agent_resource_usage_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__agent_resource_usage>();\n        let align = align_of::<jb_ingest__agent_resource_usage>();\n        let padded_raw_size = (AGENT_RESOURCE_USAGE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__agent_resource_usage, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, cpu_usage_by_agent),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, minor_page_faults),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, user_mode_time_us),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, kernel_mode_time_us),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, max_resident_set_size),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, major_page_faults),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, block_input_count),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, block_output_count),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__agent_resource_usage,\n                voluntary_context_switch_count\n            ),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__agent_resource_usage,\n                involuntary_context_switch_count\n            ),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__agent_resource_usage, cpu_idle),\n            52usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__cloud_platform {\n    pub _rpc_id: u16,\n    pub cloud_platform: u16,\n}\n\nimpl jb_ingest__cloud_platform {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(413u16, 4, true)\n    }\n}\n\nimpl Default for jb_ingest__cloud_platform {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CLOUD_PLATFORM_WIRE_SIZE: usize = 4;\n\n#[cfg(test)]\nmod cloud_platform_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__cloud_platform>();\n        let align = align_of::<jb_ingest__cloud_platform>();\n        let padded_raw_size = (CLOUD_PLATFORM_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__cloud_platform, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__cloud_platform, cloud_platform),\n            2usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__os_info_deprecated {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub os: u8,\n    pub flavor: u8,\n}\n\nimpl jb_ingest__os_info_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(419u16, true)\n    }\n}\n\nimpl Default for jb_ingest__os_info_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const OS_INFO_DEPRECATED_WIRE_SIZE: usize = 6;\n\n#[cfg(test)]\nmod os_info_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__os_info_deprecated>();\n        let align = align_of::<jb_ingest__os_info_deprecated>();\n        let padded_raw_size = (OS_INFO_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__os_info_deprecated, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__os_info_deprecated, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__os_info_deprecated, os), 4usize);\n        assert_eq!(offset_of!(jb_ingest__os_info_deprecated, flavor), 5usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__os_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub os_version: u16,\n    pub os: u8,\n    pub flavor: u8,\n}\n\nimpl jb_ingest__os_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(545u16, true)\n    }\n}\n\nimpl Default for jb_ingest__os_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const OS_INFO_WIRE_SIZE: usize = 8;\n\n#[cfg(test)]\nmod os_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__os_info>();\n        let align = align_of::<jb_ingest__os_info>();\n        let padded_raw_size = (OS_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__os_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__os_info, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__os_info, os_version), 4usize);\n        assert_eq!(offset_of!(jb_ingest__os_info, os), 6usize);\n        assert_eq!(offset_of!(jb_ingest__os_info, flavor), 7usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__kernel_headers_source {\n    pub _rpc_id: u16,\n    pub source: u8,\n}\n\nimpl jb_ingest__kernel_headers_source {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(420u16, 3, true)\n    }\n}\n\nimpl Default for jb_ingest__kernel_headers_source {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const KERNEL_HEADERS_SOURCE_WIRE_SIZE: usize = 3;\n\n#[cfg(test)]\nmod kernel_headers_source_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__kernel_headers_source>();\n        let align = align_of::<jb_ingest__kernel_headers_source>();\n        let padded_raw_size = (KERNEL_HEADERS_SOURCE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__kernel_headers_source, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__kernel_headers_source, source), 2usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__entrypoint_error {\n    pub _rpc_id: u16,\n    pub error: u8,\n}\n\nimpl jb_ingest__entrypoint_error {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(491u16, 3, true)\n    }\n}\n\nimpl Default for jb_ingest__entrypoint_error {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const ENTRYPOINT_ERROR_WIRE_SIZE: usize = 3;\n\n#[cfg(test)]\nmod entrypoint_error_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__entrypoint_error>();\n        let align = align_of::<jb_ingest__entrypoint_error>();\n        let padded_raw_size = (ENTRYPOINT_ERROR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__entrypoint_error, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__entrypoint_error, error), 2usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__bpf_compiled {\n    pub _rpc_id: u16,\n}\n\nimpl jb_ingest__bpf_compiled {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(492u16, 2, true)\n    }\n}\n\nimpl Default for jb_ingest__bpf_compiled {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BPF_COMPILED_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod bpf_compiled_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__bpf_compiled>();\n        let align = align_of::<jb_ingest__bpf_compiled>();\n        let padded_raw_size = (BPF_COMPILED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__bpf_compiled, _rpc_id), 0);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__begin_telemetry {\n    pub _rpc_id: u16,\n}\n\nimpl jb_ingest__begin_telemetry {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(493u16, 2, true)\n    }\n}\n\nimpl Default for jb_ingest__begin_telemetry {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BEGIN_TELEMETRY_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod begin_telemetry_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__begin_telemetry>();\n        let align = align_of::<jb_ingest__begin_telemetry>();\n        let padded_raw_size = (BEGIN_TELEMETRY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__begin_telemetry, _rpc_id), 0);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__cloud_platform_account_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n}\n\nimpl jb_ingest__cloud_platform_account_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(495u16, true)\n    }\n}\n\nimpl Default for jb_ingest__cloud_platform_account_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CLOUD_PLATFORM_ACCOUNT_INFO_WIRE_SIZE: usize = 4;\n\n#[cfg(test)]\nmod cloud_platform_account_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__cloud_platform_account_info>();\n        let align = align_of::<jb_ingest__cloud_platform_account_info>();\n        let padded_raw_size = (CLOUD_PLATFORM_ACCOUNT_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__cloud_platform_account_info, _rpc_id),\n            0\n        );\n        assert_eq!(offset_of!(jb_ingest__cloud_platform_account_info, _len), 2);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__collector_health {\n    pub _rpc_id: u16,\n    pub status: u16,\n    pub detail: u16,\n}\n\nimpl jb_ingest__collector_health {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(496u16, 6, true)\n    }\n}\n\nimpl Default for jb_ingest__collector_health {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const COLLECTOR_HEALTH_WIRE_SIZE: usize = 6;\n\n#[cfg(test)]\nmod collector_health_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__collector_health>();\n        let align = align_of::<jb_ingest__collector_health>();\n        let padded_raw_size = (COLLECTOR_HEALTH_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__collector_health, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__collector_health, status), 2usize);\n        assert_eq!(offset_of!(jb_ingest__collector_health, detail), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__system_wide_process_settings {\n    pub _rpc_id: u16,\n    pub clock_ticks_per_second: u64,\n    pub memory_page_bytes: u64,\n}\n\nimpl jb_ingest__system_wide_process_settings {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(498u16, 24, true)\n    }\n}\n\nimpl Default for jb_ingest__system_wide_process_settings {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SYSTEM_WIDE_PROCESS_SETTINGS_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod system_wide_process_settings_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__system_wide_process_settings>();\n        let align = align_of::<jb_ingest__system_wide_process_settings>();\n        let padded_raw_size = (SYSTEM_WIDE_PROCESS_SETTINGS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__system_wide_process_settings, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__system_wide_process_settings,\n                clock_ticks_per_second\n            ),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__system_wide_process_settings, memory_page_bytes),\n            16usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__collect_blob {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub blob_type: u16,\n    pub metadata: u16,\n    pub subtype: u64,\n}\n\nimpl jb_ingest__collect_blob {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(511u16, true)\n    }\n}\n\nimpl Default for jb_ingest__collect_blob {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const COLLECT_BLOB_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod collect_blob_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__collect_blob>();\n        let align = align_of::<jb_ingest__collect_blob>();\n        let padded_raw_size = (COLLECT_BLOB_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__collect_blob, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__collect_blob, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__collect_blob, blob_type), 4usize);\n        assert_eq!(offset_of!(jb_ingest__collect_blob, metadata), 6usize);\n        assert_eq!(offset_of!(jb_ingest__collect_blob, subtype), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__report_cpu_cores {\n    pub _rpc_id: u16,\n    pub cpu_core_count: u32,\n}\n\nimpl jb_ingest__report_cpu_cores {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(536u16, 8, true)\n    }\n}\n\nimpl Default for jb_ingest__report_cpu_cores {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const REPORT_CPU_CORES_WIRE_SIZE: usize = 8;\n\n#[cfg(test)]\nmod report_cpu_cores_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__report_cpu_cores>();\n        let align = align_of::<jb_ingest__report_cpu_cores>();\n        let padded_raw_size = (REPORT_CPU_CORES_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__report_cpu_cores, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__report_cpu_cores, cpu_core_count),\n            4usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__bpf_log {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub line: u32,\n    pub code: u64,\n    pub arg0: u64,\n    pub arg1: u64,\n    pub arg2: u64,\n}\n\nimpl jb_ingest__bpf_log {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(537u16, true)\n    }\n}\n\nimpl Default for jb_ingest__bpf_log {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BPF_LOG_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod bpf_log_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__bpf_log>();\n        let align = align_of::<jb_ingest__bpf_log>();\n        let padded_raw_size = (BPF_LOG_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__bpf_log, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, line), 4usize);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, code), 8usize);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, arg0), 16usize);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, arg1), 24usize);\n        assert_eq!(offset_of!(jb_ingest__bpf_log, arg2), 32usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__aws_network_interface_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip: u128,\n}\n\nimpl jb_ingest__aws_network_interface_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(406u16, 32, true)\n    }\n}\n\nimpl Default for jb_ingest__aws_network_interface_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AWS_NETWORK_INTERFACE_START_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod aws_network_interface_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__aws_network_interface_start>();\n        let align = align_of::<jb_ingest__aws_network_interface_start>();\n        let padded_raw_size = (AWS_NETWORK_INTERFACE_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__aws_network_interface_start, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__aws_network_interface_start, _ref),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__aws_network_interface_start, ip),\n            16usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__aws_network_interface_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_ingest__aws_network_interface_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(407u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__aws_network_interface_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AWS_NETWORK_INTERFACE_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod aws_network_interface_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__aws_network_interface_end>();\n        let align = align_of::<jb_ingest__aws_network_interface_end>();\n        let padded_raw_size = (AWS_NETWORK_INTERFACE_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__aws_network_interface_end, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__aws_network_interface_end, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__network_interface_info_deprecated {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub interface_id: u16,\n    pub interface_type: u16,\n    pub _ref: u64,\n    pub instance_id: u16,\n    pub instance_owner_id: u16,\n    pub public_dns_name: u16,\n    pub private_dns_name: u16,\n    pub ip_owner_id: [u8; 18],\n    pub vpc_id: [u8; 22],\n    pub az: [u8; 16],\n}\n\nimpl jb_ingest__network_interface_info_deprecated {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(408u16, true)\n    }\n}\n\nimpl Default for jb_ingest__network_interface_info_deprecated {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NETWORK_INTERFACE_INFO_DEPRECATED_WIRE_SIZE: usize = 80;\n\n#[cfg(test)]\nmod network_interface_info_deprecated_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__network_interface_info_deprecated>();\n        let align = align_of::<jb_ingest__network_interface_info_deprecated>();\n        let padded_raw_size =\n            (NETWORK_INTERFACE_INFO_DEPRECATED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, interface_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, interface_type),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, _ref),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, instance_id),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__network_interface_info_deprecated,\n                instance_owner_id\n            ),\n            18usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__network_interface_info_deprecated,\n                public_dns_name\n            ),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_ingest__network_interface_info_deprecated,\n                private_dns_name\n            ),\n            22usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, ip_owner_id),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, vpc_id),\n            42usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info_deprecated, az),\n            64usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__network_interface_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub ip_owner_id: u16,\n    pub vpc_id: u16,\n    pub _ref: u64,\n    pub az: u16,\n    pub interface_id: u16,\n    pub interface_type: u16,\n    pub instance_id: u16,\n    pub instance_owner_id: u16,\n    pub public_dns_name: u16,\n    pub private_dns_name: u16,\n}\n\nimpl jb_ingest__network_interface_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(417u16, true)\n    }\n}\n\nimpl Default for jb_ingest__network_interface_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const NETWORK_INTERFACE_INFO_WIRE_SIZE: usize = 30;\n\n#[cfg(test)]\nmod network_interface_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__network_interface_info>();\n        let align = align_of::<jb_ingest__network_interface_info>();\n        let padded_raw_size = (NETWORK_INTERFACE_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__network_interface_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__network_interface_info, _len), 2);\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, ip_owner_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, vpc_id),\n            6usize\n        );\n        assert_eq!(offset_of!(jb_ingest__network_interface_info, _ref), 8usize);\n        assert_eq!(offset_of!(jb_ingest__network_interface_info, az), 16usize);\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, interface_id),\n            18usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, interface_type),\n            20usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, instance_id),\n            22usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, instance_owner_id),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, public_dns_name),\n            26usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__network_interface_info, private_dns_name),\n            28usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_new_socket {\n    pub _rpc_id: u16,\n    pub lport: u16,\n    pub pid: u32,\n    pub sk_id: u32,\n    pub laddr: [u8; 16],\n}\n\nimpl jb_ingest__udp_new_socket {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(328u16, 28, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_new_socket {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_NEW_SOCKET_WIRE_SIZE: usize = 28;\n\n#[cfg(test)]\nmod udp_new_socket_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_new_socket>();\n        let align = align_of::<jb_ingest__udp_new_socket>();\n        let padded_raw_size = (UDP_NEW_SOCKET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_new_socket, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__udp_new_socket, lport), 2usize);\n        assert_eq!(offset_of!(jb_ingest__udp_new_socket, pid), 4usize);\n        assert_eq!(offset_of!(jb_ingest__udp_new_socket, sk_id), 8usize);\n        assert_eq!(offset_of!(jb_ingest__udp_new_socket, laddr), 12usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_stats_addr_unchanged {\n    pub _rpc_id: u16,\n    pub is_rx: u8,\n    pub sk_id: u32,\n    pub packets: u32,\n    pub bytes: u32,\n}\n\nimpl jb_ingest__udp_stats_addr_unchanged {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(330u16, 16, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_stats_addr_unchanged {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_STATS_ADDR_UNCHANGED_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod udp_stats_addr_unchanged_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_stats_addr_unchanged>();\n        let align = align_of::<jb_ingest__udp_stats_addr_unchanged>();\n        let padded_raw_size = (UDP_STATS_ADDR_UNCHANGED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_stats_addr_unchanged, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_unchanged, is_rx),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_unchanged, sk_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_unchanged, packets),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_unchanged, bytes),\n            12usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_stats_addr_changed_v4 {\n    pub _rpc_id: u16,\n    pub rport: u16,\n    pub sk_id: u32,\n    pub packets: u32,\n    pub bytes: u32,\n    pub raddr: u32,\n    pub is_rx: u8,\n}\n\nimpl jb_ingest__udp_stats_addr_changed_v4 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(341u16, 21, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_stats_addr_changed_v4 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_STATS_ADDR_CHANGED_V4_WIRE_SIZE: usize = 21;\n\n#[cfg(test)]\nmod udp_stats_addr_changed_v4_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_stats_addr_changed_v4>();\n        let align = align_of::<jb_ingest__udp_stats_addr_changed_v4>();\n        let padded_raw_size = (UDP_STATS_ADDR_CHANGED_V4_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_stats_addr_changed_v4, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, rport),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, sk_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, packets),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, bytes),\n            12usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, raddr),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v4, is_rx),\n            20usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_stats_addr_changed_v6 {\n    pub _rpc_id: u16,\n    pub rport: u16,\n    pub sk_id: u32,\n    pub packets: u32,\n    pub bytes: u32,\n    pub is_rx: u8,\n    pub raddr: [u8; 16],\n}\n\nimpl jb_ingest__udp_stats_addr_changed_v6 {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(350u16, 33, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_stats_addr_changed_v6 {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_STATS_ADDR_CHANGED_V6_WIRE_SIZE: usize = 33;\n\n#[cfg(test)]\nmod udp_stats_addr_changed_v6_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_stats_addr_changed_v6>();\n        let align = align_of::<jb_ingest__udp_stats_addr_changed_v6>();\n        let padded_raw_size = (UDP_STATS_ADDR_CHANGED_V6_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_stats_addr_changed_v6, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, rport),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, sk_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, packets),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, bytes),\n            12usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, is_rx),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_addr_changed_v6, raddr),\n            17usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__dns_response_dep_b {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub sk_id: u32,\n    pub latency_ns: u64,\n    pub total_dn_len: u16,\n    pub domain_name: u16,\n    pub ipv4_addrs: u16,\n}\n\nimpl jb_ingest__dns_response_dep_b {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(402u16, true)\n    }\n}\n\nimpl Default for jb_ingest__dns_response_dep_b {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_RESPONSE_DEP_B_WIRE_SIZE: usize = 22;\n\n#[cfg(test)]\nmod dns_response_dep_b_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__dns_response_dep_b>();\n        let align = align_of::<jb_ingest__dns_response_dep_b>();\n        let padded_raw_size = (DNS_RESPONSE_DEP_B_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__dns_response_dep_b, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__dns_response_dep_b, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__dns_response_dep_b, sk_id), 4usize);\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_b, latency_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_b, total_dn_len),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_b, domain_name),\n            18usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__dns_response_dep_b, ipv4_addrs),\n            20usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__dns_timeout {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub sk_id: u32,\n    pub timeout_ns: u64,\n    pub total_dn_len: u16,\n}\n\nimpl jb_ingest__dns_timeout {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(403u16, true)\n    }\n}\n\nimpl Default for jb_ingest__dns_timeout {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_TIMEOUT_WIRE_SIZE: usize = 18;\n\n#[cfg(test)]\nmod dns_timeout_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__dns_timeout>();\n        let align = align_of::<jb_ingest__dns_timeout>();\n        let padded_raw_size = (DNS_TIMEOUT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__dns_timeout, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__dns_timeout, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__dns_timeout, sk_id), 4usize);\n        assert_eq!(offset_of!(jb_ingest__dns_timeout, timeout_ns), 8usize);\n        assert_eq!(offset_of!(jb_ingest__dns_timeout, total_dn_len), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_stats_drops_changed {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n    pub drops: u32,\n}\n\nimpl jb_ingest__udp_stats_drops_changed {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(405u16, 12, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_stats_drops_changed {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_STATS_DROPS_CHANGED_WIRE_SIZE: usize = 12;\n\n#[cfg(test)]\nmod udp_stats_drops_changed_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_stats_drops_changed>();\n        let align = align_of::<jb_ingest__udp_stats_drops_changed>();\n        let padded_raw_size = (UDP_STATS_DROPS_CHANGED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_stats_drops_changed, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_drops_changed, sk_id),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_ingest__udp_stats_drops_changed, drops),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__dns_response {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub sk_id: u32,\n    pub latency_ns: u64,\n    pub total_dn_len: u16,\n    pub domain_name: u16,\n    pub ipv4_addrs: u16,\n    pub client_server: u8,\n}\n\nimpl jb_ingest__dns_response {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(418u16, true)\n    }\n}\n\nimpl Default for jb_ingest__dns_response {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_RESPONSE_WIRE_SIZE: usize = 23;\n\n#[cfg(test)]\nmod dns_response_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__dns_response>();\n        let align = align_of::<jb_ingest__dns_response>();\n        let padded_raw_size = (DNS_RESPONSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__dns_response, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__dns_response, _len), 2);\n        assert_eq!(offset_of!(jb_ingest__dns_response, sk_id), 4usize);\n        assert_eq!(offset_of!(jb_ingest__dns_response, latency_ns), 8usize);\n        assert_eq!(offset_of!(jb_ingest__dns_response, total_dn_len), 16usize);\n        assert_eq!(offset_of!(jb_ingest__dns_response, domain_name), 18usize);\n        assert_eq!(offset_of!(jb_ingest__dns_response, ipv4_addrs), 20usize);\n        assert_eq!(offset_of!(jb_ingest__dns_response, client_server), 22usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__udp_destroy_socket {\n    pub _rpc_id: u16,\n    pub sk_id: u32,\n}\n\nimpl jb_ingest__udp_destroy_socket {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(329u16, 8, true)\n    }\n}\n\nimpl Default for jb_ingest__udp_destroy_socket {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_DESTROY_SOCKET_WIRE_SIZE: usize = 8;\n\n#[cfg(test)]\nmod udp_destroy_socket_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__udp_destroy_socket>();\n        let align = align_of::<jb_ingest__udp_destroy_socket>();\n        let padded_raw_size = (UDP_DESTROY_SOCKET_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__udp_destroy_socket, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_ingest__udp_destroy_socket, sk_id), 4usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_ingest__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_ingest__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_ingest__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_ingest__pulse>();\n        let align = align_of::<jb_ingest__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_ingest__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![\n        jb_ingest__pid_info::metadata(),\n        jb_ingest__pid_close_info::metadata(),\n        jb_ingest__pid_info_create_deprecated::metadata(),\n        jb_ingest__pid_info_create::metadata(),\n        jb_ingest__pid_cgroup_move::metadata(),\n        jb_ingest__pid_set_comm::metadata(),\n        jb_ingest__pid_set_cmdline::metadata(),\n        jb_ingest__tracked_process_start::metadata(),\n        jb_ingest__tracked_process_end::metadata(),\n        jb_ingest__set_tgid::metadata(),\n        jb_ingest__set_cgroup::metadata(),\n        jb_ingest__set_command::metadata(),\n        jb_ingest__pid_exit::metadata(),\n        jb_ingest__cgroup_create_deprecated::metadata(),\n        jb_ingest__cgroup_create::metadata(),\n        jb_ingest__cgroup_close::metadata(),\n        jb_ingest__container_metadata::metadata(),\n        jb_ingest__pod_name::metadata(),\n        jb_ingest__nomad_metadata::metadata(),\n        jb_ingest__k8s_metadata::metadata(),\n        jb_ingest__k8s_metadata_port::metadata(),\n        jb_ingest__container_resource_limits_deprecated::metadata(),\n        jb_ingest__container_resource_limits::metadata(),\n        jb_ingest__container_annotation::metadata(),\n        jb_ingest__new_sock_info::metadata(),\n        jb_ingest__set_state_ipv4::metadata(),\n        jb_ingest__set_state_ipv6::metadata(),\n        jb_ingest__socket_stats::metadata(),\n        jb_ingest__nat_remapping::metadata(),\n        jb_ingest__close_sock_info::metadata(),\n        jb_ingest__syn_timeout::metadata(),\n        jb_ingest__http_response::metadata(),\n        jb_ingest__tcp_reset::metadata(),\n        jb_ingest__process_steady_state::metadata(),\n        jb_ingest__socket_steady_state::metadata(),\n        jb_ingest__version_info::metadata(),\n        jb_ingest__set_node_info::metadata(),\n        jb_ingest__set_config_label::metadata(),\n        jb_ingest__set_availability_zone_deprecated::metadata(),\n        jb_ingest__set_iam_role_deprecated::metadata(),\n        jb_ingest__set_instance_id_deprecated::metadata(),\n        jb_ingest__set_instance_type_deprecated::metadata(),\n        jb_ingest__dns_response_fake::metadata(),\n        jb_ingest__dns_response_dep_a_deprecated::metadata(),\n        jb_ingest__set_config_label_deprecated::metadata(),\n        jb_ingest__api_key::metadata(),\n        jb_ingest__private_ipv4_addr::metadata(),\n        jb_ingest__ipv6_addr::metadata(),\n        jb_ingest__public_to_private_ipv4::metadata(),\n        jb_ingest__metadata_complete::metadata(),\n        jb_ingest__bpf_lost_samples::metadata(),\n        jb_ingest__pod_new_legacy::metadata(),\n        jb_ingest__pod_new_legacy2::metadata(),\n        jb_ingest__pod_new_with_name::metadata(),\n        jb_ingest__pod_container_legacy::metadata(),\n        jb_ingest__pod_container::metadata(),\n        jb_ingest__pod_delete::metadata(),\n        jb_ingest__pod_resync::metadata(),\n        jb_ingest__span_duration_info::metadata(),\n        jb_ingest__heartbeat::metadata(),\n        jb_ingest__connect::metadata(),\n        jb_ingest__health_check::metadata(),\n        jb_ingest__log_message::metadata(),\n        jb_ingest__agent_resource_usage::metadata(),\n        jb_ingest__cloud_platform::metadata(),\n        jb_ingest__os_info_deprecated::metadata(),\n        jb_ingest__os_info::metadata(),\n        jb_ingest__kernel_headers_source::metadata(),\n        jb_ingest__entrypoint_error::metadata(),\n        jb_ingest__bpf_compiled::metadata(),\n        jb_ingest__begin_telemetry::metadata(),\n        jb_ingest__cloud_platform_account_info::metadata(),\n        jb_ingest__collector_health::metadata(),\n        jb_ingest__system_wide_process_settings::metadata(),\n        jb_ingest__collect_blob::metadata(),\n        jb_ingest__report_cpu_cores::metadata(),\n        jb_ingest__bpf_log::metadata(),\n        jb_ingest__aws_network_interface_start::metadata(),\n        jb_ingest__aws_network_interface_end::metadata(),\n        jb_ingest__network_interface_info_deprecated::metadata(),\n        jb_ingest__network_interface_info::metadata(),\n        jb_ingest__udp_new_socket::metadata(),\n        jb_ingest__udp_stats_addr_unchanged::metadata(),\n        jb_ingest__udp_stats_addr_changed_v4::metadata(),\n        jb_ingest__udp_stats_addr_changed_v6::metadata(),\n        jb_ingest__dns_response_dep_b::metadata(),\n        jb_ingest__dns_timeout::metadata(),\n        jb_ingest__udp_stats_drops_changed::metadata(),\n        jb_ingest__dns_response::metadata(),\n        jb_ingest__udp_destroy_socket::metadata(),\n        jb_ingest__pulse::metadata(),\n    ]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_kernel_collector\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::kernel_collector\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_kernel_collector_encode_pulse(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_kernel_collector__pulse = jb_kernel_collector__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::kernel_collector\n//\n// g_type: u8\n// g_size: 2\n// g_shift: 31\n// hash_shift: 29\n// hash_mask: 3\n// n_keys: 1\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const KERNEL_COLLECTOR_HASH_SIZE: u32 = 4u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 2] = [0, 2];\n\n#[inline]\n#[allow(dead_code)]\npub fn kernel_collector_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 31) as usize] as u32;\n    (k >> 29).wrapping_add(g) & 3u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/kernel_collector/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_kernel_collector__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_kernel_collector__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_kernel_collector__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_kernel_collector__pulse>();\n        let align = align_of::<jb_kernel_collector__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_kernel_collector__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![jb_kernel_collector__pulse::metadata(),]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_logging\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::logging\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_logger_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__logger_start = jb_logging__logger_start {\n        _rpc_id: 600 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_logger_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__logger_end = jb_logging__logger_end {\n        _rpc_id: 601 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_lost_events(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    count: u32,\n    client_hostname: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_lost_events = jb_logging__agent_lost_events {\n        _rpc_id: 602 as u16,\n        _len: __consumed as u16,\n        count,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_pod_not_found(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    uid: JbBlob,\n    on_delete: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(uid.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid: &[u8] = unsafe { slice::from_raw_parts(uid.buf as *const u8, uid.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__pod_not_found = jb_logging__pod_not_found {\n        _rpc_id: 603 as u16,\n        _len: __consumed as u16,\n        on_delete,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_uid.is_empty() {\n        let __len = __sl_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_uid);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_cgroup_not_found(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    cgroup: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 24 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__cgroup_not_found = jb_logging__cgroup_not_found {\n        _rpc_id: 604 as u16,\n        cgroup,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 24 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 24 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_rewriting_private_to_public_ip_mapping(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    private_addr: JbBlob,\n    existing_public_addr: JbBlob,\n    new_public_addr: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(private_addr.len as u32);\n    __consumed = __consumed.saturating_add(existing_public_addr.len as u32);\n    __consumed = __consumed.saturating_add(new_public_addr.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_private_addr: &[u8] =\n        unsafe { slice::from_raw_parts(private_addr.buf as *const u8, private_addr.len as usize) };\n    let __sl_existing_public_addr: &[u8] = unsafe {\n        slice::from_raw_parts(\n            existing_public_addr.buf as *const u8,\n            existing_public_addr.len as usize,\n        )\n    };\n    let __sl_new_public_addr: &[u8] = unsafe {\n        slice::from_raw_parts(\n            new_public_addr.buf as *const u8,\n            new_public_addr.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__rewriting_private_to_public_ip_mapping =\n        jb_logging__rewriting_private_to_public_ip_mapping {\n            _rpc_id: 605 as u16,\n            _len: __consumed as u16,\n            private_addr: (__sl_private_addr.len() as u16),\n            existing_public_addr: (__sl_existing_public_addr.len() as u16),\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_private_addr.is_empty() {\n        let __len = __sl_private_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_private_addr);\n        __off += __len;\n    }\n    if !__sl_existing_public_addr.is_empty() {\n        let __len = __sl_existing_public_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_existing_public_addr);\n        __off += __len;\n    }\n    if !__sl_new_public_addr.is_empty() {\n        let __len = __sl_new_public_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_new_public_addr);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_private_ip_in_private_to_public_ip_mapping(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    private_addr: JbBlob,\n    existing_public_addr: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(private_addr.len as u32);\n    __consumed = __consumed.saturating_add(existing_public_addr.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_private_addr: &[u8] =\n        unsafe { slice::from_raw_parts(private_addr.buf as *const u8, private_addr.len as usize) };\n    let __sl_existing_public_addr: &[u8] = unsafe {\n        slice::from_raw_parts(\n            existing_public_addr.buf as *const u8,\n            existing_public_addr.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__private_ip_in_private_to_public_ip_mapping =\n        jb_logging__private_ip_in_private_to_public_ip_mapping {\n            _rpc_id: 606 as u16,\n            _len: __consumed as u16,\n            private_addr: (__sl_private_addr.len() as u16),\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_private_addr.is_empty() {\n        let __len = __sl_private_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_private_addr);\n        __off += __len;\n    }\n    if !__sl_existing_public_addr.is_empty() {\n        let __len = __sl_existing_public_addr.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_existing_public_addr);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_failed_to_insert_dns_record(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__failed_to_insert_dns_record = jb_logging__failed_to_insert_dns_record {\n        _rpc_id: 607 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_tcp_socket_failed_getting_process_reference(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    pid: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__tcp_socket_failed_getting_process_reference =\n        jb_logging__tcp_socket_failed_getting_process_reference {\n            _rpc_id: 608 as u16,\n            pid,\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_udp_socket_failed_getting_process_reference(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    pid: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__udp_socket_failed_getting_process_reference =\n        jb_logging__udp_socket_failed_getting_process_reference {\n            _rpc_id: 609 as u16,\n            pid,\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_socket_address_already_assigned(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__socket_address_already_assigned =\n        jb_logging__socket_address_already_assigned {\n            _rpc_id: 610 as u16,\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_ingest_decompression_error(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n    error: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__ingest_decompression_error = jb_logging__ingest_decompression_error {\n        _rpc_id: 611 as u16,\n        _len: __consumed as u16,\n        client_hostname: (__sl_client_hostname.len() as u16),\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_ingest_processing_error(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n    error: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__ingest_processing_error = jb_logging__ingest_processing_error {\n        _rpc_id: 612 as u16,\n        _len: __consumed as u16,\n        client_hostname: (__sl_client_hostname.len() as u16),\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_ingest_connection_error(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n    error: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__ingest_connection_error = jb_logging__ingest_connection_error {\n        _rpc_id: 613 as u16,\n        _len: __consumed as u16,\n        client_hostname: (__sl_client_hostname.len() as u16),\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_auth_success(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_auth_success = jb_logging__agent_auth_success {\n        _rpc_id: 614 as u16,\n        _len: __consumed as u16,\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_auth_failure(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n    error: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_auth_failure = jb_logging__agent_auth_failure {\n        _rpc_id: 615 as u16,\n        _len: __consumed as u16,\n        client_hostname: (__sl_client_hostname.len() as u16),\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_attempting_auth_using_api_key(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_attempting_auth_using_api_key =\n        jb_logging__agent_attempting_auth_using_api_key {\n            _rpc_id: 616 as u16,\n            _len: __consumed as u16,\n            client_type,\n            _ref,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_k8s_container_pod_not_found(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    pod_uid_suffix: *const u8,\n    pod_uid_hash: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 88 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_pod_uid_suffix: &[u8] = unsafe { slice::from_raw_parts(pod_uid_suffix, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__k8s_container_pod_not_found = jb_logging__k8s_container_pod_not_found {\n        _rpc_id: 617 as u16,\n        pod_uid_suffix: __sl_pod_uid_suffix.try_into().unwrap(),\n        pod_uid_hash,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_connect_success(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    client_type: u8,\n    client_hostname: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(client_hostname.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_client_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(\n            client_hostname.buf as *const u8,\n            client_hostname.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_connect_success = jb_logging__agent_connect_success {\n        _rpc_id: 618 as u16,\n        _len: __consumed as u16,\n        client_type,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_client_hostname.is_empty() {\n        let __len = __sl_client_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_hostname);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_core_stats_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__core_stats_start = jb_logging__core_stats_start {\n        _rpc_id: 619 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_core_stats_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__core_stats_end = jb_logging__core_stats_end {\n        _rpc_id: 620 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_span_utilization_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    span_name: JbBlob,\n    module: JbBlob,\n    shard: u16,\n    allocated: u16,\n    max_allocated: u16,\n    pool_size_: u16,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 30 as u32;\n    __consumed = __consumed.saturating_add(span_name.len as u32);\n    __consumed = __consumed.saturating_add(module.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_span_name: &[u8] =\n        unsafe { slice::from_raw_parts(span_name.buf as *const u8, span_name.len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__span_utilization_stats = jb_logging__span_utilization_stats {\n        _rpc_id: 621 as u16,\n        _len: __consumed as u16,\n        span_name: (__sl_span_name.len() as u16),\n        shard,\n        time_ns,\n        _ref,\n        allocated,\n        max_allocated,\n        pool_size_,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 30 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 30 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_span_name.is_empty() {\n        let __len = __sl_span_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_span_name);\n        __off += __len;\n    }\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_connection_message_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    msg_: JbBlob,\n    shard: u16,\n    severity_: u32,\n    conn: u16,\n    time_ns: u64,\n    count: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 38 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(msg_.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_msg_: &[u8] =\n        unsafe { slice::from_raw_parts(msg_.buf as *const u8, msg_.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__connection_message_stats = jb_logging__connection_message_stats {\n        _rpc_id: 622 as u16,\n        _len: __consumed as u16,\n        severity_,\n        time_ns,\n        count,\n        _ref,\n        module: (__sl_module.len() as u16),\n        shard,\n        conn,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 38 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 38 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_msg_.is_empty() {\n        let __len = __sl_msg_.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_msg_);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_connection_message_error_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    conn: u16,\n    msg_: JbBlob,\n    error: JbBlob,\n    count: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 36 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(msg_.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_msg_: &[u8] =\n        unsafe { slice::from_raw_parts(msg_.buf as *const u8, msg_.len as usize) };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__connection_message_error_stats =\n        jb_logging__connection_message_error_stats {\n            _rpc_id: 623 as u16,\n            _len: __consumed as u16,\n            module: (__sl_module.len() as u16),\n            shard,\n            count,\n            time_ns,\n            _ref,\n            conn,\n            msg_: (__sl_msg_.len() as u16),\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_msg_.is_empty() {\n        let __len = __sl_msg_.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_msg_);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_status_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    program: JbBlob,\n    version: JbBlob,\n    status: u8,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 27 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(program.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_program: &[u8] =\n        unsafe { slice::from_raw_parts(program.buf as *const u8, program.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__status_stats = jb_logging__status_stats {\n        _rpc_id: 624 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        _ref,\n        program: (__sl_program.len() as u16),\n        status,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 27 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 27 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_program.is_empty() {\n        let __len = __sl_program.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_program);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_rpc_receive_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    receiver_app: JbBlob,\n    shard: u16,\n    sender_app: JbBlob,\n    max_latency_ns: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 32 as u32;\n    __consumed = __consumed.saturating_add(receiver_app.len as u32);\n    __consumed = __consumed.saturating_add(sender_app.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_receiver_app: &[u8] =\n        unsafe { slice::from_raw_parts(receiver_app.buf as *const u8, receiver_app.len as usize) };\n    let __sl_sender_app: &[u8] =\n        unsafe { slice::from_raw_parts(sender_app.buf as *const u8, sender_app.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__rpc_receive_stats = jb_logging__rpc_receive_stats {\n        _rpc_id: 625 as u16,\n        _len: __consumed as u16,\n        receiver_app: (__sl_receiver_app.len() as u16),\n        shard,\n        max_latency_ns,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_receiver_app.is_empty() {\n        let __len = __sl_receiver_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_receiver_app);\n        __off += __len;\n    }\n    if !__sl_sender_app.is_empty() {\n        let __len = __sl_sender_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_sender_app);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_rpc_write_stalls_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    sender_app: JbBlob,\n    shard: u16,\n    receiver_app: JbBlob,\n    count: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 32 as u32;\n    __consumed = __consumed.saturating_add(sender_app.len as u32);\n    __consumed = __consumed.saturating_add(receiver_app.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_sender_app: &[u8] =\n        unsafe { slice::from_raw_parts(sender_app.buf as *const u8, sender_app.len as usize) };\n    let __sl_receiver_app: &[u8] =\n        unsafe { slice::from_raw_parts(receiver_app.buf as *const u8, receiver_app.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__rpc_write_stalls_stats = jb_logging__rpc_write_stalls_stats {\n        _rpc_id: 626 as u16,\n        _len: __consumed as u16,\n        sender_app: (__sl_sender_app.len() as u16),\n        shard,\n        count,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_sender_app.is_empty() {\n        let __len = __sl_sender_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_sender_app);\n        __off += __len;\n    }\n    if !__sl_receiver_app.is_empty() {\n        let __len = __sl_receiver_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_receiver_app);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_rpc_write_utilization_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    sender_app: JbBlob,\n    shard: u16,\n    receiver_app: JbBlob,\n    max_buf_used: u32,\n    max_buf_util: u64,\n    max_elem_util: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 44 as u32;\n    __consumed = __consumed.saturating_add(sender_app.len as u32);\n    __consumed = __consumed.saturating_add(receiver_app.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_sender_app: &[u8] =\n        unsafe { slice::from_raw_parts(sender_app.buf as *const u8, sender_app.len as usize) };\n    let __sl_receiver_app: &[u8] =\n        unsafe { slice::from_raw_parts(receiver_app.buf as *const u8, receiver_app.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__rpc_write_utilization_stats = jb_logging__rpc_write_utilization_stats {\n        _rpc_id: 627 as u16,\n        _len: __consumed as u16,\n        max_buf_used,\n        max_buf_util,\n        max_elem_util,\n        time_ns,\n        _ref,\n        sender_app: (__sl_sender_app.len() as u16),\n        shard,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 44 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 44 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_sender_app.is_empty() {\n        let __len = __sl_sender_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_sender_app);\n        __off += __len;\n    }\n    if !__sl_receiver_app.is_empty() {\n        let __len = __sl_receiver_app.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_receiver_app);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_code_timing_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    name: JbBlob,\n    filename: JbBlob,\n    line: u16,\n    index_string: u64,\n    count: u64,\n    avg_ns: u64,\n    min_ns: u64,\n    sum_ns: u64,\n    max_ns: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 72 as u32;\n    __consumed = __consumed.saturating_add(name.len as u32);\n    __consumed = __consumed.saturating_add(filename.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n    let __sl_filename: &[u8] =\n        unsafe { slice::from_raw_parts(filename.buf as *const u8, filename.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__code_timing_stats = jb_logging__code_timing_stats {\n        _rpc_id: 628 as u16,\n        _len: __consumed as u16,\n        name: (__sl_name.len() as u16),\n        line,\n        index_string,\n        count,\n        avg_ns,\n        min_ns,\n        max_ns,\n        sum_ns,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 72 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 72 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n    if !__sl_filename.is_empty() {\n        let __len = __sl_filename.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_filename);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agg_core_stats_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agg_core_stats_start = jb_logging__agg_core_stats_start {\n        _rpc_id: 629 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agg_core_stats_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agg_core_stats_end = jb_logging__agg_core_stats_end {\n        _rpc_id: 630 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agg_root_truncation_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    field: JbBlob,\n    count: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 32 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(field.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_field: &[u8] =\n        unsafe { slice::from_raw_parts(field.buf as *const u8, field.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agg_root_truncation_stats = jb_logging__agg_root_truncation_stats {\n        _rpc_id: 631 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        count,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_field.is_empty() {\n        let __len = __sl_field.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_field);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agg_prometheus_bytes_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    prometheus_bytes_written: u64,\n    prometheus_bytes_discarded: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 40 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agg_prometheus_bytes_stats = jb_logging__agg_prometheus_bytes_stats {\n        _rpc_id: 632 as u16,\n        _len: __consumed as u16,\n        shard,\n        prometheus_bytes_written,\n        prometheus_bytes_discarded,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agg_otlp_grpc_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    client_type: JbBlob,\n    bytes_failed: u64,\n    bytes_sent: u64,\n    data_points_failed: u64,\n    data_points_sent: u64,\n    requests_failed: u64,\n    requests_sent: u64,\n    unknown_response_tags: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 80 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(client_type.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_client_type: &[u8] =\n        unsafe { slice::from_raw_parts(client_type.buf as *const u8, client_type.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agg_otlp_grpc_stats = jb_logging__agg_otlp_grpc_stats {\n        _rpc_id: 644 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        bytes_failed,\n        bytes_sent,\n        data_points_failed,\n        data_points_sent,\n        requests_failed,\n        requests_sent,\n        unknown_response_tags,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 80 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 80 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_client_type.is_empty() {\n        let __len = __sl_client_type.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_client_type);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_ingest_core_stats_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__ingest_core_stats_start = jb_logging__ingest_core_stats_start {\n        _rpc_id: 633 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_ingest_core_stats_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__ingest_core_stats_end = jb_logging__ingest_core_stats_end {\n        _rpc_id: 634 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_client_handle_pool_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    span_name: JbBlob,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    client_handle_pool: u64,\n    client_handle_pool_fraction: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 62 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(span_name.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_span_name: &[u8] =\n        unsafe { slice::from_raw_parts(span_name.buf as *const u8, span_name.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__client_handle_pool_stats = jb_logging__client_handle_pool_stats {\n        _rpc_id: 635 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        client_handle_pool,\n        client_handle_pool_fraction,\n        _ref,\n        span_name: (__sl_span_name.len() as u16),\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        agent_hostname: (__sl_agent_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 62 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 62 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_span_name.is_empty() {\n        let __len = __sl_span_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_span_name);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_connection_message_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    message: JbBlob,\n    severity_: u16,\n    count: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 56 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(message.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_message: &[u8] =\n        unsafe { slice::from_raw_parts(message.buf as *const u8, message.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_connection_message_stats =\n        jb_logging__agent_connection_message_stats {\n            _rpc_id: 636 as u16,\n            _len: __consumed as u16,\n            module: (__sl_module.len() as u16),\n            shard,\n            time_ns,\n            count,\n            _ref,\n            version: (__sl_version.len() as u16),\n            cloud: (__sl_cloud.len() as u16),\n            env: (__sl_env.len() as u16),\n            role: (__sl_role.len() as u16),\n            az: (__sl_az.len() as u16),\n            node_id: (__sl_node_id.len() as u16),\n            kernel_version: (__sl_kernel_version.len() as u16),\n            client_type,\n            agent_hostname: (__sl_agent_hostname.len() as u16),\n            os: (__sl_os.len() as u16),\n            os_version: (__sl_os_version.len() as u16),\n            severity_,\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 56 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 56 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_message.is_empty() {\n        let __len = __sl_message.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_message);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_agent_connection_message_error_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    message: JbBlob,\n    error: JbBlob,\n    count: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 56 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(message.len as u32);\n    __consumed = __consumed.saturating_add(error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_message: &[u8] =\n        unsafe { slice::from_raw_parts(message.buf as *const u8, message.len as usize) };\n    let __sl_error: &[u8] =\n        unsafe { slice::from_raw_parts(error.buf as *const u8, error.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__agent_connection_message_error_stats =\n        jb_logging__agent_connection_message_error_stats {\n            _rpc_id: 637 as u16,\n            _len: __consumed as u16,\n            module: (__sl_module.len() as u16),\n            shard,\n            time_ns,\n            count,\n            _ref,\n            version: (__sl_version.len() as u16),\n            cloud: (__sl_cloud.len() as u16),\n            env: (__sl_env.len() as u16),\n            role: (__sl_role.len() as u16),\n            az: (__sl_az.len() as u16),\n            node_id: (__sl_node_id.len() as u16),\n            kernel_version: (__sl_kernel_version.len() as u16),\n            client_type,\n            agent_hostname: (__sl_agent_hostname.len() as u16),\n            os: (__sl_os.len() as u16),\n            os_version: (__sl_os_version.len() as u16),\n            message: (__sl_message.len() as u16),\n        };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 56 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 56 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_message.is_empty() {\n        let __len = __sl_message.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_message);\n        __off += __len;\n    }\n    if !__sl_error.is_empty() {\n        let __len = __sl_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_connection_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    time_since_last_message_ns: u64,\n    clock_offset_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 60 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__connection_stats = jb_logging__connection_stats {\n        _rpc_id: 638 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        time_since_last_message_ns,\n        clock_offset_ns,\n        _ref,\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        agent_hostname: (__sl_agent_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 60 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 60 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_collector_log_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    severity_: JbBlob,\n    count: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 50 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(severity_.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_severity_: &[u8] =\n        unsafe { slice::from_raw_parts(severity_.buf as *const u8, severity_.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__collector_log_stats = jb_logging__collector_log_stats {\n        _rpc_id: 639 as u16,\n        _len: __consumed as u16,\n        count,\n        time_ns,\n        _ref,\n        module: (__sl_module.len() as u16),\n        shard,\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        agent_hostname: (__sl_agent_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n        os_version: (__sl_os_version.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 50 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 50 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_severity_.is_empty() {\n        let __len = __sl_severity_.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_severity_);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_entry_point_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    agent_hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    kernel_headers_source: JbBlob,\n    entrypoint_error: JbBlob,\n    entrypoint_info: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 49 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(agent_hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(kernel_headers_source.len as u32);\n    __consumed = __consumed.saturating_add(entrypoint_error.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_agent_hostname: &[u8] = unsafe {\n        slice::from_raw_parts(agent_hostname.buf as *const u8, agent_hostname.len as usize)\n    };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_kernel_headers_source: &[u8] = unsafe {\n        slice::from_raw_parts(\n            kernel_headers_source.buf as *const u8,\n            kernel_headers_source.len as usize,\n        )\n    };\n    let __sl_entrypoint_error: &[u8] = unsafe {\n        slice::from_raw_parts(\n            entrypoint_error.buf as *const u8,\n            entrypoint_error.len as usize,\n        )\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__entry_point_stats = jb_logging__entry_point_stats {\n        _rpc_id: 640 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        _ref,\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        agent_hostname: (__sl_agent_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n        os_version: (__sl_os_version.len() as u16),\n        kernel_headers_source: (__sl_kernel_headers_source.len() as u16),\n        entrypoint_info,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 49 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 49 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_agent_hostname.is_empty() {\n        let __len = __sl_agent_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_agent_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_kernel_headers_source.is_empty() {\n        let __len = __sl_kernel_headers_source.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_headers_source);\n        __off += __len;\n    }\n    if !__sl_entrypoint_error.is_empty() {\n        let __len = __sl_entrypoint_error.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_entrypoint_error);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_collector_health_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    status: JbBlob,\n    status_detail: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 48 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(status.len as u32);\n    __consumed = __consumed.saturating_add(status_detail.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_hostname: &[u8] =\n        unsafe { slice::from_raw_parts(hostname.buf as *const u8, hostname.len as usize) };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_status: &[u8] =\n        unsafe { slice::from_raw_parts(status.buf as *const u8, status.len as usize) };\n    let __sl_status_detail: &[u8] = unsafe {\n        slice::from_raw_parts(status_detail.buf as *const u8, status_detail.len as usize)\n    };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__collector_health_stats = jb_logging__collector_health_stats {\n        _rpc_id: 641 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        _ref,\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        hostname: (__sl_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n        os_version: (__sl_os_version.len() as u16),\n        status: (__sl_status.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_hostname.is_empty() {\n        let __len = __sl_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_status.is_empty() {\n        let __len = __sl_status.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_status);\n        __off += __len;\n    }\n    if !__sl_status_detail.is_empty() {\n        let __len = __sl_status_detail.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_status_detail);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_bpf_log_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    shard: u16,\n    version: JbBlob,\n    cloud: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    az: JbBlob,\n    node_id: JbBlob,\n    kernel_version: JbBlob,\n    client_type: u16,\n    hostname: JbBlob,\n    os: JbBlob,\n    os_version: JbBlob,\n    time_ns: u64,\n    filename: JbBlob,\n    line: JbBlob,\n    code: JbBlob,\n    arg0: JbBlob,\n    arg1: JbBlob,\n    arg2: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 56 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(cloud.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(node_id.len as u32);\n    __consumed = __consumed.saturating_add(kernel_version.len as u32);\n    __consumed = __consumed.saturating_add(hostname.len as u32);\n    __consumed = __consumed.saturating_add(os.len as u32);\n    __consumed = __consumed.saturating_add(os_version.len as u32);\n    __consumed = __consumed.saturating_add(filename.len as u32);\n    __consumed = __consumed.saturating_add(line.len as u32);\n    __consumed = __consumed.saturating_add(code.len as u32);\n    __consumed = __consumed.saturating_add(arg0.len as u32);\n    __consumed = __consumed.saturating_add(arg1.len as u32);\n    __consumed = __consumed.saturating_add(arg2.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_cloud: &[u8] =\n        unsafe { slice::from_raw_parts(cloud.buf as *const u8, cloud.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_node_id: &[u8] =\n        unsafe { slice::from_raw_parts(node_id.buf as *const u8, node_id.len as usize) };\n    let __sl_kernel_version: &[u8] = unsafe {\n        slice::from_raw_parts(kernel_version.buf as *const u8, kernel_version.len as usize)\n    };\n    let __sl_hostname: &[u8] =\n        unsafe { slice::from_raw_parts(hostname.buf as *const u8, hostname.len as usize) };\n    let __sl_os: &[u8] = unsafe { slice::from_raw_parts(os.buf as *const u8, os.len as usize) };\n    let __sl_os_version: &[u8] =\n        unsafe { slice::from_raw_parts(os_version.buf as *const u8, os_version.len as usize) };\n    let __sl_filename: &[u8] =\n        unsafe { slice::from_raw_parts(filename.buf as *const u8, filename.len as usize) };\n    let __sl_line: &[u8] =\n        unsafe { slice::from_raw_parts(line.buf as *const u8, line.len as usize) };\n    let __sl_code: &[u8] =\n        unsafe { slice::from_raw_parts(code.buf as *const u8, code.len as usize) };\n    let __sl_arg0: &[u8] =\n        unsafe { slice::from_raw_parts(arg0.buf as *const u8, arg0.len as usize) };\n    let __sl_arg1: &[u8] =\n        unsafe { slice::from_raw_parts(arg1.buf as *const u8, arg1.len as usize) };\n    let __sl_arg2: &[u8] =\n        unsafe { slice::from_raw_parts(arg2.buf as *const u8, arg2.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__bpf_log_stats = jb_logging__bpf_log_stats {\n        _rpc_id: 642 as u16,\n        _len: __consumed as u16,\n        module: (__sl_module.len() as u16),\n        shard,\n        time_ns,\n        _ref,\n        version: (__sl_version.len() as u16),\n        cloud: (__sl_cloud.len() as u16),\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        node_id: (__sl_node_id.len() as u16),\n        kernel_version: (__sl_kernel_version.len() as u16),\n        client_type,\n        hostname: (__sl_hostname.len() as u16),\n        os: (__sl_os.len() as u16),\n        os_version: (__sl_os_version.len() as u16),\n        filename: (__sl_filename.len() as u16),\n        line: (__sl_line.len() as u16),\n        code: (__sl_code.len() as u16),\n        arg0: (__sl_arg0.len() as u16),\n        arg1: (__sl_arg1.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 56 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 56 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_cloud.is_empty() {\n        let __len = __sl_cloud.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cloud);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_node_id.is_empty() {\n        let __len = __sl_node_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_node_id);\n        __off += __len;\n    }\n    if !__sl_kernel_version.is_empty() {\n        let __len = __sl_kernel_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_kernel_version);\n        __off += __len;\n    }\n    if !__sl_hostname.is_empty() {\n        let __len = __sl_hostname.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_hostname);\n        __off += __len;\n    }\n    if !__sl_os.is_empty() {\n        let __len = __sl_os.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os);\n        __off += __len;\n    }\n    if !__sl_os_version.is_empty() {\n        let __len = __sl_os_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_os_version);\n        __off += __len;\n    }\n    if !__sl_filename.is_empty() {\n        let __len = __sl_filename.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_filename);\n        __off += __len;\n    }\n    if !__sl_line.is_empty() {\n        let __len = __sl_line.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_line);\n        __off += __len;\n    }\n    if !__sl_code.is_empty() {\n        let __len = __sl_code.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_code);\n        __off += __len;\n    }\n    if !__sl_arg0.is_empty() {\n        let __len = __sl_arg0.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_arg0);\n        __off += __len;\n    }\n    if !__sl_arg1.is_empty() {\n        let __len = __sl_arg1.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_arg1);\n        __off += __len;\n    }\n    if !__sl_arg2.is_empty() {\n        let __len = __sl_arg2.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_arg2);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_server_stats(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    module: JbBlob,\n    connection_counter: u64,\n    disconnect_counter: u64,\n    time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 40 as u32;\n    __consumed = __consumed.saturating_add(module.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_module: &[u8] =\n        unsafe { slice::from_raw_parts(module.buf as *const u8, module.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__server_stats = jb_logging__server_stats {\n        _rpc_id: 643 as u16,\n        _len: __consumed as u16,\n        connection_counter,\n        disconnect_counter,\n        time_ns,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 40 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 40 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_module.is_empty() {\n        let __len = __sl_module.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_module);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_logging_encode_pulse(__dest: *mut u8, __dest_len: u32, __tstamp: u64) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_logging__pulse = jb_logging__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::logging\n//\n// g_type: u8\n// g_size: 16\n// g_shift: 28\n// hash_shift: 22\n// hash_mask: 63\n// n_keys: 46\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const LOGGING_HASH_SIZE: u32 = 64u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 16] = [1, 4, 0, 0, 4, 3, 3, 4, 2, 3, 2, 2, 1, 0, 0, 0];\n\n#[inline]\n#[allow(dead_code)]\npub fn logging_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 28) as usize] as u32;\n    (k >> 22).wrapping_add(g) & 63u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for logger_start\npub struct logger_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl logger_start {\n    pub const RPC_ID: u16 = 600u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for logger_end\npub struct logger_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl logger_end {\n    pub const RPC_ID: u16 = 601u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for agent_lost_events\npub struct agent_lost_events {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub count: u32,\n    pub client_hostname: ::std::string::String,\n}\n\nimpl agent_lost_events {\n    pub const RPC_ID: u16 = 602u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let count = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            count: count,\n            client_hostname: client_hostname,\n        })\n    }\n}\n// Parsed struct for pod_not_found\npub struct pod_not_found {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub uid: ::std::string::String,\n    pub on_delete: u8,\n}\n\nimpl pod_not_found {\n    pub const RPC_ID: u16 = 603u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let on_delete = body[4usize];\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let uid = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            uid: uid,\n            on_delete: on_delete,\n        })\n    }\n}\n// Parsed struct for cgroup_not_found\npub struct cgroup_not_found {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub cgroup: u64,\n}\n\nimpl cgroup_not_found {\n    pub const RPC_ID: u16 = 604u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 24usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let cgroup = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            cgroup: cgroup,\n        })\n    }\n}\n// Parsed struct for rewriting_private_to_public_ip_mapping\npub struct rewriting_private_to_public_ip_mapping {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub private_addr: ::std::string::String,\n    pub existing_public_addr: ::std::string::String,\n    pub new_public_addr: ::std::string::String,\n}\n\nimpl rewriting_private_to_public_ip_mapping {\n    pub const RPC_ID: u16 = 605u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_private_addr = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_private_addr > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let private_addr = if __l_private_addr == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_private_addr])\n                .into_owned()\n        };\n        __off += __l_private_addr;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_existing_public_addr = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_existing_public_addr > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let existing_public_addr = if __l_existing_public_addr == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_existing_public_addr])\n                .into_owned()\n        };\n        __off += __l_existing_public_addr;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let new_public_addr = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            private_addr: private_addr,\n            existing_public_addr: existing_public_addr,\n            new_public_addr: new_public_addr,\n        })\n    }\n}\n// Parsed struct for private_ip_in_private_to_public_ip_mapping\npub struct private_ip_in_private_to_public_ip_mapping {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub private_addr: ::std::string::String,\n    pub existing_public_addr: ::std::string::String,\n}\n\nimpl private_ip_in_private_to_public_ip_mapping {\n    pub const RPC_ID: u16 = 606u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_private_addr = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_private_addr > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let private_addr = if __l_private_addr == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_private_addr])\n                .into_owned()\n        };\n        __off += __l_private_addr;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let existing_public_addr = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            private_addr: private_addr,\n            existing_public_addr: existing_public_addr,\n        })\n    }\n}\n// Parsed struct for failed_to_insert_dns_record\npub struct failed_to_insert_dns_record {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl failed_to_insert_dns_record {\n    pub const RPC_ID: u16 = 607u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for tcp_socket_failed_getting_process_reference\npub struct tcp_socket_failed_getting_process_reference {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub pid: u32,\n}\n\nimpl tcp_socket_failed_getting_process_reference {\n    pub const RPC_ID: u16 = 608u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            pid: pid,\n        })\n    }\n}\n// Parsed struct for udp_socket_failed_getting_process_reference\npub struct udp_socket_failed_getting_process_reference {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub pid: u32,\n}\n\nimpl udp_socket_failed_getting_process_reference {\n    pub const RPC_ID: u16 = 609u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let pid = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            pid: pid,\n        })\n    }\n}\n// Parsed struct for socket_address_already_assigned\npub struct socket_address_already_assigned {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl socket_address_already_assigned {\n    pub const RPC_ID: u16 = 610u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for ingest_decompression_error\npub struct ingest_decompression_error {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n    pub error: ::std::string::String,\n}\n\nimpl ingest_decompression_error {\n    pub const RPC_ID: u16 = 611u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[6usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_client_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_client_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __l_client_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_client_hostname])\n                .into_owned()\n        };\n        __off += __l_client_hostname;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n            error: error,\n        })\n    }\n}\n// Parsed struct for ingest_processing_error\npub struct ingest_processing_error {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n    pub error: ::std::string::String,\n}\n\nimpl ingest_processing_error {\n    pub const RPC_ID: u16 = 612u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[6usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_client_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_client_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __l_client_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_client_hostname])\n                .into_owned()\n        };\n        __off += __l_client_hostname;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n            error: error,\n        })\n    }\n}\n// Parsed struct for ingest_connection_error\npub struct ingest_connection_error {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n    pub error: ::std::string::String,\n}\n\nimpl ingest_connection_error {\n    pub const RPC_ID: u16 = 613u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[6usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_client_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_client_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __l_client_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_client_hostname])\n                .into_owned()\n        };\n        __off += __l_client_hostname;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n            error: error,\n        })\n    }\n}\n// Parsed struct for agent_auth_success\npub struct agent_auth_success {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n}\n\nimpl agent_auth_success {\n    pub const RPC_ID: u16 = 614u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n        })\n    }\n}\n// Parsed struct for agent_auth_failure\npub struct agent_auth_failure {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n    pub error: ::std::string::String,\n}\n\nimpl agent_auth_failure {\n    pub const RPC_ID: u16 = 615u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[6usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_client_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_client_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __l_client_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_client_hostname])\n                .into_owned()\n        };\n        __off += __l_client_hostname;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n            error: error,\n        })\n    }\n}\n// Parsed struct for agent_attempting_auth_using_api_key\npub struct agent_attempting_auth_using_api_key {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n}\n\nimpl agent_attempting_auth_using_api_key {\n    pub const RPC_ID: u16 = 616u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n        })\n    }\n}\n// Parsed struct for k8s_container_pod_not_found\npub struct k8s_container_pod_not_found {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n}\n\nimpl k8s_container_pod_not_found {\n    pub const RPC_ID: u16 = 617u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 88usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let pod_uid_suffix = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let pod_uid_hash = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            pod_uid_suffix: pod_uid_suffix,\n            pod_uid_hash: pod_uid_hash,\n        })\n    }\n}\n// Parsed struct for agent_connect_success\npub struct agent_connect_success {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub client_type: u8,\n    pub client_hostname: ::std::string::String,\n}\n\nimpl agent_connect_success {\n    pub const RPC_ID: u16 = 618u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_type = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_hostname = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            client_type: client_type,\n            client_hostname: client_hostname,\n        })\n    }\n}\n// Parsed struct for core_stats_start\npub struct core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl core_stats_start {\n    pub const RPC_ID: u16 = 619u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for core_stats_end\npub struct core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl core_stats_end {\n    pub const RPC_ID: u16 = 620u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for span_utilization_stats\npub struct span_utilization_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub span_name: ::std::string::String,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub allocated: u16,\n    pub max_allocated: u16,\n    pub pool_size_: u16,\n    pub time_ns: u64,\n}\n\nimpl span_utilization_stats {\n    pub const RPC_ID: u16 = 621u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        let allocated = u16::from_ne_bytes(body[24usize..24usize + 2].try_into().unwrap());\n        let max_allocated = u16::from_ne_bytes(body[26usize..26usize + 2].try_into().unwrap());\n        let pool_size_ = u16::from_ne_bytes(body[28usize..28usize + 2].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 30usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_span_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_span_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let span_name = if __l_span_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_span_name]).into_owned()\n        };\n        __off += __l_span_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            span_name: span_name,\n            module: module,\n            shard: shard,\n            allocated: allocated,\n            max_allocated: max_allocated,\n            pool_size_: pool_size_,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for connection_message_stats\npub struct connection_message_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub msg_: ::std::string::String,\n    pub shard: u16,\n    pub severity_: u32,\n    pub conn: u16,\n    pub time_ns: u64,\n    pub count: u64,\n}\n\nimpl connection_message_stats {\n    pub const RPC_ID: u16 = 622u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[34usize..34usize + 2].try_into().unwrap());\n        let severity_ = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let conn = u16::from_ne_bytes(body[36usize..36usize + 2].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let count = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 38usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let msg_ = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            msg_: msg_,\n            shard: shard,\n            severity_: severity_,\n            conn: conn,\n            time_ns: time_ns,\n            count: count,\n        })\n    }\n}\n// Parsed struct for connection_message_error_stats\npub struct connection_message_error_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub conn: u16,\n    pub msg_: ::std::string::String,\n    pub error: ::std::string::String,\n    pub count: u64,\n    pub time_ns: u64,\n}\n\nimpl connection_message_error_stats {\n    pub const RPC_ID: u16 = 623u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        let conn = u16::from_ne_bytes(body[32usize..32usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let count = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 36usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_msg_ = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_msg_ > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let msg_ = if __l_msg_ == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_msg_]).into_owned()\n        };\n        __off += __l_msg_;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            conn: conn,\n            msg_: msg_,\n            error: error,\n            count: count,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for status_stats\npub struct status_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub program: ::std::string::String,\n    pub version: ::std::string::String,\n    pub status: u8,\n    pub time_ns: u64,\n}\n\nimpl status_stats {\n    pub const RPC_ID: u16 = 624u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let status = body[26usize];\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 27usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_program = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_program > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let program = if __l_program == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_program]).into_owned()\n        };\n        __off += __l_program;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            program: program,\n            version: version,\n            status: status,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for rpc_receive_stats\npub struct rpc_receive_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub receiver_app: ::std::string::String,\n    pub shard: u16,\n    pub sender_app: ::std::string::String,\n    pub max_latency_ns: u64,\n    pub time_ns: u64,\n}\n\nimpl rpc_receive_stats {\n    pub const RPC_ID: u16 = 625u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let max_latency_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 32usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_receiver_app = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_receiver_app > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let receiver_app = if __l_receiver_app == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_receiver_app])\n                .into_owned()\n        };\n        __off += __l_receiver_app;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let sender_app = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            receiver_app: receiver_app,\n            shard: shard,\n            sender_app: sender_app,\n            max_latency_ns: max_latency_ns,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for rpc_write_stalls_stats\npub struct rpc_write_stalls_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub sender_app: ::std::string::String,\n    pub shard: u16,\n    pub receiver_app: ::std::string::String,\n    pub count: u64,\n    pub time_ns: u64,\n}\n\nimpl rpc_write_stalls_stats {\n    pub const RPC_ID: u16 = 626u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let count = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 32usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_sender_app = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_sender_app > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let sender_app = if __l_sender_app == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_sender_app])\n                .into_owned()\n        };\n        __off += __l_sender_app;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let receiver_app = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            sender_app: sender_app,\n            shard: shard,\n            receiver_app: receiver_app,\n            count: count,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for rpc_write_utilization_stats\npub struct rpc_write_utilization_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub sender_app: ::std::string::String,\n    pub shard: u16,\n    pub receiver_app: ::std::string::String,\n    pub max_buf_used: u32,\n    pub max_buf_util: u64,\n    pub max_elem_util: u64,\n    pub time_ns: u64,\n}\n\nimpl rpc_write_utilization_stats {\n    pub const RPC_ID: u16 = 627u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[42usize..42usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let max_buf_used = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let max_buf_util = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let max_elem_util = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 44usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_sender_app = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_sender_app > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let sender_app = if __l_sender_app == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_sender_app])\n                .into_owned()\n        };\n        __off += __l_sender_app;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let receiver_app = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            sender_app: sender_app,\n            shard: shard,\n            receiver_app: receiver_app,\n            max_buf_used: max_buf_used,\n            max_buf_util: max_buf_util,\n            max_elem_util: max_elem_util,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for code_timing_stats\npub struct code_timing_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub name: ::std::string::String,\n    pub filename: ::std::string::String,\n    pub line: u16,\n    pub index_string: u64,\n    pub count: u64,\n    pub avg_ns: u64,\n    pub min_ns: u64,\n    pub sum_ns: u64,\n    pub max_ns: u64,\n    pub time_ns: u64,\n}\n\nimpl code_timing_stats {\n    pub const RPC_ID: u16 = 628u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[64usize..64usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let line = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        let index_string = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let count = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let avg_ns = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let min_ns = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        let sum_ns = u64::from_ne_bytes(body[48usize..48usize + 8].try_into().unwrap());\n        let max_ns = u64::from_ne_bytes(body[40usize..40usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[56usize..56usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 72usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __l_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_name]).into_owned()\n        };\n        __off += __l_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let filename = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            name: name,\n            filename: filename,\n            line: line,\n            index_string: index_string,\n            count: count,\n            avg_ns: avg_ns,\n            min_ns: min_ns,\n            sum_ns: sum_ns,\n            max_ns: max_ns,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for agg_core_stats_start\npub struct agg_core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl agg_core_stats_start {\n    pub const RPC_ID: u16 = 629u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for agg_core_stats_end\npub struct agg_core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl agg_core_stats_end {\n    pub const RPC_ID: u16 = 630u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for agg_root_truncation_stats\npub struct agg_root_truncation_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub field: ::std::string::String,\n    pub count: u64,\n    pub time_ns: u64,\n}\n\nimpl agg_root_truncation_stats {\n    pub const RPC_ID: u16 = 631u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let count = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 32usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let field = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            field: field,\n            count: count,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for agg_prometheus_bytes_stats\npub struct agg_prometheus_bytes_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub prometheus_bytes_written: u64,\n    pub prometheus_bytes_discarded: u64,\n    pub time_ns: u64,\n}\n\nimpl agg_prometheus_bytes_stats {\n    pub const RPC_ID: u16 = 632u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let prometheus_bytes_written =\n            u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let prometheus_bytes_discarded =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 40usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            prometheus_bytes_written: prometheus_bytes_written,\n            prometheus_bytes_discarded: prometheus_bytes_discarded,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for agg_otlp_grpc_stats\npub struct agg_otlp_grpc_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub client_type: ::std::string::String,\n    pub bytes_failed: u64,\n    pub bytes_sent: u64,\n    pub data_points_failed: u64,\n    pub data_points_sent: u64,\n    pub requests_failed: u64,\n    pub requests_sent: u64,\n    pub unknown_response_tags: u64,\n    pub time_ns: u64,\n}\n\nimpl agg_otlp_grpc_stats {\n    pub const RPC_ID: u16 = 644u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let bytes_failed = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let bytes_sent = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let data_points_failed = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let data_points_sent = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        let requests_failed = u64::from_ne_bytes(body[40usize..40usize + 8].try_into().unwrap());\n        let requests_sent = u64::from_ne_bytes(body[48usize..48usize + 8].try_into().unwrap());\n        let unknown_response_tags =\n            u64::from_ne_bytes(body[56usize..56usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[64usize..64usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 80usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let client_type = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            client_type: client_type,\n            bytes_failed: bytes_failed,\n            bytes_sent: bytes_sent,\n            data_points_failed: data_points_failed,\n            data_points_sent: data_points_sent,\n            requests_failed: requests_failed,\n            requests_sent: requests_sent,\n            unknown_response_tags: unknown_response_tags,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for ingest_core_stats_start\npub struct ingest_core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl ingest_core_stats_start {\n    pub const RPC_ID: u16 = 633u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for ingest_core_stats_end\npub struct ingest_core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl ingest_core_stats_end {\n    pub const RPC_ID: u16 = 634u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for client_handle_pool_stats\npub struct client_handle_pool_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub span_name: ::std::string::String,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub client_handle_pool: u64,\n    pub client_handle_pool_fraction: u64,\n}\n\nimpl client_handle_pool_stats {\n    pub const RPC_ID: u16 = 635u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[56usize..56usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let client_handle_pool = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let client_handle_pool_fraction =\n            u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 62usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_span_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_span_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let span_name = if __l_span_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_span_name]).into_owned()\n        };\n        __off += __l_span_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[50usize..50usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[52usize..52usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[54usize..54usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[58usize..58usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[60usize..60usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            span_name: span_name,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            client_handle_pool: client_handle_pool,\n            client_handle_pool_fraction: client_handle_pool_fraction,\n        })\n    }\n}\n// Parsed struct for agent_connection_message_stats\npub struct agent_connection_message_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub message: ::std::string::String,\n    pub severity_: u16,\n    pub count: u64,\n}\n\nimpl agent_connection_message_stats {\n    pub const RPC_ID: u16 = 636u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[46usize..46usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let severity_ = u16::from_ne_bytes(body[54usize..54usize + 2].try_into().unwrap());\n        let count = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 56usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[38usize..38usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[50usize..50usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[52usize..52usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let message = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            message: message,\n            severity_: severity_,\n            count: count,\n        })\n    }\n}\n// Parsed struct for agent_connection_message_error_stats\npub struct agent_connection_message_error_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub message: ::std::string::String,\n    pub error: ::std::string::String,\n    pub count: u64,\n}\n\nimpl agent_connection_message_error_stats {\n    pub const RPC_ID: u16 = 637u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[46usize..46usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let count = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 56usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[38usize..38usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[50usize..50usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[52usize..52usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[54usize..54usize + 2]);\n        let __l_message = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_message > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let message = if __l_message == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_message]).into_owned()\n        };\n        __off += __l_message;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            message: message,\n            error: error,\n            count: count,\n        })\n    }\n}\n// Parsed struct for connection_stats\npub struct connection_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub time_since_last_message_ns: u64,\n    pub clock_offset_ns: u64,\n}\n\nimpl connection_stats {\n    pub const RPC_ID: u16 = 638u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[54usize..54usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let time_since_last_message_ns =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let clock_offset_ns = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 60usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[50usize..50usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[52usize..52usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[56usize..56usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[58usize..58usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            time_since_last_message_ns: time_since_last_message_ns,\n            clock_offset_ns: clock_offset_ns,\n        })\n    }\n}\n// Parsed struct for collector_log_stats\npub struct collector_log_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub severity_: ::std::string::String,\n    pub count: u32,\n}\n\nimpl collector_log_stats {\n    pub const RPC_ID: u16 = 639u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[26usize..26usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[42usize..42usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let count = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 50usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[30usize..30usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[38usize..38usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let severity_ = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            severity_: severity_,\n            count: count,\n        })\n    }\n}\n// Parsed struct for entry_point_stats\npub struct entry_point_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub agent_hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub kernel_headers_source: ::std::string::String,\n    pub entrypoint_error: ::std::string::String,\n    pub entrypoint_info: u8,\n}\n\nimpl entry_point_stats {\n    pub const RPC_ID: u16 = 640u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[38usize..38usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let entrypoint_info = body[48usize];\n\n        // Decode dynamic payload strings\n        let mut __off = 49usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[30usize..30usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_agent_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_agent_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let agent_hostname = if __l_agent_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_agent_hostname])\n                .into_owned()\n        };\n        __off += __l_agent_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_kernel_headers_source = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_headers_source > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_headers_source = if __l_kernel_headers_source == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_headers_source])\n                .into_owned()\n        };\n        __off += __l_kernel_headers_source;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let entrypoint_error = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            agent_hostname: agent_hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            kernel_headers_source: kernel_headers_source,\n            entrypoint_error: entrypoint_error,\n            entrypoint_info: entrypoint_info,\n        })\n    }\n}\n// Parsed struct for collector_health_stats\npub struct collector_health_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub status: ::std::string::String,\n    pub status_detail: ::std::string::String,\n}\n\nimpl collector_health_stats {\n    pub const RPC_ID: u16 = 641u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[38usize..38usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 48usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[30usize..30usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let hostname = if __l_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_hostname]).into_owned()\n        };\n        __off += __l_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_status = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_status > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let status = if __l_status == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_status]).into_owned()\n        };\n        __off += __l_status;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let status_detail = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            hostname: hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            status: status,\n            status_detail: status_detail,\n        })\n    }\n}\n// Parsed struct for bpf_log_stats\npub struct bpf_log_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub shard: u16,\n    pub version: ::std::string::String,\n    pub cloud: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub node_id: ::std::string::String,\n    pub kernel_version: ::std::string::String,\n    pub client_type: u16,\n    pub hostname: ::std::string::String,\n    pub os: ::std::string::String,\n    pub os_version: ::std::string::String,\n    pub time_ns: u64,\n    pub filename: ::std::string::String,\n    pub line: ::std::string::String,\n    pub code: ::std::string::String,\n    pub arg0: ::std::string::String,\n    pub arg1: ::std::string::String,\n    pub arg2: ::std::string::String,\n}\n\nimpl bpf_log_stats {\n    pub const RPC_ID: u16 = 642u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let shard = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let client_type = u16::from_ne_bytes(body[38usize..38usize + 2].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 56usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_module = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_module > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __l_module == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_module]).into_owned()\n        };\n        __off += __l_module;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[24usize..24usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[26usize..26usize + 2]);\n        let __l_cloud = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_cloud > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cloud = if __l_cloud == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_cloud]).into_owned()\n        };\n        __off += __l_cloud;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[28usize..28usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[30usize..30usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[32usize..32usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[34usize..34usize + 2]);\n        let __l_node_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_node_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let node_id = if __l_node_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_node_id]).into_owned()\n        };\n        __off += __l_node_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[36usize..36usize + 2]);\n        let __l_kernel_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_kernel_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let kernel_version = if __l_kernel_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_kernel_version])\n                .into_owned()\n        };\n        __off += __l_kernel_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[40usize..40usize + 2]);\n        let __l_hostname = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_hostname > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let hostname = if __l_hostname == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_hostname]).into_owned()\n        };\n        __off += __l_hostname;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[42usize..42usize + 2]);\n        let __l_os = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os = if __l_os == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os]).into_owned()\n        };\n        __off += __l_os;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[44usize..44usize + 2]);\n        let __l_os_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_os_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let os_version = if __l_os_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_os_version])\n                .into_owned()\n        };\n        __off += __l_os_version;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[46usize..46usize + 2]);\n        let __l_filename = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_filename > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let filename = if __l_filename == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_filename]).into_owned()\n        };\n        __off += __l_filename;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[48usize..48usize + 2]);\n        let __l_line = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_line > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let line = if __l_line == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_line]).into_owned()\n        };\n        __off += __l_line;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[50usize..50usize + 2]);\n        let __l_code = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_code > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let code = if __l_code == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_code]).into_owned()\n        };\n        __off += __l_code;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[52usize..52usize + 2]);\n        let __l_arg0 = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_arg0 > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let arg0 = if __l_arg0 == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_arg0]).into_owned()\n        };\n        __off += __l_arg0;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[54usize..54usize + 2]);\n        let __l_arg1 = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_arg1 > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let arg1 = if __l_arg1 == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_arg1]).into_owned()\n        };\n        __off += __l_arg1;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let arg2 = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            shard: shard,\n            version: version,\n            cloud: cloud,\n            env: env,\n            role: role,\n            az: az,\n            node_id: node_id,\n            kernel_version: kernel_version,\n            client_type: client_type,\n            hostname: hostname,\n            os: os,\n            os_version: os_version,\n            time_ns: time_ns,\n            filename: filename,\n            line: line,\n            code: code,\n            arg0: arg0,\n            arg1: arg1,\n            arg2: arg2,\n        })\n    }\n}\n// Parsed struct for server_stats\npub struct server_stats {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub module: ::std::string::String,\n    pub connection_counter: u64,\n    pub disconnect_counter: u64,\n    pub time_ns: u64,\n}\n\nimpl server_stats {\n    pub const RPC_ID: u16 = 643u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        let connection_counter = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let disconnect_counter = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let time_ns = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n        let mut __off = 40usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let module = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            module: module,\n            connection_counter: connection_counter,\n            disconnect_counter: disconnect_counter,\n            time_ns: time_ns,\n        })\n    }\n}\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/logging/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__logger_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__logger_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(600u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__logger_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const LOGGER_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod logger_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__logger_start>();\n        let align = align_of::<jb_logging__logger_start>();\n        let padded_raw_size = (LOGGER_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__logger_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__logger_start, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__logger_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__logger_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(601u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__logger_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const LOGGER_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod logger_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__logger_end>();\n        let align = align_of::<jb_logging__logger_end>();\n        let padded_raw_size = (LOGGER_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__logger_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__logger_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_lost_events {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub count: u32,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agent_lost_events {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(602u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_lost_events {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_LOST_EVENTS_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agent_lost_events_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_lost_events>();\n        let align = align_of::<jb_logging__agent_lost_events>();\n        let padded_raw_size = (AGENT_LOST_EVENTS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agent_lost_events, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agent_lost_events, _len), 2);\n        assert_eq!(offset_of!(jb_logging__agent_lost_events, count), 4usize);\n        assert_eq!(offset_of!(jb_logging__agent_lost_events, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__pod_not_found {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub on_delete: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__pod_not_found {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(603u16, true)\n    }\n}\n\nimpl Default for jb_logging__pod_not_found {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const POD_NOT_FOUND_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod pod_not_found_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__pod_not_found>();\n        let align = align_of::<jb_logging__pod_not_found>();\n        let padded_raw_size = (POD_NOT_FOUND_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__pod_not_found, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__pod_not_found, _len), 2);\n        assert_eq!(offset_of!(jb_logging__pod_not_found, on_delete), 4usize);\n        assert_eq!(offset_of!(jb_logging__pod_not_found, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__cgroup_not_found {\n    pub _rpc_id: u16,\n    pub cgroup: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__cgroup_not_found {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(604u16, 24, true)\n    }\n}\n\nimpl Default for jb_logging__cgroup_not_found {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CGROUP_NOT_FOUND_WIRE_SIZE: usize = 24;\n\n#[cfg(test)]\nmod cgroup_not_found_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__cgroup_not_found>();\n        let align = align_of::<jb_logging__cgroup_not_found>();\n        let padded_raw_size = (CGROUP_NOT_FOUND_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__cgroup_not_found, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__cgroup_not_found, cgroup), 8usize);\n        assert_eq!(offset_of!(jb_logging__cgroup_not_found, _ref), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__rewriting_private_to_public_ip_mapping {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub private_addr: u16,\n    pub existing_public_addr: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__rewriting_private_to_public_ip_mapping {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(605u16, true)\n    }\n}\n\nimpl Default for jb_logging__rewriting_private_to_public_ip_mapping {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const REWRITING_PRIVATE_TO_PUBLIC_IP_MAPPING_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod rewriting_private_to_public_ip_mapping_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__rewriting_private_to_public_ip_mapping>();\n        let align = align_of::<jb_logging__rewriting_private_to_public_ip_mapping>();\n        let padded_raw_size =\n            (REWRITING_PRIVATE_TO_PUBLIC_IP_MAPPING_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__rewriting_private_to_public_ip_mapping, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rewriting_private_to_public_ip_mapping, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__rewriting_private_to_public_ip_mapping,\n                private_addr\n            ),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__rewriting_private_to_public_ip_mapping,\n                existing_public_addr\n            ),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rewriting_private_to_public_ip_mapping, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__private_ip_in_private_to_public_ip_mapping {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub private_addr: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__private_ip_in_private_to_public_ip_mapping {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(606u16, true)\n    }\n}\n\nimpl Default for jb_logging__private_ip_in_private_to_public_ip_mapping {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PRIVATE_IP_IN_PRIVATE_TO_PUBLIC_IP_MAPPING_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod private_ip_in_private_to_public_ip_mapping_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__private_ip_in_private_to_public_ip_mapping>();\n        let align = align_of::<jb_logging__private_ip_in_private_to_public_ip_mapping>();\n        let padded_raw_size =\n            (PRIVATE_IP_IN_PRIVATE_TO_PUBLIC_IP_MAPPING_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(\n                jb_logging__private_ip_in_private_to_public_ip_mapping,\n                _rpc_id\n            ),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__private_ip_in_private_to_public_ip_mapping, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__private_ip_in_private_to_public_ip_mapping,\n                private_addr\n            ),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__private_ip_in_private_to_public_ip_mapping, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__failed_to_insert_dns_record {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__failed_to_insert_dns_record {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(607u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__failed_to_insert_dns_record {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const FAILED_TO_INSERT_DNS_RECORD_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod failed_to_insert_dns_record_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__failed_to_insert_dns_record>();\n        let align = align_of::<jb_logging__failed_to_insert_dns_record>();\n        let padded_raw_size = (FAILED_TO_INSERT_DNS_RECORD_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__failed_to_insert_dns_record, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__failed_to_insert_dns_record, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__tcp_socket_failed_getting_process_reference {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub _ref: u64,\n}\n\nimpl jb_logging__tcp_socket_failed_getting_process_reference {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(608u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__tcp_socket_failed_getting_process_reference {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_SOCKET_FAILED_GETTING_PROCESS_REFERENCE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod tcp_socket_failed_getting_process_reference_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__tcp_socket_failed_getting_process_reference>();\n        let align = align_of::<jb_logging__tcp_socket_failed_getting_process_reference>();\n        let padded_raw_size =\n            (TCP_SOCKET_FAILED_GETTING_PROCESS_REFERENCE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(\n                jb_logging__tcp_socket_failed_getting_process_reference,\n                _rpc_id\n            ),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__tcp_socket_failed_getting_process_reference, pid),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__tcp_socket_failed_getting_process_reference,\n                _ref\n            ),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__udp_socket_failed_getting_process_reference {\n    pub _rpc_id: u16,\n    pub pid: u32,\n    pub _ref: u64,\n}\n\nimpl jb_logging__udp_socket_failed_getting_process_reference {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(609u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__udp_socket_failed_getting_process_reference {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_SOCKET_FAILED_GETTING_PROCESS_REFERENCE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod udp_socket_failed_getting_process_reference_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__udp_socket_failed_getting_process_reference>();\n        let align = align_of::<jb_logging__udp_socket_failed_getting_process_reference>();\n        let padded_raw_size =\n            (UDP_SOCKET_FAILED_GETTING_PROCESS_REFERENCE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(\n                jb_logging__udp_socket_failed_getting_process_reference,\n                _rpc_id\n            ),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__udp_socket_failed_getting_process_reference, pid),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__udp_socket_failed_getting_process_reference,\n                _ref\n            ),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__socket_address_already_assigned {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__socket_address_already_assigned {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(610u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__socket_address_already_assigned {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SOCKET_ADDRESS_ALREADY_ASSIGNED_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod socket_address_already_assigned_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__socket_address_already_assigned>();\n        let align = align_of::<jb_logging__socket_address_already_assigned>();\n        let padded_raw_size =\n            (SOCKET_ADDRESS_ALREADY_ASSIGNED_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__socket_address_already_assigned, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__socket_address_already_assigned, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__ingest_decompression_error {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_hostname: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__ingest_decompression_error {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(611u16, true)\n    }\n}\n\nimpl Default for jb_logging__ingest_decompression_error {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const INGEST_DECOMPRESSION_ERROR_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod ingest_decompression_error_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__ingest_decompression_error>();\n        let align = align_of::<jb_logging__ingest_decompression_error>();\n        let padded_raw_size = (INGEST_DECOMPRESSION_ERROR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__ingest_decompression_error, _rpc_id),\n            0\n        );\n        assert_eq!(offset_of!(jb_logging__ingest_decompression_error, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__ingest_decompression_error, client_hostname),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_decompression_error, client_type),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_decompression_error, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__ingest_processing_error {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_hostname: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__ingest_processing_error {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(612u16, true)\n    }\n}\n\nimpl Default for jb_logging__ingest_processing_error {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const INGEST_PROCESSING_ERROR_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod ingest_processing_error_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__ingest_processing_error>();\n        let align = align_of::<jb_logging__ingest_processing_error>();\n        let padded_raw_size = (INGEST_PROCESSING_ERROR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__ingest_processing_error, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__ingest_processing_error, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__ingest_processing_error, client_hostname),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_processing_error, client_type),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_processing_error, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__ingest_connection_error {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_hostname: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__ingest_connection_error {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(613u16, true)\n    }\n}\n\nimpl Default for jb_logging__ingest_connection_error {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const INGEST_CONNECTION_ERROR_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod ingest_connection_error_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__ingest_connection_error>();\n        let align = align_of::<jb_logging__ingest_connection_error>();\n        let padded_raw_size = (INGEST_CONNECTION_ERROR_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__ingest_connection_error, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__ingest_connection_error, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__ingest_connection_error, client_hostname),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_connection_error, client_type),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__ingest_connection_error, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_auth_success {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agent_auth_success {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(614u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_auth_success {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_AUTH_SUCCESS_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agent_auth_success_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_auth_success>();\n        let align = align_of::<jb_logging__agent_auth_success>();\n        let padded_raw_size = (AGENT_AUTH_SUCCESS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agent_auth_success, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agent_auth_success, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__agent_auth_success, client_type),\n            4usize\n        );\n        assert_eq!(offset_of!(jb_logging__agent_auth_success, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_auth_failure {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_hostname: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agent_auth_failure {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(615u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_auth_failure {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_AUTH_FAILURE_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agent_auth_failure_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_auth_failure>();\n        let align = align_of::<jb_logging__agent_auth_failure>();\n        let padded_raw_size = (AGENT_AUTH_FAILURE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agent_auth_failure, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agent_auth_failure, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__agent_auth_failure, client_hostname),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_auth_failure, client_type),\n            6usize\n        );\n        assert_eq!(offset_of!(jb_logging__agent_auth_failure, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_attempting_auth_using_api_key {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agent_attempting_auth_using_api_key {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(616u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_attempting_auth_using_api_key {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_ATTEMPTING_AUTH_USING_API_KEY_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agent_attempting_auth_using_api_key_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_attempting_auth_using_api_key>();\n        let align = align_of::<jb_logging__agent_attempting_auth_using_api_key>();\n        let padded_raw_size =\n            (AGENT_ATTEMPTING_AUTH_USING_API_KEY_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__agent_attempting_auth_using_api_key, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_attempting_auth_using_api_key, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_attempting_auth_using_api_key, client_type),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_attempting_auth_using_api_key, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__k8s_container_pod_not_found {\n    pub _rpc_id: u16,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__k8s_container_pod_not_found {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(617u16, 88, true)\n    }\n}\n\nimpl Default for jb_logging__k8s_container_pod_not_found {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_CONTAINER_POD_NOT_FOUND_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod k8s_container_pod_not_found_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__k8s_container_pod_not_found>();\n        let align = align_of::<jb_logging__k8s_container_pod_not_found>();\n        let padded_raw_size = (K8S_CONTAINER_POD_NOT_FOUND_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__k8s_container_pod_not_found, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__k8s_container_pod_not_found, pod_uid_suffix),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__k8s_container_pod_not_found, pod_uid_hash),\n            72usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__k8s_container_pod_not_found, _ref),\n            80usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_connect_success {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub client_type: u8,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agent_connect_success {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(618u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_connect_success {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_CONNECT_SUCCESS_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agent_connect_success_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_connect_success>();\n        let align = align_of::<jb_logging__agent_connect_success>();\n        let padded_raw_size = (AGENT_CONNECT_SUCCESS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agent_connect_success, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agent_connect_success, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__agent_connect_success, client_type),\n            4usize\n        );\n        assert_eq!(offset_of!(jb_logging__agent_connect_success, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__core_stats_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(619u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__core_stats_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CORE_STATS_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod core_stats_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__core_stats_start>();\n        let align = align_of::<jb_logging__core_stats_start>();\n        let padded_raw_size = (CORE_STATS_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__core_stats_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__core_stats_start, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__core_stats_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(620u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__core_stats_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CORE_STATS_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod core_stats_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__core_stats_end>();\n        let align = align_of::<jb_logging__core_stats_end>();\n        let padded_raw_size = (CORE_STATS_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__core_stats_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__core_stats_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__span_utilization_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub span_name: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub allocated: u16,\n    pub max_allocated: u16,\n    pub pool_size_: u16,\n}\n\nimpl jb_logging__span_utilization_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(621u16, true)\n    }\n}\n\nimpl Default for jb_logging__span_utilization_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SPAN_UTILIZATION_STATS_WIRE_SIZE: usize = 30;\n\n#[cfg(test)]\nmod span_utilization_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__span_utilization_stats>();\n        let align = align_of::<jb_logging__span_utilization_stats>();\n        let padded_raw_size = (SPAN_UTILIZATION_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__span_utilization_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__span_utilization_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, span_name),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, _ref),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, allocated),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, max_allocated),\n            26usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__span_utilization_stats, pool_size_),\n            28usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__connection_message_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub severity_: u32,\n    pub time_ns: u64,\n    pub count: u64,\n    pub _ref: u64,\n    pub module: u16,\n    pub shard: u16,\n    pub conn: u16,\n}\n\nimpl jb_logging__connection_message_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(622u16, true)\n    }\n}\n\nimpl Default for jb_logging__connection_message_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONNECTION_MESSAGE_STATS_WIRE_SIZE: usize = 38;\n\n#[cfg(test)]\nmod connection_message_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__connection_message_stats>();\n        let align = align_of::<jb_logging__connection_message_stats>();\n        let padded_raw_size = (CONNECTION_MESSAGE_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__connection_message_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__connection_message_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, severity_),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, count),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, module),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, shard),\n            34usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_stats, conn),\n            36usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__connection_message_error_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub count: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub conn: u16,\n    pub msg_: u16,\n}\n\nimpl jb_logging__connection_message_error_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(623u16, true)\n    }\n}\n\nimpl Default for jb_logging__connection_message_error_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONNECTION_MESSAGE_ERROR_STATS_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod connection_message_error_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__connection_message_error_stats>();\n        let align = align_of::<jb_logging__connection_message_error_stats>();\n        let padded_raw_size =\n            (CONNECTION_MESSAGE_ERROR_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, count),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, time_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, conn),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_message_error_stats, msg_),\n            34usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__status_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub program: u16,\n    pub status: u8,\n}\n\nimpl jb_logging__status_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(624u16, true)\n    }\n}\n\nimpl Default for jb_logging__status_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const STATUS_STATS_WIRE_SIZE: usize = 27;\n\n#[cfg(test)]\nmod status_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__status_stats>();\n        let align = align_of::<jb_logging__status_stats>();\n        let padded_raw_size = (STATUS_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__status_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__status_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__status_stats, module), 4usize);\n        assert_eq!(offset_of!(jb_logging__status_stats, shard), 6usize);\n        assert_eq!(offset_of!(jb_logging__status_stats, time_ns), 8usize);\n        assert_eq!(offset_of!(jb_logging__status_stats, _ref), 16usize);\n        assert_eq!(offset_of!(jb_logging__status_stats, program), 24usize);\n        assert_eq!(offset_of!(jb_logging__status_stats, status), 26usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__rpc_receive_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub receiver_app: u16,\n    pub shard: u16,\n    pub max_latency_ns: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__rpc_receive_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(625u16, true)\n    }\n}\n\nimpl Default for jb_logging__rpc_receive_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const RPC_RECEIVE_STATS_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod rpc_receive_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__rpc_receive_stats>();\n        let align = align_of::<jb_logging__rpc_receive_stats>();\n        let padded_raw_size = (RPC_RECEIVE_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__rpc_receive_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__rpc_receive_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__rpc_receive_stats, receiver_app),\n            4usize\n        );\n        assert_eq!(offset_of!(jb_logging__rpc_receive_stats, shard), 6usize);\n        assert_eq!(\n            offset_of!(jb_logging__rpc_receive_stats, max_latency_ns),\n            8usize\n        );\n        assert_eq!(offset_of!(jb_logging__rpc_receive_stats, time_ns), 16usize);\n        assert_eq!(offset_of!(jb_logging__rpc_receive_stats, _ref), 24usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__rpc_write_stalls_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub sender_app: u16,\n    pub shard: u16,\n    pub count: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__rpc_write_stalls_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(626u16, true)\n    }\n}\n\nimpl Default for jb_logging__rpc_write_stalls_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const RPC_WRITE_STALLS_STATS_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod rpc_write_stalls_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__rpc_write_stalls_stats>();\n        let align = align_of::<jb_logging__rpc_write_stalls_stats>();\n        let padded_raw_size = (RPC_WRITE_STALLS_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__rpc_write_stalls_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__rpc_write_stalls_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_stalls_stats, sender_app),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_stalls_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_stalls_stats, count),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_stalls_stats, time_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_stalls_stats, _ref),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__rpc_write_utilization_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub max_buf_used: u32,\n    pub max_buf_util: u64,\n    pub max_elem_util: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub sender_app: u16,\n    pub shard: u16,\n}\n\nimpl jb_logging__rpc_write_utilization_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(627u16, true)\n    }\n}\n\nimpl Default for jb_logging__rpc_write_utilization_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const RPC_WRITE_UTILIZATION_STATS_WIRE_SIZE: usize = 44;\n\n#[cfg(test)]\nmod rpc_write_utilization_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__rpc_write_utilization_stats>();\n        let align = align_of::<jb_logging__rpc_write_utilization_stats>();\n        let padded_raw_size = (RPC_WRITE_UTILIZATION_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, _rpc_id),\n            0\n        );\n        assert_eq!(offset_of!(jb_logging__rpc_write_utilization_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, max_buf_used),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, max_buf_util),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, max_elem_util),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, time_ns),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, _ref),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, sender_app),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__rpc_write_utilization_stats, shard),\n            42usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__code_timing_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub name: u16,\n    pub line: u16,\n    pub index_string: u64,\n    pub count: u64,\n    pub avg_ns: u64,\n    pub min_ns: u64,\n    pub max_ns: u64,\n    pub sum_ns: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__code_timing_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(628u16, true)\n    }\n}\n\nimpl Default for jb_logging__code_timing_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CODE_TIMING_STATS_WIRE_SIZE: usize = 72;\n\n#[cfg(test)]\nmod code_timing_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__code_timing_stats>();\n        let align = align_of::<jb_logging__code_timing_stats>();\n        let padded_raw_size = (CODE_TIMING_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, name), 4usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, line), 6usize);\n        assert_eq!(\n            offset_of!(jb_logging__code_timing_stats, index_string),\n            8usize\n        );\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, count), 16usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, avg_ns), 24usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, min_ns), 32usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, max_ns), 40usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, sum_ns), 48usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, time_ns), 56usize);\n        assert_eq!(offset_of!(jb_logging__code_timing_stats, _ref), 64usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agg_core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agg_core_stats_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(629u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__agg_core_stats_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_CORE_STATS_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agg_core_stats_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agg_core_stats_start>();\n        let align = align_of::<jb_logging__agg_core_stats_start>();\n        let padded_raw_size = (AGG_CORE_STATS_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agg_core_stats_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agg_core_stats_start, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agg_core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agg_core_stats_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(630u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__agg_core_stats_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_CORE_STATS_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod agg_core_stats_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agg_core_stats_end>();\n        let align = align_of::<jb_logging__agg_core_stats_end>();\n        let padded_raw_size = (AGG_CORE_STATS_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agg_core_stats_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agg_core_stats_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agg_root_truncation_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub count: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agg_root_truncation_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(631u16, true)\n    }\n}\n\nimpl Default for jb_logging__agg_root_truncation_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_ROOT_TRUNCATION_STATS_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod agg_root_truncation_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agg_root_truncation_stats>();\n        let align = align_of::<jb_logging__agg_root_truncation_stats>();\n        let padded_raw_size = (AGG_ROOT_TRUNCATION_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, _rpc_id),\n            0\n        );\n        assert_eq!(offset_of!(jb_logging__agg_root_truncation_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, count),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, time_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_root_truncation_stats, _ref),\n            24usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agg_prometheus_bytes_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub shard: u16,\n    pub prometheus_bytes_written: u64,\n    pub prometheus_bytes_discarded: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agg_prometheus_bytes_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(632u16, true)\n    }\n}\n\nimpl Default for jb_logging__agg_prometheus_bytes_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_PROMETHEUS_BYTES_STATS_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod agg_prometheus_bytes_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agg_prometheus_bytes_stats>();\n        let align = align_of::<jb_logging__agg_prometheus_bytes_stats>();\n        let padded_raw_size = (AGG_PROMETHEUS_BYTES_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__agg_prometheus_bytes_stats, _rpc_id),\n            0\n        );\n        assert_eq!(offset_of!(jb_logging__agg_prometheus_bytes_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__agg_prometheus_bytes_stats, shard),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__agg_prometheus_bytes_stats,\n                prometheus_bytes_written\n            ),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__agg_prometheus_bytes_stats,\n                prometheus_bytes_discarded\n            ),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_prometheus_bytes_stats, time_ns),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_prometheus_bytes_stats, _ref),\n            32usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agg_otlp_grpc_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub bytes_failed: u64,\n    pub bytes_sent: u64,\n    pub data_points_failed: u64,\n    pub data_points_sent: u64,\n    pub requests_failed: u64,\n    pub requests_sent: u64,\n    pub unknown_response_tags: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__agg_otlp_grpc_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(644u16, true)\n    }\n}\n\nimpl Default for jb_logging__agg_otlp_grpc_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGG_OTLP_GRPC_STATS_WIRE_SIZE: usize = 80;\n\n#[cfg(test)]\nmod agg_otlp_grpc_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agg_otlp_grpc_stats>();\n        let align = align_of::<jb_logging__agg_otlp_grpc_stats>();\n        let padded_raw_size = (AGG_OTLP_GRPC_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__agg_otlp_grpc_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__agg_otlp_grpc_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__agg_otlp_grpc_stats, module), 4usize);\n        assert_eq!(offset_of!(jb_logging__agg_otlp_grpc_stats, shard), 6usize);\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, bytes_failed),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, bytes_sent),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, data_points_failed),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, data_points_sent),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, requests_failed),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, requests_sent),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, unknown_response_tags),\n            56usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agg_otlp_grpc_stats, time_ns),\n            64usize\n        );\n        assert_eq!(offset_of!(jb_logging__agg_otlp_grpc_stats, _ref), 72usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__ingest_core_stats_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__ingest_core_stats_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(633u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__ingest_core_stats_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const INGEST_CORE_STATS_START_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod ingest_core_stats_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__ingest_core_stats_start>();\n        let align = align_of::<jb_logging__ingest_core_stats_start>();\n        let padded_raw_size = (INGEST_CORE_STATS_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__ingest_core_stats_start, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_logging__ingest_core_stats_start, _ref),\n            8usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__ingest_core_stats_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_logging__ingest_core_stats_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(634u16, 16, true)\n    }\n}\n\nimpl Default for jb_logging__ingest_core_stats_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const INGEST_CORE_STATS_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod ingest_core_stats_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__ingest_core_stats_end>();\n        let align = align_of::<jb_logging__ingest_core_stats_end>();\n        let padded_raw_size = (INGEST_CORE_STATS_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__ingest_core_stats_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__ingest_core_stats_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__client_handle_pool_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub client_handle_pool: u64,\n    pub client_handle_pool_fraction: u64,\n    pub _ref: u64,\n    pub span_name: u16,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n}\n\nimpl jb_logging__client_handle_pool_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(635u16, true)\n    }\n}\n\nimpl Default for jb_logging__client_handle_pool_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CLIENT_HANDLE_POOL_STATS_WIRE_SIZE: usize = 62;\n\n#[cfg(test)]\nmod client_handle_pool_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__client_handle_pool_stats>();\n        let align = align_of::<jb_logging__client_handle_pool_stats>();\n        let padded_raw_size = (CLIENT_HANDLE_POOL_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__client_handle_pool_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__client_handle_pool_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, client_handle_pool),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__client_handle_pool_stats,\n                client_handle_pool_fraction\n            ),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, _ref),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, span_name),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, version),\n            42usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, cloud),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, env),\n            46usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, role),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, az),\n            50usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, node_id),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, kernel_version),\n            54usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, client_type),\n            56usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, agent_hostname),\n            58usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__client_handle_pool_stats, os),\n            60usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_connection_message_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub count: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n    pub severity_: u16,\n}\n\nimpl jb_logging__agent_connection_message_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(636u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_connection_message_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_CONNECTION_MESSAGE_STATS_WIRE_SIZE: usize = 56;\n\n#[cfg(test)]\nmod agent_connection_message_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_connection_message_stats>();\n        let align = align_of::<jb_logging__agent_connection_message_stats>();\n        let padded_raw_size =\n            (AGENT_CONNECTION_MESSAGE_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, count),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, version),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, cloud),\n            34usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, env),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, role),\n            38usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, az),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, node_id),\n            42usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, kernel_version),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, client_type),\n            46usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, agent_hostname),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, os),\n            50usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, os_version),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_stats, severity_),\n            54usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__agent_connection_message_error_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub count: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n    pub message: u16,\n}\n\nimpl jb_logging__agent_connection_message_error_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(637u16, true)\n    }\n}\n\nimpl Default for jb_logging__agent_connection_message_error_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_CONNECTION_MESSAGE_ERROR_STATS_WIRE_SIZE: usize = 56;\n\n#[cfg(test)]\nmod agent_connection_message_error_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__agent_connection_message_error_stats>();\n        let align = align_of::<jb_logging__agent_connection_message_error_stats>();\n        let padded_raw_size =\n            (AGENT_CONNECTION_MESSAGE_ERROR_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, _rpc_id),\n            0\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, _len),\n            2\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, count),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, _ref),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, version),\n            32usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, cloud),\n            34usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, env),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, role),\n            38usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, az),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, node_id),\n            42usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__agent_connection_message_error_stats,\n                kernel_version\n            ),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__agent_connection_message_error_stats,\n                client_type\n            ),\n            46usize\n        );\n        assert_eq!(\n            offset_of!(\n                jb_logging__agent_connection_message_error_stats,\n                agent_hostname\n            ),\n            48usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, os),\n            50usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, os_version),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__agent_connection_message_error_stats, message),\n            54usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__connection_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub time_since_last_message_ns: u64,\n    pub clock_offset_ns: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n}\n\nimpl jb_logging__connection_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(638u16, true)\n    }\n}\n\nimpl Default for jb_logging__connection_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONNECTION_STATS_WIRE_SIZE: usize = 60;\n\n#[cfg(test)]\nmod connection_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__connection_stats>();\n        let align = align_of::<jb_logging__connection_stats>();\n        let padded_raw_size = (CONNECTION_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__connection_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__connection_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__connection_stats, module), 4usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, shard), 6usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, time_ns), 8usize);\n        assert_eq!(\n            offset_of!(jb_logging__connection_stats, time_since_last_message_ns),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_stats, clock_offset_ns),\n            24usize\n        );\n        assert_eq!(offset_of!(jb_logging__connection_stats, _ref), 32usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, version), 40usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, cloud), 42usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, env), 44usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, role), 46usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, az), 48usize);\n        assert_eq!(offset_of!(jb_logging__connection_stats, node_id), 50usize);\n        assert_eq!(\n            offset_of!(jb_logging__connection_stats, kernel_version),\n            52usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_stats, client_type),\n            54usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__connection_stats, agent_hostname),\n            56usize\n        );\n        assert_eq!(offset_of!(jb_logging__connection_stats, os), 58usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__collector_log_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub count: u32,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub module: u16,\n    pub shard: u16,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n}\n\nimpl jb_logging__collector_log_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(639u16, true)\n    }\n}\n\nimpl Default for jb_logging__collector_log_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const COLLECTOR_LOG_STATS_WIRE_SIZE: usize = 50;\n\n#[cfg(test)]\nmod collector_log_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__collector_log_stats>();\n        let align = align_of::<jb_logging__collector_log_stats>();\n        let padded_raw_size = (COLLECTOR_LOG_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, count), 4usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, time_ns), 8usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, _ref), 16usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, module), 24usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, shard), 26usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, version),\n            28usize\n        );\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, cloud), 30usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, env), 32usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, role), 34usize);\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, az), 36usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, node_id),\n            38usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, kernel_version),\n            40usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, client_type),\n            42usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, agent_hostname),\n            44usize\n        );\n        assert_eq!(offset_of!(jb_logging__collector_log_stats, os), 46usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_log_stats, os_version),\n            48usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__entry_point_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub agent_hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n    pub kernel_headers_source: u16,\n    pub entrypoint_info: u8,\n}\n\nimpl jb_logging__entry_point_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(640u16, true)\n    }\n}\n\nimpl Default for jb_logging__entry_point_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const ENTRY_POINT_STATS_WIRE_SIZE: usize = 49;\n\n#[cfg(test)]\nmod entry_point_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__entry_point_stats>();\n        let align = align_of::<jb_logging__entry_point_stats>();\n        let padded_raw_size = (ENTRY_POINT_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, module), 4usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, shard), 6usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, time_ns), 8usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, _ref), 16usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, version), 24usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, cloud), 26usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, env), 28usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, role), 30usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, az), 32usize);\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, node_id), 34usize);\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, kernel_version),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, client_type),\n            38usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, agent_hostname),\n            40usize\n        );\n        assert_eq!(offset_of!(jb_logging__entry_point_stats, os), 42usize);\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, os_version),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, kernel_headers_source),\n            46usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__entry_point_stats, entrypoint_info),\n            48usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__collector_health_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n    pub status: u16,\n}\n\nimpl jb_logging__collector_health_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(641u16, true)\n    }\n}\n\nimpl Default for jb_logging__collector_health_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const COLLECTOR_HEALTH_STATS_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod collector_health_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__collector_health_stats>();\n        let align = align_of::<jb_logging__collector_health_stats>();\n        let padded_raw_size = (COLLECTOR_HEALTH_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__collector_health_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__collector_health_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, module),\n            4usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, shard),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, _ref),\n            16usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, version),\n            24usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, cloud),\n            26usize\n        );\n        assert_eq!(offset_of!(jb_logging__collector_health_stats, env), 28usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, role),\n            30usize\n        );\n        assert_eq!(offset_of!(jb_logging__collector_health_stats, az), 32usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, node_id),\n            34usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, kernel_version),\n            36usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, client_type),\n            38usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, hostname),\n            40usize\n        );\n        assert_eq!(offset_of!(jb_logging__collector_health_stats, os), 42usize);\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, os_version),\n            44usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__collector_health_stats, status),\n            46usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__bpf_log_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub module: u16,\n    pub shard: u16,\n    pub time_ns: u64,\n    pub _ref: u64,\n    pub version: u16,\n    pub cloud: u16,\n    pub env: u16,\n    pub role: u16,\n    pub az: u16,\n    pub node_id: u16,\n    pub kernel_version: u16,\n    pub client_type: u16,\n    pub hostname: u16,\n    pub os: u16,\n    pub os_version: u16,\n    pub filename: u16,\n    pub line: u16,\n    pub code: u16,\n    pub arg0: u16,\n    pub arg1: u16,\n}\n\nimpl jb_logging__bpf_log_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(642u16, true)\n    }\n}\n\nimpl Default for jb_logging__bpf_log_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const BPF_LOG_STATS_WIRE_SIZE: usize = 56;\n\n#[cfg(test)]\nmod bpf_log_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__bpf_log_stats>();\n        let align = align_of::<jb_logging__bpf_log_stats>();\n        let padded_raw_size = (BPF_LOG_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, _len), 2);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, module), 4usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, shard), 6usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, time_ns), 8usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, _ref), 16usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, version), 24usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, cloud), 26usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, env), 28usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, role), 30usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, az), 32usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, node_id), 34usize);\n        assert_eq!(\n            offset_of!(jb_logging__bpf_log_stats, kernel_version),\n            36usize\n        );\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, client_type), 38usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, hostname), 40usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, os), 42usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, os_version), 44usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, filename), 46usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, line), 48usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, code), 50usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, arg0), 52usize);\n        assert_eq!(offset_of!(jb_logging__bpf_log_stats, arg1), 54usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__server_stats {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub connection_counter: u64,\n    pub disconnect_counter: u64,\n    pub time_ns: u64,\n    pub _ref: u64,\n}\n\nimpl jb_logging__server_stats {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(643u16, true)\n    }\n}\n\nimpl Default for jb_logging__server_stats {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SERVER_STATS_WIRE_SIZE: usize = 40;\n\n#[cfg(test)]\nmod server_stats_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__server_stats>();\n        let align = align_of::<jb_logging__server_stats>();\n        let padded_raw_size = (SERVER_STATS_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__server_stats, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_logging__server_stats, _len), 2);\n        assert_eq!(\n            offset_of!(jb_logging__server_stats, connection_counter),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_logging__server_stats, disconnect_counter),\n            16usize\n        );\n        assert_eq!(offset_of!(jb_logging__server_stats, time_ns), 24usize);\n        assert_eq!(offset_of!(jb_logging__server_stats, _ref), 32usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_logging__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_logging__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_logging__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_logging__pulse>();\n        let align = align_of::<jb_logging__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_logging__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![\n        jb_logging__logger_start::metadata(),\n        jb_logging__logger_end::metadata(),\n        jb_logging__agent_lost_events::metadata(),\n        jb_logging__pod_not_found::metadata(),\n        jb_logging__cgroup_not_found::metadata(),\n        jb_logging__rewriting_private_to_public_ip_mapping::metadata(),\n        jb_logging__private_ip_in_private_to_public_ip_mapping::metadata(),\n        jb_logging__failed_to_insert_dns_record::metadata(),\n        jb_logging__tcp_socket_failed_getting_process_reference::metadata(),\n        jb_logging__udp_socket_failed_getting_process_reference::metadata(),\n        jb_logging__socket_address_already_assigned::metadata(),\n        jb_logging__ingest_decompression_error::metadata(),\n        jb_logging__ingest_processing_error::metadata(),\n        jb_logging__ingest_connection_error::metadata(),\n        jb_logging__agent_auth_success::metadata(),\n        jb_logging__agent_auth_failure::metadata(),\n        jb_logging__agent_attempting_auth_using_api_key::metadata(),\n        jb_logging__k8s_container_pod_not_found::metadata(),\n        jb_logging__agent_connect_success::metadata(),\n        jb_logging__core_stats_start::metadata(),\n        jb_logging__core_stats_end::metadata(),\n        jb_logging__span_utilization_stats::metadata(),\n        jb_logging__connection_message_stats::metadata(),\n        jb_logging__connection_message_error_stats::metadata(),\n        jb_logging__status_stats::metadata(),\n        jb_logging__rpc_receive_stats::metadata(),\n        jb_logging__rpc_write_stalls_stats::metadata(),\n        jb_logging__rpc_write_utilization_stats::metadata(),\n        jb_logging__code_timing_stats::metadata(),\n        jb_logging__agg_core_stats_start::metadata(),\n        jb_logging__agg_core_stats_end::metadata(),\n        jb_logging__agg_root_truncation_stats::metadata(),\n        jb_logging__agg_prometheus_bytes_stats::metadata(),\n        jb_logging__agg_otlp_grpc_stats::metadata(),\n        jb_logging__ingest_core_stats_start::metadata(),\n        jb_logging__ingest_core_stats_end::metadata(),\n        jb_logging__client_handle_pool_stats::metadata(),\n        jb_logging__agent_connection_message_stats::metadata(),\n        jb_logging__agent_connection_message_error_stats::metadata(),\n        jb_logging__connection_stats::metadata(),\n        jb_logging__collector_log_stats::metadata(),\n        jb_logging__entry_point_stats::metadata(),\n        jb_logging__collector_health_stats::metadata(),\n        jb_logging__bpf_log_stats::metadata(),\n        jb_logging__server_stats::metadata(),\n        jb_logging__pulse::metadata(),\n    ]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/Cargo.toml",
    "content": "[package]\nname = \"encoder_ebpf_net_matching\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for ebpf_net::matching\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_flow_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    addr1: u128,\n    port1: u16,\n    addr2: u128,\n    port2: u16,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__flow_start = jb_matching__flow_start {\n        _rpc_id: 421 as u16,\n        port1,\n        port2,\n        _ref,\n        addr1,\n        addr2,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_flow_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__flow_end = jb_matching__flow_end {\n        _rpc_id: 422 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_agent_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    id: JbBlob,\n    az: JbBlob,\n    env: JbBlob,\n    role: JbBlob,\n    ns: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 21 as u32;\n    __consumed = __consumed.saturating_add(id.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(env.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_id: &[u8] = unsafe { slice::from_raw_parts(id.buf as *const u8, id.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_env: &[u8] = unsafe { slice::from_raw_parts(env.buf as *const u8, env.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__agent_info = jb_matching__agent_info {\n        _rpc_id: 423 as u16,\n        _len: __consumed as u16,\n        id: (__sl_id.len() as u16),\n        az: (__sl_az.len() as u16),\n        _ref,\n        env: (__sl_env.len() as u16),\n        role: (__sl_role.len() as u16),\n        side,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 21 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 21 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_id.is_empty() {\n        let __len = __sl_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_id);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_env.is_empty() {\n        let __len = __sl_env.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_env);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_task_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    comm: JbBlob,\n    cgroup_name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(comm.len as u32);\n    __consumed = __consumed.saturating_add(cgroup_name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_comm: &[u8] =\n        unsafe { slice::from_raw_parts(comm.buf as *const u8, comm.len as usize) };\n    let __sl_cgroup_name: &[u8] =\n        unsafe { slice::from_raw_parts(cgroup_name.buf as *const u8, cgroup_name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__task_info = jb_matching__task_info {\n        _rpc_id: 424 as u16,\n        _len: __consumed as u16,\n        comm: (__sl_comm.len() as u16),\n        side,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_comm.is_empty() {\n        let __len = __sl_comm.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_comm);\n        __off += __len;\n    }\n    if !__sl_cgroup_name.is_empty() {\n        let __len = __sl_cgroup_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_cgroup_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_socket_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    local_addr: *const u8,\n    local_port: u16,\n    remote_addr: *const u8,\n    remote_port: u16,\n    is_connector: u8,\n    remote_dns_name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 50 as u32;\n    __consumed = __consumed.saturating_add(remote_dns_name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_remote_dns_name: &[u8] = unsafe {\n        slice::from_raw_parts(\n            remote_dns_name.buf as *const u8,\n            remote_dns_name.len as usize,\n        )\n    };\n    let __sl_local_addr: &[u8] = unsafe { slice::from_raw_parts(local_addr, 16) };\n    let __sl_remote_addr: &[u8] = unsafe { slice::from_raw_parts(remote_addr, 16) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__socket_info = jb_matching__socket_info {\n        _rpc_id: 425 as u16,\n        _len: __consumed as u16,\n        local_port,\n        remote_port,\n        _ref,\n        side,\n        local_addr: __sl_local_addr.try_into().unwrap(),\n        remote_addr: __sl_remote_addr.try_into().unwrap(),\n        is_connector,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 50 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 50 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_remote_dns_name.is_empty() {\n        let __len = __sl_remote_dns_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_remote_dns_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_k8s_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    pod_uid_suffix: *const u8,\n    pod_uid_hash: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 88 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_pod_uid_suffix: &[u8] = unsafe { slice::from_raw_parts(pod_uid_suffix, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__k8s_info = jb_matching__k8s_info {\n        _rpc_id: 426 as u16,\n        side,\n        pod_uid_suffix: __sl_pod_uid_suffix.try_into().unwrap(),\n        pod_uid_hash,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_tcp_update(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    is_rx: u8,\n    active_sockets: u32,\n    sum_retrans: u32,\n    sum_bytes: u64,\n    sum_srtt: u64,\n    sum_delivered: u64,\n    active_rtts: u32,\n    syn_timeouts: u32,\n    new_sockets: u32,\n    tcp_resets: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 60 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__tcp_update = jb_matching__tcp_update {\n        _rpc_id: 427 as u16,\n        side,\n        is_rx,\n        active_sockets,\n        sum_bytes,\n        sum_srtt,\n        sum_delivered,\n        _ref,\n        sum_retrans,\n        active_rtts,\n        syn_timeouts,\n        new_sockets,\n        tcp_resets,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 60 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 60 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_udp_update(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    is_rx: u8,\n    active_sockets: u32,\n    addr_changes: u32,\n    packets: u32,\n    bytes: u64,\n    drops: u32,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 36 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__udp_update = jb_matching__udp_update {\n        _rpc_id: 428 as u16,\n        side,\n        is_rx,\n        active_sockets,\n        bytes,\n        _ref,\n        addr_changes,\n        packets,\n        drops,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 36 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 36 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_http_update(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    client_server: u8,\n    active_sockets: u32,\n    sum_code_200: u32,\n    sum_code_400: u32,\n    sum_code_500: u32,\n    sum_code_other: u32,\n    sum_total_time_ns: u64,\n    sum_processing_time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__http_update = jb_matching__http_update {\n        _rpc_id: 429 as u16,\n        side,\n        client_server,\n        active_sockets,\n        sum_total_time_ns,\n        sum_processing_time_ns,\n        _ref,\n        sum_code_200,\n        sum_code_400,\n        sum_code_500,\n        sum_code_other,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_dns_update(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    client_server: u8,\n    active_sockets: u32,\n    requests_a: u32,\n    requests_aaaa: u32,\n    responses: u32,\n    timeouts: u32,\n    sum_total_time_ns: u64,\n    sum_processing_time_ns: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 48 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__dns_update = jb_matching__dns_update {\n        _rpc_id: 430 as u16,\n        side,\n        client_server,\n        active_sockets,\n        sum_total_time_ns,\n        sum_processing_time_ns,\n        _ref,\n        requests_a,\n        requests_aaaa,\n        responses,\n        timeouts,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 48 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 48 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_container_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    name: JbBlob,\n    pod: JbBlob,\n    role: JbBlob,\n    version: JbBlob,\n    ns: JbBlob,\n    node_type: u8,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 22 as u32;\n    __consumed = __consumed.saturating_add(name.len as u32);\n    __consumed = __consumed.saturating_add(pod.len as u32);\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n    let __sl_pod: &[u8] = unsafe { slice::from_raw_parts(pod.buf as *const u8, pod.len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__container_info = jb_matching__container_info {\n        _rpc_id: 434 as u16,\n        _len: __consumed as u16,\n        name: (__sl_name.len() as u16),\n        pod: (__sl_pod.len() as u16),\n        _ref,\n        role: (__sl_role.len() as u16),\n        version: (__sl_version.len() as u16),\n        side,\n        node_type,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 22 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 22 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n    if !__sl_pod.is_empty() {\n        let __len = __sl_pod.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod);\n        __off += __len;\n    }\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_service_info(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    side: u8,\n    name: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(name.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__service_info = jb_matching__service_info {\n        _rpc_id: 435 as u16,\n        _len: __consumed as u16,\n        side,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_aws_enrichment_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    ip: u128,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 32 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__aws_enrichment_start = jb_matching__aws_enrichment_start {\n        _rpc_id: 431 as u16,\n        _ref,\n        ip,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 32 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 32 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_aws_enrichment_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__aws_enrichment_end = jb_matching__aws_enrichment_end {\n        _rpc_id: 432 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_aws_enrichment(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    role: JbBlob,\n    az: JbBlob,\n    id: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 16 as u32;\n    __consumed = __consumed.saturating_add(role.len as u32);\n    __consumed = __consumed.saturating_add(az.len as u32);\n    __consumed = __consumed.saturating_add(id.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_role: &[u8] =\n        unsafe { slice::from_raw_parts(role.buf as *const u8, role.len as usize) };\n    let __sl_az: &[u8] = unsafe { slice::from_raw_parts(az.buf as *const u8, az.len as usize) };\n    let __sl_id: &[u8] = unsafe { slice::from_raw_parts(id.buf as *const u8, id.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__aws_enrichment = jb_matching__aws_enrichment {\n        _rpc_id: 433 as u16,\n        _len: __consumed as u16,\n        role: (__sl_role.len() as u16),\n        az: (__sl_az.len() as u16),\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_role.is_empty() {\n        let __len = __sl_role.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_role);\n        __off += __len;\n    }\n    if !__sl_az.is_empty() {\n        let __len = __sl_az.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_az);\n        __off += __len;\n    }\n    if !__sl_id.is_empty() {\n        let __len = __sl_id.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_id);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_k8s_pod_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    uid_suffix: *const u8,\n    uid_hash: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 88 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid_suffix: &[u8] = unsafe { slice::from_raw_parts(uid_suffix, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__k8s_pod_start = jb_matching__k8s_pod_start {\n        _rpc_id: 436 as u16,\n        uid_suffix: __sl_uid_suffix.try_into().unwrap(),\n        uid_hash,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_k8s_pod_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__k8s_pod_end = jb_matching__k8s_pod_end {\n        _rpc_id: 437 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_set_pod_detail(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    owner_name: JbBlob,\n    pod_name: JbBlob,\n    ns: JbBlob,\n    version: JbBlob,\n    owner_uid: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 20 as u32;\n    __consumed = __consumed.saturating_add(owner_name.len as u32);\n    __consumed = __consumed.saturating_add(pod_name.len as u32);\n    __consumed = __consumed.saturating_add(ns.len as u32);\n    __consumed = __consumed.saturating_add(version.len as u32);\n    __consumed = __consumed.saturating_add(owner_uid.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_owner_name: &[u8] =\n        unsafe { slice::from_raw_parts(owner_name.buf as *const u8, owner_name.len as usize) };\n    let __sl_pod_name: &[u8] =\n        unsafe { slice::from_raw_parts(pod_name.buf as *const u8, pod_name.len as usize) };\n    let __sl_ns: &[u8] = unsafe { slice::from_raw_parts(ns.buf as *const u8, ns.len as usize) };\n    let __sl_version: &[u8] =\n        unsafe { slice::from_raw_parts(version.buf as *const u8, version.len as usize) };\n    let __sl_owner_uid: &[u8] =\n        unsafe { slice::from_raw_parts(owner_uid.buf as *const u8, owner_uid.len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__set_pod_detail = jb_matching__set_pod_detail {\n        _rpc_id: 438 as u16,\n        _len: __consumed as u16,\n        owner_name: (__sl_owner_name.len() as u16),\n        pod_name: (__sl_pod_name.len() as u16),\n        _ref,\n        ns: (__sl_ns.len() as u16),\n        version: (__sl_version.len() as u16),\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 20 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 20 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_owner_name.is_empty() {\n        let __len = __sl_owner_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_name);\n        __off += __len;\n    }\n    if !__sl_pod_name.is_empty() {\n        let __len = __sl_pod_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_pod_name);\n        __off += __len;\n    }\n    if !__sl_ns.is_empty() {\n        let __len = __sl_ns.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_ns);\n        __off += __len;\n    }\n    if !__sl_version.is_empty() {\n        let __len = __sl_version.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_version);\n        __off += __len;\n    }\n    if !__sl_owner_uid.is_empty() {\n        let __len = __sl_owner_uid.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_owner_uid);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_k8s_container_start(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    uid_suffix: *const u8,\n    uid_hash: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 88 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_uid_suffix: &[u8] = unsafe { slice::from_raw_parts(uid_suffix, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__k8s_container_start = jb_matching__k8s_container_start {\n        _rpc_id: 439 as u16,\n        uid_suffix: __sl_uid_suffix.try_into().unwrap(),\n        uid_hash,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_k8s_container_end(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 16 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__k8s_container_end = jb_matching__k8s_container_end {\n        _rpc_id: 440 as u16,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 16 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 16 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_set_container_pod(\n    __dest: *mut u8,\n    __dest_len: u32,\n    __tstamp: u64,\n    _ref: u64,\n    pod_uid_suffix: *const u8,\n    pod_uid_hash: u64,\n    name: JbBlob,\n    image: JbBlob,\n) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let mut __consumed: u32 = 88 as u32;\n    __consumed = __consumed.saturating_add(name.len as u32);\n    __consumed = __consumed.saturating_add(image.len as u32);\n    assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n    let __sl_name: &[u8] =\n        unsafe { slice::from_raw_parts(name.buf as *const u8, name.len as usize) };\n    let __sl_image: &[u8] =\n        unsafe { slice::from_raw_parts(image.buf as *const u8, image.len as usize) };\n    let __sl_pod_uid_suffix: &[u8] = unsafe { slice::from_raw_parts(pod_uid_suffix, 64) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__set_container_pod = jb_matching__set_container_pod {\n        _rpc_id: 471 as u16,\n        _len: __consumed as u16,\n        name: (__sl_name.len() as u16),\n        pod_uid_suffix: __sl_pod_uid_suffix.try_into().unwrap(),\n        pod_uid_hash,\n        _ref,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 88 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 88 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n    let mut __off = __fixed_len;\n    if !__sl_name.is_empty() {\n        let __len = __sl_name.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_name);\n        __off += __len;\n    }\n    if !__sl_image.is_empty() {\n        let __len = __sl_image.len();\n        let __dst_seg = &mut __dst[__off..__off + __len];\n        __dst_seg.copy_from_slice(__sl_image);\n        __off += __len;\n    }\n}\n#[no_mangle]\npub extern \"C\" fn ebpf_net_matching_encode_pulse(__dest: *mut u8, __dest_len: u32, __tstamp: u64) {\n    assert!(!__dest.is_null(), \"dest must not be null\");\n\n    // Compute encoded length: fixed struct size + dynamic payload\n    let __consumed: u32 = 2 as u32;\n\n    let __total_len = (8_u32).saturating_add(__consumed) as usize;\n    assert!(\n        __total_len == __dest_len as usize,\n        \"dest len must exactly match computed encoded len\"\n    );\n\n    // Prepare destination and input slices up front\n    let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n    // Build fixed header as a stack struct using an initializer expression\n    let __wire: jb_matching__pulse = jb_matching__pulse {\n        _rpc_id: 65535 as u16,\n    };\n\n    // Copy timestamp and packed header (without struct tail padding)\n    let __fixed_len = 8usize + 2 as usize;\n    __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n    let __src_struct: &[u8] =\n        unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n    __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n    // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for ebpf_net::matching\n//\n// g_type: u8\n// g_size: 8\n// g_shift: 29\n// hash_shift: 24\n// hash_mask: 31\n// n_keys: 22\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const MATCHING_HASH_SIZE: u32 = 32u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 8] = [1, 2, 0, 0, 2, 0, 0, 1];\n\n#[inline]\n#[allow(dead_code)]\npub fn matching_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 29) as usize] as u32;\n    (k >> 24).wrapping_add(g) & 31u32\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n#[allow(dead_code)]\npub mod parsed_message;\n#[allow(dead_code)]\npub mod wire_messages;\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n    BufferTooSmall,\n    InvalidRpcId { got: u16 },\n    InvalidLength { len: u16 },\n    Utf8 { field: &'static str },\n}\n\n// Parsed struct for flow_start\npub struct flow_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub addr1: u128,\n    pub port1: u16,\n    pub addr2: u128,\n    pub port2: u16,\n}\n\nimpl flow_start {\n    pub const RPC_ID: u16 = 421u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let addr1 = u128::from_ne_bytes(body[16usize..16usize + 16].try_into().unwrap());\n        let port1 = u16::from_ne_bytes(body[2usize..2usize + 2].try_into().unwrap());\n        let addr2 = u128::from_ne_bytes(body[32usize..32usize + 16].try_into().unwrap());\n        let port2 = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            addr1: addr1,\n            port1: port1,\n            addr2: addr2,\n            port2: port2,\n        })\n    }\n}\n// Parsed struct for flow_end\npub struct flow_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl flow_end {\n    pub const RPC_ID: u16 = 422u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for agent_info\npub struct agent_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub id: ::std::string::String,\n    pub az: ::std::string::String,\n    pub env: ::std::string::String,\n    pub role: ::std::string::String,\n    pub ns: ::std::string::String,\n}\n\nimpl agent_info {\n    pub const RPC_ID: u16 = 423u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[20usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 21usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_id = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_id > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let id = if __l_id == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_id]).into_owned()\n        };\n        __off += __l_id;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_env = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_env > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let env = if __l_env == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_env]).into_owned()\n        };\n        __off += __l_env;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            id: id,\n            az: az,\n            env: env,\n            role: role,\n            ns: ns,\n        })\n    }\n}\n// Parsed struct for task_info\npub struct task_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub comm: ::std::string::String,\n    pub cgroup_name: ::std::string::String,\n}\n\nimpl task_info {\n    pub const RPC_ID: u16 = 424u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[6usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_comm = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_comm > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let comm = if __l_comm == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_comm]).into_owned()\n        };\n        __off += __l_comm;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let cgroup_name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            comm: comm,\n            cgroup_name: cgroup_name,\n        })\n    }\n}\n// Parsed struct for socket_info\npub struct socket_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub local_addr: [u8; 16],\n    pub local_port: u16,\n    pub remote_addr: [u8; 16],\n    pub remote_port: u16,\n    pub is_connector: u8,\n    pub remote_dns_name: ::std::string::String,\n}\n\nimpl socket_info {\n    pub const RPC_ID: u16 = 425u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[16usize];\n        let local_addr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 17usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let local_port = u16::from_ne_bytes(body[4usize..4usize + 2].try_into().unwrap());\n        let remote_addr = {\n            let mut tmp = [0u8; 16];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 16usize {\n                let off = 33usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let remote_port = u16::from_ne_bytes(body[6usize..6usize + 2].try_into().unwrap());\n        let is_connector = body[49usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 50usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let remote_dns_name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            local_addr: local_addr,\n            local_port: local_port,\n            remote_addr: remote_addr,\n            remote_port: remote_port,\n            is_connector: is_connector,\n            remote_dns_name: remote_dns_name,\n        })\n    }\n}\n// Parsed struct for k8s_info\npub struct k8s_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n}\n\nimpl k8s_info {\n    pub const RPC_ID: u16 = 426u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 88usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let side = body[2usize];\n        let pod_uid_suffix = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 3usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let pod_uid_hash = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            pod_uid_suffix: pod_uid_suffix,\n            pod_uid_hash: pod_uid_hash,\n        })\n    }\n}\n// Parsed struct for tcp_update\npub struct tcp_update {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub is_rx: u8,\n    pub active_sockets: u32,\n    pub sum_retrans: u32,\n    pub sum_bytes: u64,\n    pub sum_srtt: u64,\n    pub sum_delivered: u64,\n    pub active_rtts: u32,\n    pub syn_timeouts: u32,\n    pub new_sockets: u32,\n    pub tcp_resets: u32,\n}\n\nimpl tcp_update {\n    pub const RPC_ID: u16 = 427u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 60usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[32usize..32usize + 8].try_into().unwrap());\n        let side = body[2usize];\n        let is_rx = body[3usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sum_retrans = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let sum_bytes = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_srtt = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let sum_delivered = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let active_rtts = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let syn_timeouts = u32::from_ne_bytes(body[48usize..48usize + 4].try_into().unwrap());\n        let new_sockets = u32::from_ne_bytes(body[52usize..52usize + 4].try_into().unwrap());\n        let tcp_resets = u32::from_ne_bytes(body[56usize..56usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            is_rx: is_rx,\n            active_sockets: active_sockets,\n            sum_retrans: sum_retrans,\n            sum_bytes: sum_bytes,\n            sum_srtt: sum_srtt,\n            sum_delivered: sum_delivered,\n            active_rtts: active_rtts,\n            syn_timeouts: syn_timeouts,\n            new_sockets: new_sockets,\n            tcp_resets: tcp_resets,\n        })\n    }\n}\n// Parsed struct for udp_update\npub struct udp_update {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub is_rx: u8,\n    pub active_sockets: u32,\n    pub addr_changes: u32,\n    pub packets: u32,\n    pub bytes: u64,\n    pub drops: u32,\n}\n\nimpl udp_update {\n    pub const RPC_ID: u16 = 428u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 36usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n        let side = body[2usize];\n        let is_rx = body[3usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let addr_changes = u32::from_ne_bytes(body[24usize..24usize + 4].try_into().unwrap());\n        let packets = u32::from_ne_bytes(body[28usize..28usize + 4].try_into().unwrap());\n        let bytes = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let drops = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            is_rx: is_rx,\n            active_sockets: active_sockets,\n            addr_changes: addr_changes,\n            packets: packets,\n            bytes: bytes,\n            drops: drops,\n        })\n    }\n}\n// Parsed struct for http_update\npub struct http_update {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub client_server: u8,\n    pub active_sockets: u32,\n    pub sum_code_200: u32,\n    pub sum_code_400: u32,\n    pub sum_code_500: u32,\n    pub sum_code_other: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n}\n\nimpl http_update {\n    pub const RPC_ID: u16 = 429u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let side = body[2usize];\n        let client_server = body[3usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let sum_code_200 = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let sum_code_400 = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let sum_code_500 = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let sum_code_other = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let sum_total_time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_processing_time_ns =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            client_server: client_server,\n            active_sockets: active_sockets,\n            sum_code_200: sum_code_200,\n            sum_code_400: sum_code_400,\n            sum_code_500: sum_code_500,\n            sum_code_other: sum_code_other,\n            sum_total_time_ns: sum_total_time_ns,\n            sum_processing_time_ns: sum_processing_time_ns,\n        })\n    }\n}\n// Parsed struct for dns_update\npub struct dns_update {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub client_server: u8,\n    pub active_sockets: u32,\n    pub requests_a: u32,\n    pub requests_aaaa: u32,\n    pub responses: u32,\n    pub timeouts: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n}\n\nimpl dns_update {\n    pub const RPC_ID: u16 = 430u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 48usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[24usize..24usize + 8].try_into().unwrap());\n        let side = body[2usize];\n        let client_server = body[3usize];\n        let active_sockets = u32::from_ne_bytes(body[4usize..4usize + 4].try_into().unwrap());\n        let requests_a = u32::from_ne_bytes(body[32usize..32usize + 4].try_into().unwrap());\n        let requests_aaaa = u32::from_ne_bytes(body[36usize..36usize + 4].try_into().unwrap());\n        let responses = u32::from_ne_bytes(body[40usize..40usize + 4].try_into().unwrap());\n        let timeouts = u32::from_ne_bytes(body[44usize..44usize + 4].try_into().unwrap());\n        let sum_total_time_ns = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let sum_processing_time_ns =\n            u64::from_ne_bytes(body[16usize..16usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            client_server: client_server,\n            active_sockets: active_sockets,\n            requests_a: requests_a,\n            requests_aaaa: requests_aaaa,\n            responses: responses,\n            timeouts: timeouts,\n            sum_total_time_ns: sum_total_time_ns,\n            sum_processing_time_ns: sum_processing_time_ns,\n        })\n    }\n}\n// Parsed struct for container_info\npub struct container_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub name: ::std::string::String,\n    pub pod: ::std::string::String,\n    pub role: ::std::string::String,\n    pub version: ::std::string::String,\n    pub ns: ::std::string::String,\n    pub node_type: u8,\n}\n\nimpl container_info {\n    pub const RPC_ID: u16 = 434u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[20usize];\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        let node_type = body[21usize];\n\n        // Decode dynamic payload strings\n        let mut __off = 22usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __l_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_name]).into_owned()\n        };\n        __off += __l_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_pod = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod = if __l_pod == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod]).into_owned()\n        };\n        __off += __l_pod;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            name: name,\n            pod: pod,\n            role: role,\n            version: version,\n            ns: ns,\n            node_type: node_type,\n        })\n    }\n}\n// Parsed struct for service_info\npub struct service_info {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub name: ::std::string::String,\n}\n\nimpl service_info {\n    pub const RPC_ID: u16 = 435u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let side = body[4usize];\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            side: side,\n            name: name,\n        })\n    }\n}\n// Parsed struct for aws_enrichment_start\npub struct aws_enrichment_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip: u128,\n}\n\nimpl aws_enrichment_start {\n    pub const RPC_ID: u16 = 431u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 32usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        let ip = u128::from_ne_bytes(body[16usize..16usize + 16].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            ip: ip,\n        })\n    }\n}\n// Parsed struct for aws_enrichment_end\npub struct aws_enrichment_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl aws_enrichment_end {\n    pub const RPC_ID: u16 = 432u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for aws_enrichment\npub struct aws_enrichment {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub role: ::std::string::String,\n    pub az: ::std::string::String,\n    pub id: ::std::string::String,\n}\n\nimpl aws_enrichment {\n    pub const RPC_ID: u16 = 433u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 16usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_role = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_role > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let role = if __l_role == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_role]).into_owned()\n        };\n        __off += __l_role;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_az = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_az > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let az = if __l_az == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_az]).into_owned()\n        };\n        __off += __l_az;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let id = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            role: role,\n            az: az,\n            id: id,\n        })\n    }\n}\n// Parsed struct for k8s_pod_start\npub struct k8s_pod_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub uid_suffix: [u8; 64],\n    pub uid_hash: u64,\n}\n\nimpl k8s_pod_start {\n    pub const RPC_ID: u16 = 436u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 88usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let uid_suffix = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let uid_hash = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            uid_suffix: uid_suffix,\n            uid_hash: uid_hash,\n        })\n    }\n}\n// Parsed struct for k8s_pod_end\npub struct k8s_pod_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl k8s_pod_end {\n    pub const RPC_ID: u16 = 437u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for set_pod_detail\npub struct set_pod_detail {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub owner_name: ::std::string::String,\n    pub pod_name: ::std::string::String,\n    pub ns: ::std::string::String,\n    pub version: ::std::string::String,\n    pub owner_uid: ::std::string::String,\n}\n\nimpl set_pod_detail {\n    pub const RPC_ID: u16 = 438u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 20usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_owner_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_owner_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_name = if __l_owner_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_owner_name])\n                .into_owned()\n        };\n        __off += __l_owner_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[6usize..6usize + 2]);\n        let __l_pod_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_pod_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let pod_name = if __l_pod_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_pod_name]).into_owned()\n        };\n        __off += __l_pod_name;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[16usize..16usize + 2]);\n        let __l_ns = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_ns > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let ns = if __l_ns == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_ns]).into_owned()\n        };\n        __off += __l_ns;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[18usize..18usize + 2]);\n        let __l_version = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_version > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let version = if __l_version == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_version]).into_owned()\n        };\n        __off += __l_version;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let owner_uid = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            owner_name: owner_name,\n            pod_name: pod_name,\n            ns: ns,\n            version: version,\n            owner_uid: owner_uid,\n        })\n    }\n}\n// Parsed struct for k8s_container_start\npub struct k8s_container_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub uid_suffix: [u8; 64],\n    pub uid_hash: u64,\n}\n\nimpl k8s_container_start {\n    pub const RPC_ID: u16 = 439u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 88usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let uid_suffix = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 2usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let uid_hash = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            uid_suffix: uid_suffix,\n            uid_hash: uid_hash,\n        })\n    }\n}\n// Parsed struct for k8s_container_end\npub struct k8s_container_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl k8s_container_end {\n    pub const RPC_ID: u16 = 440u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 16usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[8usize..8usize + 8].try_into().unwrap());\n\n        // Decode dynamic payload strings\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n        })\n    }\n}\n// Parsed struct for set_container_pod\npub struct set_container_pod {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n    pub name: ::std::string::String,\n    pub image: ::std::string::String,\n}\n\nimpl set_container_pod {\n    pub const RPC_ID: u16 = 471u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 4 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[2..4]);\n        let __len = u16::from_ne_bytes(b2);\n        if __len < 4 {\n            return Err(DecodeError::InvalidLength { len: __len });\n        }\n        if body.len() < __len as usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n        let _ref = u64::from_ne_bytes(body[80usize..80usize + 8].try_into().unwrap());\n        let pod_uid_suffix = {\n            let mut tmp = [0u8; 64];\n            let es = 1usize;\n            let mut i = 0usize;\n            while i < 64usize {\n                let off = 6usize + i * es;\n                tmp[i] = body[off];\n                i += 1;\n            }\n            tmp\n        };\n        let pod_uid_hash = u64::from_ne_bytes(body[72usize..72usize + 8].try_into().unwrap());\n        // dynamic string; decode later from payload\n        // dynamic string; decode later from payload\n\n        // Decode dynamic payload strings\n        let mut __off = 88usize;\n        // length from header\n        let mut __b = [0u8; 2];\n        __b.copy_from_slice(&body[4usize..4usize + 2]);\n        let __l_name = u16::from_ne_bytes(__b) as usize;\n        if __off + __l_name > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let name = if __l_name == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __l_name]).into_owned()\n        };\n        __off += __l_name;\n        let __tail = (__len as usize).saturating_sub(__off);\n        if __off + __tail > body.len() {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let image = if __tail == 0 {\n            ::std::string::String::new()\n        } else {\n            ::std::string::String::from_utf8_lossy(&body[__off..__off + __tail]).into_owned()\n        };\n\n        Ok(Self {\n            _rpc_id: rpc,\n            _ref: _ref,\n            pod_uid_suffix: pod_uid_suffix,\n            pod_uid_hash: pod_uid_hash,\n            name: name,\n            image: image,\n        })\n    }\n}\n// Parsed struct for pulse\npub struct pulse {\n    pub _rpc_id: u16,\n}\n\nimpl pulse {\n    pub const RPC_ID: u16 = 65535u16;\n\n    #[inline]\n    pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n        // Require rpc_id\n        if body.len() < 2 {\n            return Err(DecodeError::BufferTooSmall);\n        }\n        let mut b2 = [0u8; 2];\n        b2.copy_from_slice(&body[0..2]);\n        let rpc = u16::from_ne_bytes(b2);\n        if rpc != Self::RPC_ID {\n            return Err(DecodeError::InvalidRpcId { got: rpc });\n        }\n\n        if body.len() < 2usize {\n            return Err(DecodeError::BufferTooSmall);\n        }\n\n        // Decode fixed header fields\n\n        // Decode dynamic payload strings\n\n        Ok(Self { _rpc_id: rpc })\n    }\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/matching/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__flow_start {\n    pub _rpc_id: u16,\n    pub port1: u16,\n    pub port2: u16,\n    pub _ref: u64,\n    pub addr1: u128,\n    pub addr2: u128,\n}\n\nimpl jb_matching__flow_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(421u16, 48, true)\n    }\n}\n\nimpl Default for jb_matching__flow_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const FLOW_START_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod flow_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__flow_start>();\n        let align = align_of::<jb_matching__flow_start>();\n        let padded_raw_size = (FLOW_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__flow_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__flow_start, port1), 2usize);\n        assert_eq!(offset_of!(jb_matching__flow_start, port2), 4usize);\n        assert_eq!(offset_of!(jb_matching__flow_start, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__flow_start, addr1), 16usize);\n        assert_eq!(offset_of!(jb_matching__flow_start, addr2), 32usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__flow_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_matching__flow_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(422u16, 16, true)\n    }\n}\n\nimpl Default for jb_matching__flow_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const FLOW_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod flow_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__flow_end>();\n        let align = align_of::<jb_matching__flow_end>();\n        let padded_raw_size = (FLOW_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__flow_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__flow_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__agent_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub id: u16,\n    pub az: u16,\n    pub _ref: u64,\n    pub env: u16,\n    pub role: u16,\n    pub side: u8,\n}\n\nimpl jb_matching__agent_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(423u16, true)\n    }\n}\n\nimpl Default for jb_matching__agent_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AGENT_INFO_WIRE_SIZE: usize = 21;\n\n#[cfg(test)]\nmod agent_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__agent_info>();\n        let align = align_of::<jb_matching__agent_info>();\n        let padded_raw_size = (AGENT_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__agent_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__agent_info, _len), 2);\n        assert_eq!(offset_of!(jb_matching__agent_info, id), 4usize);\n        assert_eq!(offset_of!(jb_matching__agent_info, az), 6usize);\n        assert_eq!(offset_of!(jb_matching__agent_info, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__agent_info, env), 16usize);\n        assert_eq!(offset_of!(jb_matching__agent_info, role), 18usize);\n        assert_eq!(offset_of!(jb_matching__agent_info, side), 20usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__task_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub comm: u16,\n    pub side: u8,\n    pub _ref: u64,\n}\n\nimpl jb_matching__task_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(424u16, true)\n    }\n}\n\nimpl Default for jb_matching__task_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TASK_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod task_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__task_info>();\n        let align = align_of::<jb_matching__task_info>();\n        let padded_raw_size = (TASK_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__task_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__task_info, _len), 2);\n        assert_eq!(offset_of!(jb_matching__task_info, comm), 4usize);\n        assert_eq!(offset_of!(jb_matching__task_info, side), 6usize);\n        assert_eq!(offset_of!(jb_matching__task_info, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__socket_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub local_port: u16,\n    pub remote_port: u16,\n    pub _ref: u64,\n    pub side: u8,\n    pub local_addr: [u8; 16],\n    pub remote_addr: [u8; 16],\n    pub is_connector: u8,\n}\n\nimpl jb_matching__socket_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(425u16, true)\n    }\n}\n\nimpl Default for jb_matching__socket_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SOCKET_INFO_WIRE_SIZE: usize = 50;\n\n#[cfg(test)]\nmod socket_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__socket_info>();\n        let align = align_of::<jb_matching__socket_info>();\n        let padded_raw_size = (SOCKET_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__socket_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__socket_info, _len), 2);\n        assert_eq!(offset_of!(jb_matching__socket_info, local_port), 4usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, remote_port), 6usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, side), 16usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, local_addr), 17usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, remote_addr), 33usize);\n        assert_eq!(offset_of!(jb_matching__socket_info, is_connector), 49usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__k8s_info {\n    pub _rpc_id: u16,\n    pub side: u8,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n    pub _ref: u64,\n}\n\nimpl jb_matching__k8s_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(426u16, 88, true)\n    }\n}\n\nimpl Default for jb_matching__k8s_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_INFO_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod k8s_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__k8s_info>();\n        let align = align_of::<jb_matching__k8s_info>();\n        let padded_raw_size = (K8S_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__k8s_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__k8s_info, side), 2usize);\n        assert_eq!(offset_of!(jb_matching__k8s_info, pod_uid_suffix), 3usize);\n        assert_eq!(offset_of!(jb_matching__k8s_info, pod_uid_hash), 72usize);\n        assert_eq!(offset_of!(jb_matching__k8s_info, _ref), 80usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__tcp_update {\n    pub _rpc_id: u16,\n    pub side: u8,\n    pub is_rx: u8,\n    pub active_sockets: u32,\n    pub sum_bytes: u64,\n    pub sum_srtt: u64,\n    pub sum_delivered: u64,\n    pub _ref: u64,\n    pub sum_retrans: u32,\n    pub active_rtts: u32,\n    pub syn_timeouts: u32,\n    pub new_sockets: u32,\n    pub tcp_resets: u32,\n}\n\nimpl jb_matching__tcp_update {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(427u16, 60, true)\n    }\n}\n\nimpl Default for jb_matching__tcp_update {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const TCP_UPDATE_WIRE_SIZE: usize = 60;\n\n#[cfg(test)]\nmod tcp_update_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__tcp_update>();\n        let align = align_of::<jb_matching__tcp_update>();\n        let padded_raw_size = (TCP_UPDATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__tcp_update, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__tcp_update, side), 2usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, is_rx), 3usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, active_sockets), 4usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, sum_bytes), 8usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, sum_srtt), 16usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, sum_delivered), 24usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, _ref), 32usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, sum_retrans), 40usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, active_rtts), 44usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, syn_timeouts), 48usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, new_sockets), 52usize);\n        assert_eq!(offset_of!(jb_matching__tcp_update, tcp_resets), 56usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__udp_update {\n    pub _rpc_id: u16,\n    pub side: u8,\n    pub is_rx: u8,\n    pub active_sockets: u32,\n    pub bytes: u64,\n    pub _ref: u64,\n    pub addr_changes: u32,\n    pub packets: u32,\n    pub drops: u32,\n}\n\nimpl jb_matching__udp_update {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(428u16, 36, true)\n    }\n}\n\nimpl Default for jb_matching__udp_update {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const UDP_UPDATE_WIRE_SIZE: usize = 36;\n\n#[cfg(test)]\nmod udp_update_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__udp_update>();\n        let align = align_of::<jb_matching__udp_update>();\n        let padded_raw_size = (UDP_UPDATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__udp_update, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__udp_update, side), 2usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, is_rx), 3usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, active_sockets), 4usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, bytes), 8usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, _ref), 16usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, addr_changes), 24usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, packets), 28usize);\n        assert_eq!(offset_of!(jb_matching__udp_update, drops), 32usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__http_update {\n    pub _rpc_id: u16,\n    pub side: u8,\n    pub client_server: u8,\n    pub active_sockets: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub _ref: u64,\n    pub sum_code_200: u32,\n    pub sum_code_400: u32,\n    pub sum_code_500: u32,\n    pub sum_code_other: u32,\n}\n\nimpl jb_matching__http_update {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(429u16, 48, true)\n    }\n}\n\nimpl Default for jb_matching__http_update {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const HTTP_UPDATE_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod http_update_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__http_update>();\n        let align = align_of::<jb_matching__http_update>();\n        let padded_raw_size = (HTTP_UPDATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__http_update, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__http_update, side), 2usize);\n        assert_eq!(offset_of!(jb_matching__http_update, client_server), 3usize);\n        assert_eq!(offset_of!(jb_matching__http_update, active_sockets), 4usize);\n        assert_eq!(\n            offset_of!(jb_matching__http_update, sum_total_time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_matching__http_update, sum_processing_time_ns),\n            16usize\n        );\n        assert_eq!(offset_of!(jb_matching__http_update, _ref), 24usize);\n        assert_eq!(offset_of!(jb_matching__http_update, sum_code_200), 32usize);\n        assert_eq!(offset_of!(jb_matching__http_update, sum_code_400), 36usize);\n        assert_eq!(offset_of!(jb_matching__http_update, sum_code_500), 40usize);\n        assert_eq!(\n            offset_of!(jb_matching__http_update, sum_code_other),\n            44usize\n        );\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__dns_update {\n    pub _rpc_id: u16,\n    pub side: u8,\n    pub client_server: u8,\n    pub active_sockets: u32,\n    pub sum_total_time_ns: u64,\n    pub sum_processing_time_ns: u64,\n    pub _ref: u64,\n    pub requests_a: u32,\n    pub requests_aaaa: u32,\n    pub responses: u32,\n    pub timeouts: u32,\n}\n\nimpl jb_matching__dns_update {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(430u16, 48, true)\n    }\n}\n\nimpl Default for jb_matching__dns_update {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const DNS_UPDATE_WIRE_SIZE: usize = 48;\n\n#[cfg(test)]\nmod dns_update_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__dns_update>();\n        let align = align_of::<jb_matching__dns_update>();\n        let padded_raw_size = (DNS_UPDATE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__dns_update, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__dns_update, side), 2usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, client_server), 3usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, active_sockets), 4usize);\n        assert_eq!(\n            offset_of!(jb_matching__dns_update, sum_total_time_ns),\n            8usize\n        );\n        assert_eq!(\n            offset_of!(jb_matching__dns_update, sum_processing_time_ns),\n            16usize\n        );\n        assert_eq!(offset_of!(jb_matching__dns_update, _ref), 24usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, requests_a), 32usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, requests_aaaa), 36usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, responses), 40usize);\n        assert_eq!(offset_of!(jb_matching__dns_update, timeouts), 44usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__container_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub name: u16,\n    pub pod: u16,\n    pub _ref: u64,\n    pub role: u16,\n    pub version: u16,\n    pub side: u8,\n    pub node_type: u8,\n}\n\nimpl jb_matching__container_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(434u16, true)\n    }\n}\n\nimpl Default for jb_matching__container_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const CONTAINER_INFO_WIRE_SIZE: usize = 22;\n\n#[cfg(test)]\nmod container_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__container_info>();\n        let align = align_of::<jb_matching__container_info>();\n        let padded_raw_size = (CONTAINER_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__container_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__container_info, _len), 2);\n        assert_eq!(offset_of!(jb_matching__container_info, name), 4usize);\n        assert_eq!(offset_of!(jb_matching__container_info, pod), 6usize);\n        assert_eq!(offset_of!(jb_matching__container_info, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__container_info, role), 16usize);\n        assert_eq!(offset_of!(jb_matching__container_info, version), 18usize);\n        assert_eq!(offset_of!(jb_matching__container_info, side), 20usize);\n        assert_eq!(offset_of!(jb_matching__container_info, node_type), 21usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__service_info {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub side: u8,\n    pub _ref: u64,\n}\n\nimpl jb_matching__service_info {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(435u16, true)\n    }\n}\n\nimpl Default for jb_matching__service_info {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SERVICE_INFO_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod service_info_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__service_info>();\n        let align = align_of::<jb_matching__service_info>();\n        let padded_raw_size = (SERVICE_INFO_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__service_info, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__service_info, _len), 2);\n        assert_eq!(offset_of!(jb_matching__service_info, side), 4usize);\n        assert_eq!(offset_of!(jb_matching__service_info, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__aws_enrichment_start {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n    pub ip: u128,\n}\n\nimpl jb_matching__aws_enrichment_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(431u16, 32, true)\n    }\n}\n\nimpl Default for jb_matching__aws_enrichment_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AWS_ENRICHMENT_START_WIRE_SIZE: usize = 32;\n\n#[cfg(test)]\nmod aws_enrichment_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__aws_enrichment_start>();\n        let align = align_of::<jb_matching__aws_enrichment_start>();\n        let padded_raw_size = (AWS_ENRICHMENT_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__aws_enrichment_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment_start, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment_start, ip), 16usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__aws_enrichment_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_matching__aws_enrichment_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(432u16, 16, true)\n    }\n}\n\nimpl Default for jb_matching__aws_enrichment_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AWS_ENRICHMENT_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod aws_enrichment_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__aws_enrichment_end>();\n        let align = align_of::<jb_matching__aws_enrichment_end>();\n        let padded_raw_size = (AWS_ENRICHMENT_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__aws_enrichment_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__aws_enrichment {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub role: u16,\n    pub az: u16,\n    pub _ref: u64,\n}\n\nimpl jb_matching__aws_enrichment {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(433u16, true)\n    }\n}\n\nimpl Default for jb_matching__aws_enrichment {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const AWS_ENRICHMENT_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod aws_enrichment_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__aws_enrichment>();\n        let align = align_of::<jb_matching__aws_enrichment>();\n        let padded_raw_size = (AWS_ENRICHMENT_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__aws_enrichment, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment, _len), 2);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment, role), 4usize);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment, az), 6usize);\n        assert_eq!(offset_of!(jb_matching__aws_enrichment, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__k8s_pod_start {\n    pub _rpc_id: u16,\n    pub uid_suffix: [u8; 64],\n    pub uid_hash: u64,\n    pub _ref: u64,\n}\n\nimpl jb_matching__k8s_pod_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(436u16, 88, true)\n    }\n}\n\nimpl Default for jb_matching__k8s_pod_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_POD_START_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod k8s_pod_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__k8s_pod_start>();\n        let align = align_of::<jb_matching__k8s_pod_start>();\n        let padded_raw_size = (K8S_POD_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__k8s_pod_start, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__k8s_pod_start, uid_suffix), 2usize);\n        assert_eq!(offset_of!(jb_matching__k8s_pod_start, uid_hash), 72usize);\n        assert_eq!(offset_of!(jb_matching__k8s_pod_start, _ref), 80usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__k8s_pod_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_matching__k8s_pod_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(437u16, 16, true)\n    }\n}\n\nimpl Default for jb_matching__k8s_pod_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_POD_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod k8s_pod_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__k8s_pod_end>();\n        let align = align_of::<jb_matching__k8s_pod_end>();\n        let padded_raw_size = (K8S_POD_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__k8s_pod_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__k8s_pod_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__set_pod_detail {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub owner_name: u16,\n    pub pod_name: u16,\n    pub _ref: u64,\n    pub ns: u16,\n    pub version: u16,\n}\n\nimpl jb_matching__set_pod_detail {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(438u16, true)\n    }\n}\n\nimpl Default for jb_matching__set_pod_detail {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_POD_DETAIL_WIRE_SIZE: usize = 20;\n\n#[cfg(test)]\nmod set_pod_detail_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__set_pod_detail>();\n        let align = align_of::<jb_matching__set_pod_detail>();\n        let padded_raw_size = (SET_POD_DETAIL_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, _len), 2);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, owner_name), 4usize);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, pod_name), 6usize);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, _ref), 8usize);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, ns), 16usize);\n        assert_eq!(offset_of!(jb_matching__set_pod_detail, version), 18usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__k8s_container_start {\n    pub _rpc_id: u16,\n    pub uid_suffix: [u8; 64],\n    pub uid_hash: u64,\n    pub _ref: u64,\n}\n\nimpl jb_matching__k8s_container_start {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(439u16, 88, true)\n    }\n}\n\nimpl Default for jb_matching__k8s_container_start {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_CONTAINER_START_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod k8s_container_start_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__k8s_container_start>();\n        let align = align_of::<jb_matching__k8s_container_start>();\n        let padded_raw_size = (K8S_CONTAINER_START_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__k8s_container_start, _rpc_id), 0);\n        assert_eq!(\n            offset_of!(jb_matching__k8s_container_start, uid_suffix),\n            2usize\n        );\n        assert_eq!(\n            offset_of!(jb_matching__k8s_container_start, uid_hash),\n            72usize\n        );\n        assert_eq!(offset_of!(jb_matching__k8s_container_start, _ref), 80usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__k8s_container_end {\n    pub _rpc_id: u16,\n    pub _ref: u64,\n}\n\nimpl jb_matching__k8s_container_end {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(440u16, 16, true)\n    }\n}\n\nimpl Default for jb_matching__k8s_container_end {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const K8S_CONTAINER_END_WIRE_SIZE: usize = 16;\n\n#[cfg(test)]\nmod k8s_container_end_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__k8s_container_end>();\n        let align = align_of::<jb_matching__k8s_container_end>();\n        let padded_raw_size = (K8S_CONTAINER_END_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__k8s_container_end, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__k8s_container_end, _ref), 8usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__set_container_pod {\n    pub _rpc_id: u16,\n    pub _len: u16,\n    pub name: u16,\n    pub pod_uid_suffix: [u8; 64],\n    pub pod_uid_hash: u64,\n    pub _ref: u64,\n}\n\nimpl jb_matching__set_container_pod {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_dynamic(471u16, true)\n    }\n}\n\nimpl Default for jb_matching__set_container_pod {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const SET_CONTAINER_POD_WIRE_SIZE: usize = 88;\n\n#[cfg(test)]\nmod set_container_pod_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__set_container_pod>();\n        let align = align_of::<jb_matching__set_container_pod>();\n        let padded_raw_size = (SET_CONTAINER_POD_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__set_container_pod, _rpc_id), 0);\n        assert_eq!(offset_of!(jb_matching__set_container_pod, _len), 2);\n        assert_eq!(offset_of!(jb_matching__set_container_pod, name), 4usize);\n        assert_eq!(\n            offset_of!(jb_matching__set_container_pod, pod_uid_suffix),\n            6usize\n        );\n        assert_eq!(\n            offset_of!(jb_matching__set_container_pod, pod_uid_hash),\n            72usize\n        );\n        assert_eq!(offset_of!(jb_matching__set_container_pod, _ref), 80usize);\n    }\n}\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct jb_matching__pulse {\n    pub _rpc_id: u16,\n}\n\nimpl jb_matching__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n        render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n}\n\nimpl Default for jb_matching__pulse {\n    #[inline]\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\npub const PULSE_WIRE_SIZE: usize = 2;\n\n#[cfg(test)]\nmod pulse_layout_tests {\n    use super::*;\n    use core::mem::{align_of, offset_of};\n    #[test]\n    fn struct_size() {\n        let size = size_of::<jb_matching__pulse>();\n        let align = align_of::<jb_matching__pulse>();\n        let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n        assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n        assert_eq!(offset_of!(jb_matching__pulse, _rpc_id), 0);\n    }\n}\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n    ::std::vec![\n        jb_matching__flow_start::metadata(),\n        jb_matching__flow_end::metadata(),\n        jb_matching__agent_info::metadata(),\n        jb_matching__task_info::metadata(),\n        jb_matching__socket_info::metadata(),\n        jb_matching__k8s_info::metadata(),\n        jb_matching__tcp_update::metadata(),\n        jb_matching__udp_update::metadata(),\n        jb_matching__http_update::metadata(),\n        jb_matching__dns_update::metadata(),\n        jb_matching__container_info::metadata(),\n        jb_matching__service_info::metadata(),\n        jb_matching__aws_enrichment_start::metadata(),\n        jb_matching__aws_enrichment_end::metadata(),\n        jb_matching__aws_enrichment::metadata(),\n        jb_matching__k8s_pod_start::metadata(),\n        jb_matching__k8s_pod_end::metadata(),\n        jb_matching__set_pod_detail::metadata(),\n        jb_matching__k8s_container_start::metadata(),\n        jb_matching__k8s_container_end::metadata(),\n        jb_matching__set_container_pod::metadata(),\n        jb_matching__pulse::metadata(),\n    ]\n}\n"
  },
  {
    "path": "crates/render/ebpf_net/src/lib.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_extern_crates)]\nextern crate encoder_ebpf_net_agent_internal as _crate_agent_internal;\nextern crate encoder_ebpf_net_aggregation as _crate_aggregation;\nextern crate encoder_ebpf_net_cloud_collector as _crate_cloud_collector;\nextern crate encoder_ebpf_net_ingest as _crate_ingest;\nextern crate encoder_ebpf_net_kernel_collector as _crate_kernel_collector;\nextern crate encoder_ebpf_net_logging as _crate_logging;\nextern crate encoder_ebpf_net_matching as _crate_matching;\n#[no_mangle]\npub extern \"C\" fn __encoder_ebpf_net_bundle_marker() {}\n"
  },
  {
    "path": "crates/render/test/Cargo.toml",
    "content": "[package]\nname = \"encoder_test_all\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Aggregator remains a staticlib for C++ consumers; Rust binaries\n# link per-app crates directly via rlib dependencies.\ncrate-type = [\"staticlib\"]\n\n[dependencies]\nencoder_test_app1 = { path = \"app1\" }\n"
  },
  {
    "path": "crates/render/test/app1/Cargo.toml",
    "content": "[package]\nname = \"encoder_test_app1\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Build as both rlib and staticlib\n# - rlib: lets Rust binaries depend on this crate via Cargo and expose\n#         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n# - staticlib: keeps support for direct C/C++ linking where needed.\ncrate-type = [\"rlib\", \"staticlib\"]\n\n[dependencies]\nrender_parser = { workspace = true }\n"
  },
  {
    "path": "crates/render/test/app1/src/encoder.rs",
    "content": "// Auto-generated by Render: Rust FFI for test::app1\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(unused_variables)]\n\n#[allow(unused_imports)]\nuse crate::JbBlob;\n\nuse crate::wire_messages::*;\n\nuse core::slice;\n\n#[no_mangle]\npub extern \"C\" fn test_app1_encode_pulse(\n  __dest: *mut u8,\n  __dest_len: u32,\n  __tstamp: u64) {\n  assert!(!__dest.is_null(), \"dest must not be null\");\n\n  // Compute encoded length: fixed struct size + dynamic payload\n  let __consumed: u32 = 2 as u32;\n\n  let __total_len = (8_u32).saturating_add(__consumed) as usize;\n  assert!(\n    __total_len == __dest_len as usize,\n    \"dest len must exactly match computed encoded len\"\n  );\n\n  // Prepare destination and input slices up front\n  let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n\n  // Build fixed header as a stack struct using an initializer expression\n  let __wire: jb_app1__pulse = jb_app1__pulse {\n    _rpc_id: 65535 as u16,\n  };\n\n  // Copy timestamp and packed header (without struct tail padding)\n  let __fixed_len = 8usize + 2 as usize;\n  __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n  let __src_struct: &[u8] = unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, 2 as usize) };\n  __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n  // Append dynamic payloads sequentially\n}\n"
  },
  {
    "path": "crates/render/test/app1/src/hash.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n// Perfect hash for RPC IDs for test::app1\n//\n// g_type: u8\n// g_size: 2\n// g_shift: 31\n// hash_shift: 29\n// hash_mask: 3\n// n_keys: 1\n// multiplier: 2654435761\n// hash_seed: 0\n\n#[allow(dead_code)]\npub const APP1_HASH_SIZE: u32 = 4u32;\n\n#[allow(dead_code)]\npub static G_ARRAY: [u8; 2] = [\n    0,2\n];\n\n#[inline]\n#[allow(dead_code)]\npub fn app1_hash(rpc_id: u32) -> u32 {\n    let k = (rpc_id ^ 0u32).wrapping_mul(2654435761u32);\n    let g = G_ARRAY[(k >> 31) as usize] as u32;\n    (k >> 29).wrapping_add(g) & 3u32\n}\n"
  },
  {
    "path": "crates/render/test/app1/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused_variables)]\n\nuse core::ffi::c_char;\n\n#[repr(C)]\npub struct JbBlob {\n    pub buf: *const c_char,\n    pub len: u16,\n}\n\n// Modules use the standard Rust module system; files live under src/\n#[allow(dead_code)]\npub mod wire_messages;\n#[allow(dead_code)]\npub mod parsed_message;\npub mod encoder;\n#[allow(dead_code)]\npub mod hash;\n"
  },
  {
    "path": "crates/render/test/app1/src/parsed_message.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n\n// For slice -> array conversions in from_ne_bytes calls\n#[allow(unused_imports)]\nuse core::convert::TryInto;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum DecodeError {\n  BufferTooSmall,\n  InvalidRpcId { got: u16 },\n  InvalidLength { len: u16 },\n  Utf8 { field: &'static str },\n}\n\n// Parsed struct for pulse\npub struct pulse {\n  pub _rpc_id: u16,\n}\n\nimpl pulse {\n  pub const RPC_ID: u16 = 65535u16;\n\n  #[inline]\n  pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n    // Require rpc_id\n    if body.len() < 2 { return Err(DecodeError::BufferTooSmall); }\n    let mut b2 = [0u8;2];\n    b2.copy_from_slice(&body[0..2]);\n    let rpc = u16::from_ne_bytes(b2);\n    if rpc != Self::RPC_ID { return Err(DecodeError::InvalidRpcId { got: rpc }); }\n\n    if body.len() < 2usize { return Err(DecodeError::BufferTooSmall); }\n\n    // Decode fixed header fields\n\n    // Decode dynamic payload strings\n\n    Ok(Self {\n      _rpc_id: rpc,\n    })\n  }\n}\n"
  },
  {
    "path": "crates/render/test/app1/src/wire_messages.rs",
    "content": "/**********************************************\n * !!! render-generated code, do not modify !!!\n **********************************************/\n\n#[allow(non_camel_case_types)]\n#[allow(non_snake_case)]\n#[allow(dead_code)]\n\n  #[repr(C)]\n  #[derive(Copy, Clone)]\n  pub struct jb_app1__pulse {\n    pub _rpc_id: u16,\n  }\n\n  impl jb_app1__pulse {\n    #[inline]\n    pub fn metadata() -> render_parser::MessageMetadata {\n      render_parser::MessageMetadata::new_fixed(65535u16, 2, true)\n    }\n  }\n\n  impl Default for jb_app1__pulse {\n    #[inline]\n    fn default() -> Self { unsafe { core::mem::zeroed() } }\n  }\n\n  pub const PULSE_WIRE_SIZE: usize = 2;\n\n  #[cfg(test)]\n  mod pulse_layout_tests {\n    use super::*;\n    use core::mem::{offset_of, align_of};\n    #[test]\n    fn struct_size() {\n      let size = size_of::<jb_app1__pulse>();\n      let align = align_of::<jb_app1__pulse>();\n      let padded_raw_size = (PULSE_WIRE_SIZE + align - 1) / align * align;\n      assert_eq!(size, padded_raw_size);\n    }\n    #[test]\n    fn field_offsets() {\n      assert_eq!(offset_of!(jb_app1__pulse, _rpc_id), 0);\n    }\n  }\n\n#[inline]\npub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n  ::std::vec![\n    jb_app1__pulse::metadata(),\n  ]\n}\n\n"
  },
  {
    "path": "crates/render/test/src/lib.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_extern_crates)]\nextern crate encoder_test_app1 as _crate_app1;\n#[no_mangle]\npub extern \"C\" fn __encoder_test_bundle_marker() {}\n"
  },
  {
    "path": "crates/render_parser/Cargo.toml",
    "content": "[package]\nname = \"render_parser\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nthiserror = \"1\"\nperfect_hash_map = { path = \"../perfect_hash_map\" }\n\n[features]\ndefault = []\n\n"
  },
  {
    "path": "crates/render_parser/src/lib.rs",
    "content": "//! Generic render parser for incoming messages.\n//!\n//! Overview\n//! - Maintains a perfect-hash map from `_rpc_id` to user-provided value `V`\n//!   together with message metadata (`MessageMetadata`).\n//! - Parses native-endian timestamp (`u64`) + message body as emitted by the\n//!   generated encoders.\n//! - Determines message length using `MessageMetadata::size()` for fixed-size\n//!   messages or the `_len: u16` header for dynamic-size messages.\n//!\n//! Message format (as produced by generated encoders)\n//! - `timestamp: u64` (native-endian)\n//! - `message: [u8]` starting at byte 8, which always begins with:\n//!   - `_rpc_id: u16`\n//!   - for dynamic-size messages only: `_len: u16` describing the total\n//!     message length in bytes after the timestamp (i.e., length of the\n//!     returned `message` slice). The parser enforces `_len >= 4` so the\n//!     returned `message` slice always contains `_rpc_id` and `_len`.\n//!\n//! Errors and panics\n//! - `Error::BufferTooSmall`: the provided buffer does not contain enough\n//!   bytes to complete parsing for the selected branch.\n//! - `Error::MessageNotRegistered`: there is no registered metadata for the\n//!   parsed `_rpc_id`.\n//! - `Error::InvalidLength { .. }`: for dynamic messages when `_len < 4`.\n//! - Panics can occur when using a broken perfect hash function that maps a\n//!   key outside `[0, hash_size)`, e.g. in `Parser::add_message` through the\n//!   underlying `PerfectHashMap`.\n//!\n//! Invariants\n//! - On success, `HandleOk::message` is a zero-copy slice into the original\n//!   buffer covering exactly the message body starting at byte 8.\n//! - For dynamic messages, `HandleOk::message.len() == _len` and `_len >= 4`.\n//! - For fixed messages, `HandleOk::message.len()` equals the registered fixed\n//!   size. The parser does not enforce a minimum fixed size, but consumers\n//!   typically encode `_rpc_id` at the start of the body.\n\n#![deny(missing_docs)]\n\nmod message;\npub use message::{MessageMetadata, Size};\n\nuse perfect_hash_map::{Key, PerfectHashMap};\n\n/// Error type for `Parser::handle`.\n#[derive(thiserror::Error, Debug, PartialEq, Eq)]\npub enum Error {\n    /// Not enough data in the buffer to make progress (like `-EAGAIN`).\n    #[error(\"buffer too small\")]\n    BufferTooSmall,\n    /// Handler was not registered for the RPC ID (like `-ENOENT`).\n    #[error(\"message not registered: rpc_id={0}\")]\n    MessageNotRegistered(u16),\n    /// Dynamic message declares an invalid `_len` smaller than header size.\n    #[error(\"invalid dynamic length: rpc_id={rpc_id} len={len}\")]\n    InvalidLength {\n        /// The parsed RPC ID.\n        rpc_id: u16,\n        /// The `_len` value.\n        len: u16,\n    },\n}\n\n/// Internal entry mapping rpc_id to metadata and user value.\npub struct Entry<V> {\n    /// The message metadata.\n    pub metadata: MessageMetadata,\n    /// The user-supplied value associated with this message.\n    pub value: V,\n}\n\n/// Successful result from `Parser::handle`.\n#[derive(Debug)]\npub struct HandleOk<'a, V> {\n    /// Slice to the full message body (excluding the 8-byte timestamp).\n    pub message: &'a [u8],\n    /// Reference to the user value associated with the message type.\n    pub value: &'a V,\n    /// Timestamp extracted from the message (native-endian u64).\n    pub timestamp: u64,\n}\n\n/// Parser that maintains message metadata and values keyed by RPC ID.\npub struct Parser<V, F = perfect_hash_map::HashFn>\nwhere\n    F: Fn(Key) -> u32 + Copy,\n{\n    map: PerfectHashMap<Entry<V>, F>,\n}\n\nimpl<V, F> Parser<V, F>\nwhere\n    F: Fn(Key) -> u32 + Copy,\n{\n    /// Creates a new parser with the given perfect-hash size and function.\n    ///\n    /// Panics\n    /// - If subsequent calls into the underlying map use a hash function that\n    ///   returns an out-of-range slot (>= `hash_size`).\n    pub fn new(hash_size: usize, hash: F) -> Self {\n        Self {\n            map: PerfectHashMap::new(hash_size, hash),\n        }\n    }\n\n    /// Adds or replaces a message entry for the given metadata and value.\n    ///\n    /// Returns\n    /// - `Ok(None)` if the key was not present.\n    /// - `Ok(Some(old))` if the same key was present and the value was replaced.\n    /// - `Err(CollisionError)` if the slot is occupied by a different key.\n    ///\n    /// Panics\n    /// - If the hash function returns an out-of-range slot (>= capacity()).\n    pub fn add_message(\n        &mut self,\n        metadata: MessageMetadata,\n        value: V,\n    ) -> Result<Option<V>, perfect_hash_map::CollisionError> {\n        let key = metadata.rpc_id as u32;\n        self.map\n            .insert(key, Entry { metadata, value })\n            .map(|opt| opt.map(|e| e.value))\n    }\n\n    /// Handles a single message from `data`.\n    ///\n    /// Expected format (as produced by the generated encoders):\n    /// - 8-byte native-endian timestamp\n    /// - message body starting with `_rpc_id: u16`\n    /// - for dynamic messages, `_len: u16` giving the total message length\n    ///   (in bytes) after the timestamp. Must be at least 4.\n    ///\n    /// Errors\n    /// - `BufferTooSmall` if the buffer does not contain enough bytes for the\n    ///   relevant branch.\n    /// - `MessageNotRegistered` if the parsed `_rpc_id` is unknown.\n    /// - `InvalidLength { .. }` if a dynamic message declares `_len < 4`.\n    pub fn handle<'a>(&'a self, data: &'a [u8]) -> Result<HandleOk<'a, V>, Error> {\n        // Need at least timestamp + rpc_id\n        if data.len() < 8 + 2 {\n            return Err(Error::BufferTooSmall);\n        }\n\n        let timestamp = {\n            let mut buf = [0u8; 8];\n            buf.copy_from_slice(&data[..8]);\n            u64::from_ne_bytes(buf)\n        };\n\n        let rpc_id = {\n            let mut b = [0u8; 2];\n            b.copy_from_slice(&data[8..10]);\n            u16::from_ne_bytes(b)\n        };\n\n        let key = rpc_id as u32;\n        let entry = match self.map.get(&key) {\n            Some(e) => e,\n            None => return Err(Error::MessageNotRegistered(rpc_id)),\n        };\n\n        let msg_slice = match entry.metadata.size() {\n            Size::Fixed(n) => {\n                // Require full fixed-size body\n                let need = 8usize + n;\n                if data.len() < need {\n                    return Err(Error::BufferTooSmall);\n                }\n                &data[8..need]\n            }\n            Size::Dynamic => {\n                // Need timestamp + rpc_id + _len\n                if data.len() < 8 + 4 {\n                    return Err(Error::BufferTooSmall);\n                }\n                let mut b = [0u8; 2];\n                b.copy_from_slice(&data[10..12]);\n                let len_field = u16::from_ne_bytes(b);\n                if len_field < 4 {\n                    return Err(Error::InvalidLength {\n                        rpc_id,\n                        len: len_field,\n                    });\n                }\n                let total_len_after_ts = len_field as usize;\n                let need = 8usize + total_len_after_ts;\n                if data.len() < need {\n                    return Err(Error::BufferTooSmall);\n                }\n                &data[8..need]\n            }\n        };\n\n        Ok(HandleOk {\n            message: msg_slice,\n            value: &entry.value,\n            timestamp,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn hash_mod8(k: Key) -> u32 {\n        (k % 8) as u32\n    }\n\n    /// Fixed-size happy path: exact-size buffer, zero-copy slice, fields.\n    #[test]\n    fn fixed_ok() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_fixed(1, 6, false);\n        p.add_message(md, \"ok\").unwrap();\n\n        // Build a buffer: ts(8) + rpc_id(2) + 6 bytes body\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&123_u64.to_ne_bytes());\n        buf.extend_from_slice(&1_u16.to_ne_bytes());\n        buf.extend_from_slice(&[0u8; 6]);\n\n        let out = p.handle(&buf).unwrap();\n        assert_eq!(out.timestamp, 123);\n        assert_eq!(out.value, &\"ok\");\n        assert_eq!(out.message.len(), 6);\n    }\n\n    /// Dynamic-size happy path: `_len` respected, zero-copy slice, fields.\n    #[test]\n    fn dynamic_ok() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_dynamic(2, false);\n        p.add_message(md, \"dyn\").unwrap();\n\n        // Build: ts(8) + rpc_id(2) + _len(2) + body(_len bytes)\n        let body_len: u16 = 10;\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&111_u64.to_ne_bytes());\n        buf.extend_from_slice(&2_u16.to_ne_bytes());\n        buf.extend_from_slice(&body_len.to_ne_bytes());\n        buf.extend_from_slice(&vec![0u8; body_len as usize]);\n\n        let out = p.handle(&buf).unwrap();\n        assert_eq!(out.timestamp, 111);\n        assert_eq!(out.value, &\"dyn\");\n        assert_eq!(out.message.len(), body_len as usize);\n    }\n\n    /// Dynamic messages must declare `_len >= 4`; `_len < 4` is invalid.\n    #[test]\n    fn dynamic_invalid_len_too_small() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_dynamic(9, false);\n        p.add_message(md, \"dyn\").unwrap();\n\n        // ts(8) + rpc_id(2) + _len(2=2) (no payload)\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&42_u64.to_ne_bytes());\n        buf.extend_from_slice(&9_u16.to_ne_bytes());\n        buf.extend_from_slice(&2u16.to_ne_bytes());\n\n        let err = p.handle(&buf).unwrap_err();\n        assert_eq!(err, Error::InvalidLength { rpc_id: 9, len: 2 });\n    }\n\n    /// Missing `_len` field for dynamic messages yields BufferTooSmall.\n    #[test]\n    fn dynamic_missing_len_field() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_dynamic(10, false);\n        p.add_message(md, \"dyn\").unwrap();\n\n        // Only ts + rpc_id present\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&7_u64.to_ne_bytes());\n        buf.extend_from_slice(&10_u16.to_ne_bytes());\n\n        assert_eq!(p.handle(&buf).unwrap_err(), Error::BufferTooSmall);\n    }\n\n    /// If `_len` claims more bytes than provided, BufferTooSmall is returned.\n    #[test]\n    fn dynamic_len_exceeds_buffer() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_dynamic(11, false);\n        p.add_message(md, \"dyn\").unwrap();\n\n        // ts + rpc_id + _len=10 but only 5 bytes of payload provided\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&1_u64.to_ne_bytes());\n        buf.extend_from_slice(&11_u16.to_ne_bytes());\n        buf.extend_from_slice(&10u16.to_ne_bytes());\n        buf.extend_from_slice(&vec![0u8; 5]);\n\n        assert_eq!(p.handle(&buf).unwrap_err(), Error::BufferTooSmall);\n    }\n\n    /// Trailing bytes after the declared dynamic length are ignored.\n    #[test]\n    fn dynamic_trailing_bytes_ignored() {\n        let mut p: Parser<&'static str, _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_dynamic(12, false);\n        p.add_message(md, \"dyn\").unwrap();\n\n        // _len = 6, but provide extra trailing bytes beyond that\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&99_u64.to_ne_bytes());\n        buf.extend_from_slice(&12_u16.to_ne_bytes());\n        buf.extend_from_slice(&6u16.to_ne_bytes());\n        buf.extend_from_slice(&vec![0u8; 6]);\n        buf.extend_from_slice(&[1, 2, 3, 4, 5, 6]);\n\n        let out = p.handle(&buf).unwrap();\n        assert_eq!(out.message.len(), 6);\n        // First two bytes of the message are rpc_id in native-endian\n        assert_eq!(&out.message[0..2], &12_u16.to_ne_bytes());\n    }\n\n    /// Fixed-size buffer too small: returns BufferTooSmall.\n    #[test]\n    fn need_more() {\n        let mut p: Parser<(), _> = Parser::new(8, hash_mod8);\n        let md = MessageMetadata::new_fixed(3, 2, false);\n        p.add_message(md, ()).unwrap();\n        let b = [0u8; 9]; // not enough for ts + body\n        assert_eq!(p.handle(&b).unwrap_err(), Error::BufferTooSmall);\n    }\n\n    /// Unregistered rpc_id: returns MessageNotRegistered.\n    #[test]\n    fn not_registered() {\n        let p: Parser<(), _> = Parser::new(8, hash_mod8);\n        // ts + rpc_id=7 + len\n        let mut buf = vec![0; 12];\n        buf[..8].copy_from_slice(&0_u64.to_ne_bytes());\n        buf[8..10].copy_from_slice(&7_u16.to_ne_bytes());\n        assert_eq!(p.handle(&buf).unwrap_err(), Error::MessageNotRegistered(7));\n    }\n\n    /// Replacing an existing rpc_id updates metadata and returns the old value.\n    #[test]\n    fn replace_same_rpc_returns_old_and_updates_metadata() {\n        fn hash_mod2(k: Key) -> u32 {\n            (k % 2) as u32\n        }\n        let mut p: Parser<&'static str, _> = Parser::new(2, hash_mod2);\n\n        // Insert fixed with rpc_id=5\n        p.add_message(MessageMetadata::new_fixed(5, 6, false), \"old\")\n            .unwrap();\n\n        // Replace with dynamic metadata and new value\n        let prev = p\n            .add_message(MessageMetadata::new_dynamic(5, false), \"new\")\n            .unwrap();\n        assert_eq!(prev, Some(\"old\"));\n\n        // Build a dynamic buffer for rpc_id=5 with len=4 (header only)\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&0_u64.to_ne_bytes());\n        buf.extend_from_slice(&5_u16.to_ne_bytes());\n        buf.extend_from_slice(&4u16.to_ne_bytes());\n\n        let out = p.handle(&buf).unwrap();\n        assert_eq!(out.value, &\"new\");\n        assert_eq!(out.message.len(), 4);\n    }\n\n    /// Collisions on different keys return an error and preserve existing data.\n    #[test]\n    fn collision_different_rpc_yields_error_and_preserves_existing() {\n        fn hash_mod2(k: Key) -> u32 {\n            (k % 2) as u32\n        }\n        let mut p: Parser<&'static str, _> = Parser::new(2, hash_mod2);\n\n        p.add_message(MessageMetadata::new_fixed(1, 2, false), \"one\")\n            .unwrap();\n        let err = p\n            .add_message(MessageMetadata::new_fixed(3, 2, false), \"three\")\n            .unwrap_err();\n        assert_eq!(err.existing_key, 1);\n\n        // Ensure original is still usable\n        let mut buf = Vec::new();\n        buf.extend_from_slice(&0_u64.to_ne_bytes());\n        buf.extend_from_slice(&1_u16.to_ne_bytes());\n        buf.extend_from_slice(&[0u8; 2]);\n        let out = p.handle(&buf).unwrap();\n        assert_eq!(out.value, &\"one\");\n    }\n\n    /// A broken hash that returns out-of-range indices will panic on insert.\n    #[test]\n    #[should_panic]\n    fn broken_hash_panics_on_insert() {\n        fn bad_hash(_: Key) -> u32 {\n            9999\n        }\n        let mut p: Parser<&'static str, _> = Parser::new(2, bad_hash);\n        let _ = p.add_message(MessageMetadata::new_fixed(1, 2, false), \"x\");\n    }\n}\n"
  },
  {
    "path": "crates/render_parser/src/message.rs",
    "content": "//! Message metadata and size descriptors.\n\n/// Size descriptor for a message.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum Size {\n    /// Fixed-size wire message (size in bytes, after the timestamp).\n    Fixed(usize),\n    /// Dynamic-size wire message; read `_len: u16` from the header.\n    Dynamic,\n}\n\n/// Metadata describing a message.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct MessageMetadata {\n    pub(crate) rpc_id: u16,\n    pub(crate) size: Size,\n}\n\nimpl MessageMetadata {\n    /// Creates a metadata entry for a fixed-size message.\n    /// The third parameter is deprecated and ignored.\n    pub const fn new_fixed(rpc_id: u16, size: usize, _needs_auth: bool) -> Self {\n        Self {\n            rpc_id,\n            size: Size::Fixed(size),\n        }\n    }\n\n    /// Creates a metadata entry for a dynamic-size message.\n    /// The second parameter is deprecated and ignored.\n    pub const fn new_dynamic(rpc_id: u16, _needs_auth: bool) -> Self {\n        Self {\n            rpc_id,\n            size: Size::Dynamic,\n        }\n    }\n\n    /// Returns the RPC ID for this message.\n    #[inline]\n    pub const fn rpc_id(&self) -> u16 {\n        self.rpc_id\n    }\n\n    /// Returns the size descriptor.\n    #[inline]\n    pub const fn size(&self) -> Size {\n        self.size\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    /// new_fixed sets rpc_id and Size::Fixed(size)\n    ///\n    /// Verifies that constructor stores the provided values and that getters\n    /// return them unchanged.\n    #[test]\n    fn new_fixed_sets_fields() {\n        let md = MessageMetadata::new_fixed(10, 6, false);\n        assert_eq!(md.rpc_id(), 10);\n        assert_eq!(md.size(), Size::Fixed(6));\n    }\n\n    /// new_dynamic sets rpc_id and Size::Dynamic\n    ///\n    /// Verifies that constructor stores the provided rpc_id and marks the\n    /// entry as dynamic.\n    #[test]\n    fn new_dynamic_sets_fields() {\n        let md = MessageMetadata::new_dynamic(11, false);\n        assert_eq!(md.rpc_id(), 11);\n        assert_eq!(md.size(), Size::Dynamic);\n    }\n\n    /// metadata equality and Debug implementations\n    ///\n    /// Asserts that deriving PartialEq works and that formatting with Debug\n    /// does not panic (smoke test via format!).\n    #[test]\n    fn metadata_equality_debug() {\n        let a = MessageMetadata::new_fixed(1, 2, false);\n        let b = MessageMetadata::new_fixed(1, 2, false);\n        let c = MessageMetadata::new_dynamic(1, false);\n        assert_eq!(a, b);\n        assert_ne!(a, c);\n        let _ = format!(\"{:?}\", a);\n    }\n}\n"
  },
  {
    "path": "crates/timeslot/Cargo.toml",
    "content": "[package]\nname = \"timeslot\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"Apache-2.0\"\ndescription = \"Fast approximate division utilities for time-based slotting\"\n\n[lib]\nname = \"timeslot\"\npath = \"src/lib.rs\"\n\n[dependencies]\n\n"
  },
  {
    "path": "crates/timeslot/src/fast_div.rs",
    "content": "//! Fast approximate division of `u64` by multiplying then right-shifting.\n//!\n//! Motivation. Integer division is relatively expensive on modern CPUs. Using Agner Fog's instruction tables [1]\n//!   as reference, the 64-bit DIV instruction (unsigned divide, see [2] for instruction documentation) has a latency of\n//!   35-88 cycles, and reciprocal throughput of 21-83 cycles/operation on the Skylake architecture (15 and 10 on Ice Lake).\n//!   Code that performs a lot of this type of division in tightly dependent instruction chains can benefit from cheaper\n//!   division.\n//!\n//! For example, in some telemetry/monitoring use-cases, there are millions of events per second, each with a nanosecond-scale\n//!   timestamp. The monitoring system wants to aggregate to seconds. Given a measurement, the code needs to quickly find which\n//!   1-second bin the measurement belongs to. When the telemetry streams have a minor amount of jitter from collection to aggregation,\n//!   say up to 60 seconds (i.e., \"close to real time\"), the result needs to disambiguate the second a sample belongs to, but\n//!   the result does not need to be unique across all seconds since the epoch. For example, the low 16-bits of the division is\n//!   sufficient. The division can also be approximate: if the division puts every 999,999,998.5 nanoseconds together rather than\n//!   exactly 1e9, the system still produces good aggregations in practice.\n//!\n//! Other instruction combinations are much cheaper than DIV. For example 64-bit MUL (integer multiplication) and SHRX (shift right\n//!   without affecting flags):\n//!    * MUL's latency is 3 cycles and reciprocal throughput is 1 on Skylake and Ice Lake\n//!    * SHRX's latency is 1 cycle and reciprocal throughput is 0.5 on Skylake and Ice Lake\n//!\n//! Idea. We can approximate the 64-bit division with a multiplication followed by a shift. To divide by D, if we find two\n//!   numbers M and S such that:\n//!       D ~= 2^S / M                   (say up to an error of 0.00001%)\n//!   then for a given x,\n//!       x / D ~=  x * M / 2^S          (up to an error of (1 / (1 +- 0.00001%)), which would be close in practice to 0.00001%).\n//!\n//! So how should we choose M and S? Given a value for S, we can compute\n//!    M = ((double)2^S / D) (where M is a u32 or u64)\n//! With the trucation to make M an integer, we have\n//!    M = 2^S / D - epsilon      where epsilon in [0, 1]\n//!\n//! Dividing the above by M and shuffling terms around, we can get the relative error:\n//!    (2^S / M) / D   =   1 + epsilon / M\n//! When 2^S is sufficiently larger than D, then M is large and the relative error (epsilon / M) is small.\n//!\n//! So large S is advantageous for precision. However, we cannot choose S to be arbitrarily large, because we are working with 64-bit\n//!   arithmetic.\n//!\n//! The user specifies how many least-significant bits they need from the fast_div, B. After the shift, 64-S bits remain, so we need\n//!   64-S >= B. We could set S = 64 - B to be the largest amount.\n//!\n//! The code below also fits M into a 32 bit value (nb, cannot see a reason why a u64 would be significanly less desireable).\n//!   To get 2^S / D <= 2^32, we want D >= 2^(S-32), or log_2(D) >= S-32. The code computes\n//!      S = min(64-B, floor(log_2(D))+32).    (because floor(log_2(D)) <= log_2(D))\n//!\n//! Examples:\n//!    Say we want to divide a nanosecond timestamp to timeslots of 5 seconds, i.e., 5e9 nanos, and the\n//!      application needs 16-bits of precision.\n//!\n//!    We have D = 5e9, B = 16.\n//!         log_2(D) = 32.219280948873624\n//!         floor(log_2(D)) = 32\n//!         S = min(64-16, 32+32) = 48\n//!         M = u32(2^48 / 5e9) = u32(56294.9953421312) = 56294\n//!    The relative error is .9953421312 / 56294 ~ 0.0017%\n//!    The division ends up being 2^S/M = 5000088405.703201, i.e., 5 seconds and around 88 microseconds\n//!\n//!    Note that in the above example, a lot of precision is lost from casting to u32. If instead the code rounded to the nearest\n//!      integer, we would have\n//!         M = round(2^48 / 5e9) = round(56294.9953421312) = 56295\n//!         2^S/M = 4999999586.29818, i.e., 5 seconds less 414 nanoseconds\n//!         and the relative error would be less than one part per million (PPM)\n//!  \n//!    Also, if the code only required 8 bits from the result:\n//!         D = 5e9, B = 8.\n//!         S = min(64-8, 32+32) = 56\n//!         M = u32(2^56 / 5e9) = u32(14411518.807585588) = 14411518\n//!         relative error is 0.807585588 / 14411518 ~ 5.6e-8, and 2^S/M is 5000000280.1875515, i.e., 5 seconds and 280 nanoseconds\n//!         and with round() instead of u32(): 4999999933.242841 i.e., 5 seconds less 77 nanoseconds.\n//!\n//! NB when using fast_div with time. With fast_div, the time interval in the divisor is changed up to a small epsilon, for example\n//!   5 seconds versus 5.000089 seconds as above. One side effect is that the boundaries between intervals also change, and do not land\n//!   on full seconds. For example, when looking at nanoseconds since the Epoch (midnight, Jan 1, 1970), today's 5 second timeslots\n//!   boundaries might land 3.5 seconds into the \"round\" 5 seconds. This is because the 89 microsecond difference really adds up over decades.\n//!   Each application should consider whether this is acceptable.\n//!\n//! [1] https://www.agner.org/optimize/instruction_tables.pdf\n//! [2] https://cdrdv2-public.intel.com/671110/325383-sdm-vol-2abcd.pdf\n\n//! Remainder via low bits.\n//!\n//! For a number `x`, the low `S` bits of `(x * M)` represent the fixed-point\n//! fractional part (with denominator `2^S`) of `x / D` when `M = 2^S / D`.\n//! Thus, an approximate remainder can be computed as:\n//!   rem ~= (D * ((x * M) & (2^S - 1))) >> S\n//!\n//! The multiplication by `D` may overflow 64-bit arithmetic. However, `D`\n//! fits in 64 bits and `((x * M) & (2^S - 1))` fits in `S` bits which is not\n//! more than `64 - B` bits (by construction). We therefore perform the\n//! multiplication in Rust `u128` and then shift right by `S`. The final result\n//! lies in `[0, D)` and fits in `u64`.\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct FastDiv {\n    mul: u32,\n    shift: u32,\n}\n\nimpl FastDiv {\n    /// Construct with known `mul` and `shift`.\n    pub const fn from_mul_shift(mul: u32, shift: u32) -> Self {\n        Self { mul, shift }\n    }\n\n    /// Construct by computing the `mul` and `shift` from a division amount.\n    ///\n    /// - `amt`: the amount by which to divide (e.g., nanoseconds per slot).\n    /// - `required_bits`: number of bits retained from the result (1..=64).\n    ///\n    /// Panics in debug builds if `amt <= 0.0` or `required_bits == 0`.\n    pub fn new(amt: f64, required_bits: u32) -> Self {\n        debug_assert!(amt > 0.0, \"amt must be > 0\");\n        debug_assert!(required_bits > 0, \"required_bits must be >= 1\");\n\n        // division by `amt` will remove at least this many bits from the dividend\n        let bits_removed = amt.log2().floor() as u32; // floor(log2(amt))\n\n        // most precision obtained if right shift all but the required bits\n        let mut shift = 64u32.saturating_sub(required_bits);\n\n        // but the multiplier must fit in u32\n        shift = shift.min(32 + bits_removed);\n\n        // Safety note: require shift < 64 because (1 << 64) does not fit in u64.\n        debug_assert!(shift < 64, \"shift must be < 64\");\n\n        // Compute mul = floor((2^shift) / amt), stored as u32\n        let two_pow_shift = (1u64 << shift) as f64;\n        let mul = (two_pow_shift / amt) as u32; // truncates toward zero for positive values\n\n        Self { mul, shift }\n    }\n\n    /// Returns the raw multiplier.\n    pub const fn mul(&self) -> u32 {\n        self.mul\n    }\n\n    /// Returns the right-shift amount.\n    pub const fn shift(&self) -> u32 {\n        self.shift\n    }\n\n    /// Returns the multiplication factor that is the estimated inverse of this division.\n    pub fn estimated_reciprocal(&self) -> f64 {\n        debug_assert!(self.shift < 64, \"shift must be < 64\");\n        (1u128 << self.shift) as f64 / (self.mul as f64)\n    }\n\n    /// Applies the fast divide to `x` producing an approximate `x / amt`.\n    ///\n    /// This mirrors the C++ operator overload `operator/(uint64_t, fast_div)`.\n    pub fn divide_u64(&self, x: u64) -> u32 {\n        ((x.wrapping_mul(self.mul as u64)) >> self.shift) as u32\n    }\n\n    /// Approximates the remainder of `x` divided by `d` using the fixed-point\n    /// low-`S` bits of `(x * M)`.\n    ///\n    /// Computes: `((d as u128) * (((x * M) & (2^S - 1)) as u128)) >> S`.\n    /// The multiply is done in `u128` to avoid overflow; the result is then\n    /// narrowed to `u64`, which is safe because it is less than `d`.\n    pub fn remainder(&self, x: u64, d: u64) -> u64 {\n        debug_assert!(self.shift < 64, \"shift must be < 64\");\n        let s = self.shift;\n        // Low S bits of (x * M)\n        let xm = x.wrapping_mul(self.mul as u64);\n        let mask = if s == 0 { 0 } else { (1u64 << s) - 1 };\n        let frac = xm & mask;\n\n        // Multiply in 128-bit to avoid overflow, then shift back by S.\n        let prod = (d as u128) * (frac as u128);\n        (prod >> s) as u64\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::FastDiv;\n\n    #[test]\n    fn basic_properties() {\n        let fd = FastDiv::from_mul_shift(1, 1);\n        assert_eq!(fd.mul(), 1);\n        assert_eq!(fd.shift(), 1);\n        assert!((fd.estimated_reciprocal() - ((1u128 << 1) as f64) / 1.0).abs() < 1e-9);\n        assert_eq!(fd.divide_u64(4), 2);\n    }\n\n    #[test]\n    fn construct_from_amount_example_like() {\n        // Similar to the example: divide nanoseconds by ~5s, require 16 bits\n        let fd = FastDiv::new(5e9_f64, 16);\n        // estimated_reciprocal should be close to ~5 seconds in nanoseconds\n        let r = fd.estimated_reciprocal();\n        assert!(r > 4.999e9 && r < 5.001e9);\n\n        // Spot-check divide behavior monotonicity\n        let a = 10_000_000_000u64; // 10s in nanos\n        let b = 15_000_000_000u64; // 15s in nanos\n        let da = fd.divide_u64(a);\n        let db = fd.divide_u64(b);\n        assert!(db >= da);\n    }\n\n    #[test]\n    fn remainder_power_of_two_exact_ratio() {\n        // Choose D = 2^13, and pick S so that M = 2^(S-13) fits in u32 exactly.\n        // Here: S=44 -> M=2^31.\n        let d: u64 = 1u64 << 13;\n        let fd = FastDiv::from_mul_shift(1u32 << 31, 44);\n\n        // For exact M=2^S/D, the remainder should be exact.\n        let xs = [0u64, 1, d - 1, d, d + 1, (1u64 << 40) + 12345];\n        for &x in &xs {\n            assert_eq!(fd.remainder(x, d), x % d);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/timeslot/src/lib.rs",
    "content": "//! timeslot crate\n//!\n//! Exposes `FastDiv` for fast approximate integer division suited for\n//! time-slotting use cases.\n\nmod fast_div;\npub mod virtual_clock;\n\npub use crate::fast_div::FastDiv;\npub use crate::virtual_clock::{UpdateError, VirtualClock};\n"
  },
  {
    "path": "crates/timeslot/src/virtual_clock.rs",
    "content": "//! VirtualClock: A clock driven by multiple inputs, discretized into timeslots.\n//!\n//! Input timestamps are divided into timeslots based on a fast approximate\n//! divider. Once all inputs move out of the current timeslot, the clock can\n//! advance (possibly skipping multiple slots). Timeslots are `u16` and deltas\n//! are computed as `i16` with modular wrap-around semantics.\n//!\n//! This is a Rust port of reducer/util/virtual_clock.{h,cc} with semantics\n//! verified against reducer/util/virtual_clock_test.cc.\n\nuse crate::FastDiv;\n\n/// Timeslot index type (16-bit ring).\npub type Timeslot = u16;\n/// Signed timeslot delta (wrap-around aware, via narrowing to i16).\ntype TimeslotDiff = i16;\n\n/// Error codes for `VirtualClock::update`.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum UpdateError {\n    /// The specified input cannot be updated at this time (EPERM).\n    NotPermitted,\n    /// The supplied timestamp points to a past timeslot (EINVAL).\n    PastTimeslot,\n}\n\n#[derive(Clone, Debug, Default)]\nstruct Input {\n    timeslot: Option<Timeslot>,\n}\n\n/// Clock driven by multiple inputs.\n///\n/// - Input timestamps are divided into timeslots based on `divider`.\n/// - Once all inputs move out of the current timeslot, the clock advances.\n/// - Inputs are first added using `add_inputs()`.\n#[derive(Clone, Debug)]\npub struct VirtualClock {\n    inputs: Vec<Input>,\n    divider: FastDiv,\n    timeslot_duration: f64,\n    current_timeslot: Option<Timeslot>,\n}\n\nimpl Default for VirtualClock {\n    /// Constructs the object using the default timestamp divider of\n    /// approximately 1 second (1e9) with 16 bits of precision retained.\n    fn default() -> Self {\n        let divider = FastDiv::new(1e9_f64, 16);\n        Self::new(divider)\n    }\n}\n\nimpl VirtualClock {\n    /// Constructs the object by using the specified timestamp divider.\n    pub fn new(divider: FastDiv) -> Self {\n        let timeslot_duration = divider.estimated_reciprocal();\n        Self {\n            inputs: Vec::new(),\n            divider,\n            timeslot_duration,\n            current_timeslot: None,\n        }\n    }\n\n    /// Adds `n` additional inputs.\n    pub fn add_inputs(&mut self, n: usize) {\n        self.inputs\n            .resize_with(self.inputs.len() + n, Default::default);\n    }\n\n    /// Returns the current number of inputs this clock has.\n    pub fn n_inputs(&self) -> usize {\n        self.inputs.len()\n    }\n\n    /// Returns whether the specified input is current with this clock.\n    /// Current means that the input timeslot equals the clock's timeslot.\n    /// Panics if `input_index` is out of bounds.\n    pub fn is_current(&self, input_index: usize) -> bool {\n        self.current_timeslot.is_some()\n            && self.inputs[input_index].timeslot == self.current_timeslot\n    }\n\n    /// Returns whether the specified input can be updated.\n    /// Panics if `input_index` is out of bounds.\n    pub fn can_update(&self, input_index: usize) -> bool {\n        self.inputs[input_index].timeslot == self.current_timeslot\n    }\n\n    /// Updates the specified input with a timestamp.\n    ///\n    /// Returns Ok(()) on success;\n    ///  - Err(NotPermitted) if the specified input can't be updated (`can_update()` would return false).\n    ///  - Err(PastTimeslot) if the supplied timestamp points to a past timeslot.\n    ///\n    /// Panics if `input_index` is out of bounds.\n    pub fn update(&mut self, input_index: usize, timestamp: u64) -> Result<(), UpdateError> {\n        // Compute new_timeslot before borrowing `input` mutably to avoid borrow conflicts.\n        let new_timeslot = self.map_timestamp(timestamp);\n\n        let input = &mut self.inputs[input_index];\n\n        if input.timeslot != self.current_timeslot {\n            return Err(UpdateError::NotPermitted);\n        }\n\n        if let Some(old) = input.timeslot {\n            let diff = signed_delta_u16(new_timeslot, old);\n            if diff >= 0 {\n                input.timeslot = Some(old.wrapping_add(diff as u16));\n            } else {\n                return Err(UpdateError::PastTimeslot);\n            }\n        } else {\n            input.timeslot = Some(new_timeslot);\n        }\n\n        Ok(())\n    }\n\n    /// Duration of time slots, in timestamp units.\n    pub fn timeslot_duration(&self) -> f64 {\n        self.timeslot_duration\n    }\n\n    /// Current timeslot, or None if not yet initialized.\n    pub fn current_timeslot(&self) -> Option<Timeslot> {\n        self.current_timeslot\n    }\n\n    /// Advances this clock's timeslot, if possible. Returns `true` if advanced.\n    pub fn advance(&mut self) -> bool {\n        if let Some(cur) = self.current_timeslot {\n            if let Some(advance_slots) = self.min_input_advance() {\n                if advance_slots > 0 {\n                    self.current_timeslot = Some(cur.wrapping_add(advance_slots as u16));\n                    return true;\n                }\n            }\n        } else {\n            // Initialize the current timeslot to the earliest input timeslot, if available.\n            self.current_timeslot = self.earliest_input_timeslot();\n        }\n        false\n    }\n\n    // --- helpers ---\n\n    fn map_timestamp(&self, ts: u64) -> Timeslot {\n        // Mirroring C++ operator/(uint64_t, fast_div), then truncate to u16.\n        self.divider.divide_u64(ts) as u16\n    }\n\n    fn earliest_input_timeslot(&self) -> Option<Timeslot> {\n        // If any input is unset, cannot determine earliest.\n        if self.inputs.iter().any(|i| i.timeslot.is_none()) {\n            return None;\n        }\n\n        let mut min_timeslot: Option<Timeslot> = None;\n        for input in &self.inputs {\n            let ts = input.timeslot.unwrap();\n            min_timeslot = Some(match min_timeslot {\n                Some(m) => m.min(ts),\n                None => ts,\n            });\n        }\n        let min_timeslot = min_timeslot.unwrap();\n\n        let mut min_diff: Option<TimeslotDiff> = None;\n        for input in &self.inputs {\n            let ts = input.timeslot.unwrap();\n            let diff = signed_delta_u16(ts, min_timeslot);\n            min_diff = Some(match min_diff {\n                Some(d) => d.min(diff),\n                None => diff,\n            });\n        }\n        let min_diff = min_diff.unwrap();\n        Some(min_timeslot.wrapping_add(min_diff as u16))\n    }\n\n    fn min_input_advance(&self) -> Option<TimeslotDiff> {\n        let cur = self.current_timeslot?;\n        let mut min_adv: Option<TimeslotDiff> = None;\n        for input in &self.inputs {\n            let ts = input.timeslot?;\n            let adv = signed_delta_u16(ts, cur);\n            min_adv = Some(match min_adv {\n                Some(a) => a.min(adv),\n                None => adv,\n            });\n        }\n        min_adv\n    }\n}\n\n#[inline]\nfn signed_delta_u16(new: u16, old: u16) -> i16 {\n    // Compute modular difference on the u16 ring, then narrow to i16.\n    // This exactly matches the C++ approach where narrowing wraps.\n    new.wrapping_sub(old) as i16\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{UpdateError, VirtualClock};\n\n    const TIMESLOT_MIN: u16 = u16::MIN;\n    const TIMESLOT_MAX: u16 = u16::MAX;\n\n    #[test]\n    fn empty() {\n        let clock = VirtualClock::default();\n        assert_eq!(clock.n_inputs(), 0);\n        assert!(clock.current_timeslot().is_none());\n    }\n\n    #[test]\n    fn add_inputs() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        assert_eq!(clock.n_inputs(), 2);\n        assert!(clock.current_timeslot().is_none());\n    }\n\n    #[test]\n    fn current_timeslot_init() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let timestamp = 0u64;\n\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert!(clock.current_timeslot().is_none());\n\n        assert_eq!(clock.update(1, timestamp), Ok(()));\n        assert!(clock.current_timeslot().is_none());\n\n        assert_eq!(clock.advance(), false);\n        assert!(clock.current_timeslot().is_some());\n    }\n\n    #[test]\n    fn can_update() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n\n        let timestamp = 0u64;\n        assert!(clock.can_update(0));\n        assert!(clock.can_update(1));\n\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp), Ok(()));\n\n        assert!(!clock.can_update(0));\n        assert!(!clock.can_update(1));\n\n        assert_eq!(clock.advance(), false);\n        assert!(clock.can_update(0));\n        assert!(clock.can_update(1));\n\n        // update input 1 past the current slot\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n        assert_eq!(clock.update(1, timestamp + ts_step), Ok(()));\n\n        // can't advance until input 0 advances\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.can_update(0));\n        assert!(!clock.can_update(1));\n        assert_eq!(\n            clock.update(1, timestamp + ts_step),\n            Err(UpdateError::NotPermitted)\n        );\n\n        // advance input 0 and the clock\n        assert_eq!(clock.update(0, timestamp + ts_step), Ok(()));\n        assert_eq!(clock.advance(), true);\n\n        // inputs are in sync\n        assert!(clock.is_current(0));\n        assert!(clock.is_current(1));\n        assert!(clock.can_update(0));\n        assert!(clock.can_update(1));\n    }\n\n    #[test]\n    fn initial_slot_min() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        assert_eq!(\n            clock.update(0, ts_step * (TIMESLOT_MIN as u64 + 42)),\n            Ok(())\n        );\n        assert_eq!(\n            clock.update(1, ts_step * (TIMESLOT_MIN as u64 + 43)),\n            Ok(())\n        );\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MIN + 42);\n    }\n\n    #[test]\n    fn initial_slot_mid() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        let timeslot_mid = TIMESLOT_MAX / 2;\n        assert_eq!(\n            clock.update(0, ts_step * ((timeslot_mid - 10) as u64)),\n            Ok(())\n        );\n        assert_eq!(\n            clock.update(1, ts_step * ((timeslot_mid + 10) as u64)),\n            Ok(())\n        );\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), timeslot_mid - 10);\n    }\n\n    #[test]\n    fn initial_slot_wrap() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        assert_eq!(clock.update(0, ts_step * (TIMESLOT_MAX as u64)), Ok(()));\n        assert_eq!(\n            clock.update(1, ts_step * ((TIMESLOT_MAX as u64) + 1)),\n            Ok(())\n        );\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MAX);\n    }\n\n    #[test]\n    fn initial_slot_wrap_2() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n        let timeslot_mid = TIMESLOT_MAX / 2;\n\n        assert_eq!(\n            clock.update(0, ts_step * ((timeslot_mid + 10) as u64)),\n            Ok(())\n        );\n        assert_eq!(\n            clock.update(1, ts_step * ((TIMESLOT_MAX as u64) + 1)),\n            Ok(())\n        );\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), timeslot_mid + 10);\n    }\n\n    #[test]\n    fn initial_slot_wrap_3() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n        let timeslot_mid = TIMESLOT_MAX / 2;\n\n        assert_eq!(\n            clock.update(0, ts_step * ((timeslot_mid - 10) as u64)),\n            Ok(())\n        );\n        assert_eq!(\n            clock.update(1, ts_step * ((TIMESLOT_MAX as u64) + 1)),\n            Ok(())\n        );\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MIN);\n    }\n\n    #[test]\n    fn advance_basic() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n\n        let mut timestamp = 0u64;\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp), Ok(()));\n\n        assert_eq!(clock.advance(), false);\n\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n        timestamp += ts_step;\n\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp), Ok(()));\n\n        assert_eq!(clock.advance(), true);\n    }\n\n    #[test]\n    fn advance_catchup() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        let timestamp = ts_step * 42;\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp + 2 * ts_step), Ok(()));\n\n        assert_eq!(clock.advance(), false);\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), 42);\n\n        assert_eq!(clock.update(0, timestamp + ts_step), Ok(()));\n        assert_eq!(clock.advance(), true);\n        assert_eq!(clock.current_timeslot().unwrap(), 43);\n\n        assert_eq!(clock.update(0, timestamp + 2 * ts_step), Ok(()));\n        assert_eq!(clock.advance(), true);\n        assert_eq!(clock.current_timeslot().unwrap(), 44);\n\n        assert!(clock.is_current(0));\n        assert!(clock.is_current(1));\n    }\n\n    #[test]\n    fn advance_skipslots() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        let timestamp = ts_step * 42;\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp + 2 * ts_step), Ok(()));\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), 42);\n\n        assert_eq!(clock.update(0, timestamp + 2 * ts_step), Ok(()));\n        assert_eq!(clock.advance(), true);\n        assert_eq!(clock.current_timeslot().unwrap(), 44);\n\n        assert!(clock.is_current(0));\n        assert!(clock.is_current(1));\n    }\n\n    #[test]\n    fn advance_wraparound() {\n        let mut clock = VirtualClock::default();\n        clock.add_inputs(2);\n        let ts_step = clock.timeslot_duration().ceil() as u64;\n\n        let timestamp = ts_step * (TIMESLOT_MAX as u64);\n        assert_eq!(clock.update(0, timestamp), Ok(()));\n        assert_eq!(clock.update(1, timestamp), Ok(()));\n        assert_eq!(clock.advance(), false);\n\n        assert!(clock.current_timeslot().is_some());\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MAX);\n\n        let timestamp2 = timestamp + ts_step;\n        assert_eq!(clock.update(0, timestamp2), Ok(()));\n        assert_eq!(clock.advance(), false);\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MAX);\n\n        assert_eq!(clock.update(1, timestamp2), Ok(()));\n        assert_eq!(clock.advance(), true);\n        assert_eq!(clock.current_timeslot().unwrap(), TIMESLOT_MIN);\n\n        assert!(clock.is_current(0));\n        assert!(clock.is_current(1));\n    }\n}\n"
  },
  {
    "path": "dev/CMakeLists.txt",
    "content": "\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_subdirectory(script)\n\nlint_shell_script_bundle(\n  dev-scripts\n  SOURCES\n    benv-list.sh\n    benv-run.sh\n)\n"
  },
  {
    "path": "dev/benv-build.sh",
    "content": "#!/bin/bash -x\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\n# Defaults\nclean=\"false\"\ncmake_only=\"false\"\nasan=\"false\"\nubsan=\"false\"\njobs=\"$(((`nproc --all` + 1) / 2))\"\n\n# must match the mount destination of $host_build_dir in benv script\nsource_dir=\"${EBPF_NET_SRC:-${EBPF_NET_SRC_ROOT}}\"\nbuild_dir=\"${EBPF_NET_OUT_DIR:-$HOME/out}\"\n\nshow_help() {\n  cat << EOF\nUsage:\n  ${0##*/} [options...] [targets...]\n\nOptions:\n  --clean: clean previous build artifacts before building\n  -j N | --jobs N: use N parallel jobs for building (default: auto-detect)\n  --debug: build with CMAKE_BUILD_TYPE=\"Debug\" which includes debug code and turns off compiler\n           optimization that prevents readable single stepping\n  --cmake: run cmake but don't build with a subsequent make\n  --asan: enable the address sanitizer (use with --debug to get better backtraces when an asan issue is hit)\n  --ubsan: enable the undefined behavior sanitizer (which can have false positives: https://stackoverflow.com/a/57304113)\n  --py-static-check: enables static checks for python\n  --go-static-linking: enables static linking for Go binaries\n  --no-docker: don't run docker commands on docker build targets (needed when using an external docker build tool like CI/CD)\n  --fail-fast: fail on the first error instead of building as much as possible\n  --dep-tree: produce a dot-file output of the build graph\n  --list-targets: print the list of targets that can be built using cmake by passing target(s) to this build script\n  -v | --verbose: verbose build output\n\nTargets:\n  run 'make help' after successfully running cmake to get a list of targets\nEOF\n}\n\ndie() {\n    printf '%s\\n' \"$1\" >&2\n    exit 1\n}\n\nCMAKE_BUILD_TYPE=\"RelWithDebInfo\"\ncmake_args=()\nmake_args=()\nfail_fast=false\n\nwhile :; do\n  case $1 in\n    -h|-\\?|--help)\n      show_help\n      exit\n      ;;\n    --clean)\n      clean=\"true\"\n      ;;\n    -j|--jobs)\n      if [ \"$2\" ]; then\n        jobs=\"$2\"\n        shift\n      else\n        die \"ERROR: --jobs requires a numeric argument\"\n      fi\n      ;;\n    -j|--jobs=?*)\n      jobs=${1#*=}\n      ;;\n    --cmake)\n      cmake_only=\"true\"\n      ;;\n    --debug)\n      CMAKE_BUILD_TYPE=\"Debug\"\n      cmake_args+=( \\\n        -DOPTIMIZE=OFF\n      )\n      ;;\n    --dep-tree)\n      cmake_args+=(--graphviz=dep-tree.dot)\n      ;;\n    --list-targets)\n      list_targets=\"true\"\n      ;;\n    --asan)\n      cmake_args+=(-DUSE_ADDRESS_SANITIZER=ON)\n      ;;\n    --ubsan)\n      cmake_args+=(-DUSE_UNDEFINED_BEHAVIOR_SANITIZER=ON)\n      ;;\n    --py-static-check)\n      cmake_args+=(-DPY_STATIC_CHECK=ON)\n      ;;\n    --go-static-linking)\n      cmake_args+=(-DGO_STATIC_LINK=ON)\n      ;;\n    -v | --verbose)\n      make_args+=(VERBOSE=1)\n      ;;\n    --no-docker)\n      cmake_args+=(-DRUN_DOCKER_COMMANDS=OFF)\n      ;;\n    --fail-fast)\n      fail_fast=true\n      ;;\n    --)\n      shift\n      break\n      ;;\n    -?*)\n      printf 'WARN: Unknown option (ignored): %s\\n' \"$1\" >&2\n      ;;\n    *)\n      break\n  esac\n  shift\ndone\n\nwhile [[ \"$#\" -gt 0 ]]; do make_args+=(\"$1\"); shift; done\n\n# Add the source directory to git's list of safe directories.\n# Prevents \"fatal: detected dubious ownership in repository at ...\" error.\nif [[ $(git config --global --get-all safe.directory | grep -ce \"^${source_dir}\\$\") == \"0\" ]]; then\n  git config --global --add safe.directory $source_dir\nfi\n\ncd \"${source_dir}\"\necho -n \"version being built: \"\nsource ./version.sh\n\nCMAKE_FLAGS=( \\\n  -DCMAKE_INSTALL_PREFIX:PATH=\"/install\"\n  -DOPENSSL_ROOT_DIR:PATH=\"/install/openssl\"\n  -DCMAKE_PREFIX_PATH:PATH=\"/install\"\n  -DEBPF_NET_MAJOR_VERSION=${EBPF_NET_MAJOR_VERSION}\n  -DEBPF_NET_MINOR_VERSION=${EBPF_NET_MINOR_VERSION}\n  -DEBPF_NET_PATCH_VERSION=${EBPF_NET_PATCH_VERSION}\n  -DEBPF_NET_COLLECTOR_BUILD_NUMBER=${EBPF_NET_COLLECTOR_BUILD_NUMBER}\n  -DEBPF_NET_PIPELINE_BUILD_NUMBER=${EBPF_NET_PIPELINE_BUILD_NUMBER}\n  \"${cmake_args[@]}\"\n)\n\nif ((\"$jobs\" <= 0)); then\n  die \"ERROR: --jobs requires a positive numeric argument; you gave ($jobs)\"\nfi\n\nif [[ \"$clean\" == \"true\" ]]; then\n  if [ -d \"$build_dir\" ]; then\n    rm -rf $build_dir/* || true\n  fi\nfi\n\nmkdir -p \"$build_dir\"\ncd \"$build_dir\"\n\nset -e\n\n(set -x; cmake \\\n  \"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\" \\\n  \"${CMAKE_FLAGS[@]}\" \\\n  \"${source_dir}\"\n)\n\nif [[ \"$cmake_only\" == \"true\" ]]; then\n  exit 0\nfi\n\nif [[ \"$list_targets\" == \"true\" ]]; then\n    cmake --build . --target help\n    exit 0\nfi\n\n[[ \"${fail_fast}\" == \"true\" ]] || make_args=(--keep-going \"${make_args[@]}\")\n\n(set -x; make -j\"$jobs\" \"${make_args[@]}\") \\\n  && echo \"BUILD SUCCESSFUL\"\n"
  },
  {
    "path": "dev/benv-list.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n# shellcheck source=/dev/null\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n\n# determine benv container IDs\ncontainer_ids=$(docker ps | grep -E \"build-env|benv-final\" | awk '{print $1}') || true\nif [ -z \"${container_ids}\" ]\nthen\n  echo \"no running benv containers found\"\n  exit 1\nfi\n\necho -e \"CONTAINER_NAME\\\\t\\\\tCONTAINER_ID\\\\tBENV_BUILD_DIR\\\\t\\\\t\\\\tIMAGE\"\nfor container_id in ${container_ids}\ndo\n  container_name=$(docker inspect \"${container_id}\" | jq .[].Name | sed 's/\\///' | sed 's/\"//g')\n  benv_build_dir=$(docker inspect \"${container_id}\" | jq .[].Mounts | grep tmp | awk '{print $2}' | sed 's/,$//' | sed 's/\"//g')\n  image=$(docker inspect \"${container_id}\" | jq .[].Config.Image | sed 's/\"//g')\n  echo -e \"${container_name}\\\\t${container_id}\\\\t${benv_build_dir}\\\\t${image}\"\ndone\n\n\n"
  },
  {
    "path": "dev/benv-run.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n# shellcheck source=/dev/null\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n# shellcheck source=/dev/null\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/benv-lib.sh\"\n\ndocker exec \"$(get_benv_container_name)\" \"${@}\"\n\n"
  },
  {
    "path": "dev/commits_to_prs.sh",
    "content": "#!/usr/bin/env bash\n\n# Converts each commit on top of UPSTREAM/BASE into its own PR.\n# For each commit (oldest to newest):\n#   - Creates a branch from upstream base\n#   - Cherry-picks the commit onto that branch\n#   - Pushes the branch to origin\n#   - Opens a PR against upstream/base with title/body from the commit\n#\n# Defaults assume a typical fork setup with remotes:\n#   origin   -> your fork (push target)\n#   upstream -> canonical repo (PR base)\n#\n# Requirements:\n#   - git and gh installed\n#   - gh authenticated (gh auth login)\n#   - remotes configured: an 'upstream' remote pointing to the canonical repo\n#\n# Environment variables (optional):\n#   UPSTREAM_REMOTE   Remote name for upstream (default: upstream)\n#   ORIGIN_REMOTE     Remote name for origin   (default: origin)\n#   BASE_BRANCH       Base branch on upstream  (default: main)\n#   BRANCH_PREFIX     Prefix for created branches (default: pr)\n#   DRY_RUN           If set (non-empty), only print actions\n#   INCLUDE_MERGES    If set (non-empty), attempt to include merge commits (default: skip merges)\n#   OVERWRITE_LOCAL   If set, overwrite existing local branch of same name\n#\n# Notes:\n#   - Merge commits are skipped by default because cherry-picking merges\n#     requires selecting a parent (-m) and can be ambiguous.\n#   - On conflicts during cherry-pick, the script stops. Resolve conflicts and\n#     re-run; it will skip already-created branches. If a commit was already\n#     applied (empty cherry-pick), it is detected and skipped.\n\nset -euo pipefail\n\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream}\nORIGIN_REMOTE=${ORIGIN_REMOTE:-origin}\nBASE_BRANCH=${BASE_BRANCH:-main}\nBRANCH_PREFIX=${BRANCH_PREFIX:-pr}\nDRY_RUN=${DRY_RUN:-}\nINCLUDE_MERGES=${INCLUDE_MERGES:-}\nOVERWRITE_LOCAL=${OVERWRITE_LOCAL:-}\n\nlog() { echo \"[commits_to_prs] $*\"; }\ndie() { echo \"[commits_to_prs][error] $*\" >&2; exit 1; }\n\ncommand -v git >/dev/null 2>&1 || die \"git is required\"\ncommand -v gh  >/dev/null 2>&1 || die \"gh is required (https://cli.github.com/)\"\n\n# Ensure gh is authenticated early to avoid half-progress\nif ! gh auth status >/dev/null 2>&1; then\n  die \"gh auth not configured. Run 'gh auth login' first.\"\nfi\n\n# Determine upstream repo owner/repo slug\ndetermine_upstream_repo() {\n  local upstream_repo=\"\"\n  # Prefer parsing from git remote URL to avoid gh needing a resolvable slug\n  local upstream_url\n  upstream_url=$(git remote get-url \"$UPSTREAM_REMOTE\" 2>/dev/null || true)\n  if [ -n \"$upstream_url\" ]; then\n    upstream_repo=$(echo \"$upstream_url\" \\\n      | sed -E 's#^git@[^:]+:##; s#^https?://[^/]+/##; s#\\.git$##')\n  fi\n  if [ -z \"$upstream_repo\" ]; then\n    upstream_repo=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)\n  fi\n  printf \"%s\" \"$upstream_repo\"\n}\n\nUPSTREAM_REPO=$(determine_upstream_repo)\n[ -n \"$UPSTREAM_REPO\" ] || die \"Unable to determine upstream repo. Ensure remote '$UPSTREAM_REMOTE' exists.\"\n\n# Determine origin owner (for detecting existing PRs as fork:branch)\ndetermine_origin_owner() {\n  local origin_owner=\"\"\n  # Prefer parsing from git remote URL\n  local origin_url\n  origin_url=$(git remote get-url \"$ORIGIN_REMOTE\" 2>/dev/null || true)\n  if [ -n \"$origin_url\" ]; then\n    origin_owner=$(echo \"$origin_url\" \\\n      | sed -E 's#^git@[^:]+:##; s#^https?://[^/]+/##; s#/.*$##')\n  fi\n  if [ -z \"$origin_owner\" ]; then\n    origin_owner=$(gh repo view --json owner -q .owner.login 2>/dev/null || true)\n  fi\n  printf \"%s\" \"$origin_owner\"\n}\n\nORIGIN_OWNER=$(determine_origin_owner)\n[ -n \"$ORIGIN_OWNER\" ] || die \"Unable to determine origin owner. Ensure remote '$ORIGIN_REMOTE' exists.\"\n\n# Check if a PR already exists for this branch\n# Tries multiple strategies to be robust across gh versions.\npr_exists_for_branch() {\n  local branch_name=\"$1\"\n  local out\n  # 1) Direct head filter with owner:branch\n  out=$(gh pr list --repo \"$UPSTREAM_REPO\" --state open --head \"$ORIGIN_OWNER:$branch_name\" --json number --jq '.[0].number' 2>/dev/null || true)\n  if [ -n \"$out\" ]; then return 0; fi\n  # 2) Filter open PRs by headRefName (branch name)\n  out=$(gh pr list --repo \"$UPSTREAM_REPO\" --state open --json number,headRefName --jq 'map(select(.headRefName==\"'\"$branch_name\"'\")) | .[0].number' 2>/dev/null || true)\n  if [ -n \"$out\" ]; then return 0; fi\n  # 3) As a fallback, search all PRs (in case of state mismatch)\n  out=$(gh pr list --repo \"$UPSTREAM_REPO\" --state all --json number,headRefName --jq 'map(select(.headRefName==\"'\"$branch_name\"'\")) | .[0].number' 2>/dev/null || true)\n  [ -n \"$out\" ]\n}\n\n# Fetch latest base\nlog \"Fetching $UPSTREAM_REMOTE/$BASE_BRANCH\"\nif [ -z \"$DRY_RUN\" ]; then\n  git fetch \"$UPSTREAM_REMOTE\" \"$BASE_BRANCH\" --tags --prune\nfi\n\nBASE_REF=\"$UPSTREAM_REMOTE/$BASE_BRANCH\"\n\n# Compute list of commits unique to HEAD vs base, oldest->newest\nlog \"Computing commits on top of $BASE_REF\"\nif [ -n \"$INCLUDE_MERGES\" ]; then\n  COMMITS=$(git rev-list --reverse \"$BASE_REF\"..HEAD)\nelse\n  COMMITS=$(git rev-list --reverse --no-merges \"$BASE_REF\"..HEAD)\nfi\n\nif [ -z \"$COMMITS\" ]; then\n  log \"No commits found on top of $BASE_REF. Nothing to do.\"\n  exit 0\nfi\n\n# Helper to slugify a string for branch names\nslugify() {\n  # lower, replace non-alnum with '-', collapse dashes, trim, limit length\n  tr '[:upper:]' '[:lower:]' \\\n    | sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g; s/-{2,}/-/g' \\\n    | cut -c1-50\n}\n\n# For each commit, create branch, cherry-pick, push, and open PR\nfor SHA in $COMMITS; do\n  PARENTS=$(git show -s --format=%P \"$SHA\")\n  if [ -z \"$INCLUDE_MERGES\" ] && [ \"$(wc -w <<<\"$PARENTS\")\" -gt 1 ]; then\n    log \"Skipping merge commit $SHA (set INCLUDE_MERGES=1 to include)\"\n    continue\n  fi\n\n  SUBJECT=$(git show -s --format=%s \"$SHA\")\n  BODY=$(git show -s --format=%b \"$SHA\")\n  SLUG=$(printf '%s' \"$SUBJECT\" | slugify)\n  SHORTSHA=$(printf '%.7s' \"$SHA\")\n  BRANCH=\"$BRANCH_PREFIX/$SHORTSHA-$SLUG\"\n\n  log \"Processing $SHA → branch '$BRANCH'\"\n\n  # Check local branch existence\n  if git show-ref --verify --quiet \"refs/heads/$BRANCH\"; then\n    if [ -z \"$OVERWRITE_LOCAL\" ]; then\n      log \"Local branch $BRANCH exists; skipping branch creation.\"\n    else\n      log \"Overwriting local branch $BRANCH from $BASE_REF\"\n      if [ -z \"$DRY_RUN\" ]; then\n        git branch -f \"$BRANCH\" \"$BASE_REF\"\n      fi\n    fi\n  else\n    log \"Creating branch $BRANCH from $BASE_REF\"\n    if [ -z \"$DRY_RUN\" ]; then\n      git branch \"$BRANCH\" \"$BASE_REF\"\n    fi\n  fi\n\n  # Switch to branch\n  if [ -z \"$DRY_RUN\" ]; then\n    git switch \"$BRANCH\"\n  fi\n\n  # Cherry-pick the commit\n  log \"Cherry-picking $SHA onto $BRANCH\"\n  if [ -z \"$DRY_RUN\" ]; then\n    if ! git cherry-pick -x \"$SHA\"; then\n      # If the cherry-pick failed, it can be either a real conflict or an\n      # empty pick because the commit is already applied to this branch.\n      # Detect the latter and skip it so we can continue.\n      if git rev-parse -q --verify CHERRY_PICK_HEAD >/dev/null 2>&1; then\n        # If there are no unmerged files, the failure is likely an empty pick.\n        if [ -z \"$(git diff --name-only --diff-filter=U)\" ]; then\n          log \"Cherry-pick produced no changes (already applied). Skipping $SHA.\"\n          # Skip this pick in the sequencer and move on.\n          git cherry-pick --skip >/dev/null 2>&1 || true\n        else\n          die \"Cherry-pick conflicted for $SHA. Resolve conflicts and re-run.\"\n        fi\n      else\n        die \"Cherry-pick failed unexpectedly for $SHA.\"\n      fi\n    fi\n  fi\n\n  # Push to origin\n  log \"Pushing $BRANCH to $ORIGIN_REMOTE\"\n  if [ -z \"$DRY_RUN\" ]; then\n    git push -u \"$ORIGIN_REMOTE\" \"$BRANCH\"\n  fi\n\n  # Create PR with title/body from commit\n  log \"Creating PR against $UPSTREAM_REPO:$BASE_BRANCH\"\n  if [ -z \"$DRY_RUN\" ]; then\n    # If a PR for this fork branch already exists against upstream, skip creation\n    if pr_exists_for_branch \"$BRANCH\"; then\n      log \"PR already exists for branch $BRANCH; skipping creation.\"\n    else\n      BODY_FILE=$(mktemp)\n      cleanup() { rm -f \"$BODY_FILE\" 2>/dev/null || true; }\n      trap cleanup EXIT\n      printf '%s' \"$BODY\" > \"$BODY_FILE\"\n\n      if ! gh pr create \\\n        --repo \"$UPSTREAM_REPO\" \\\n        --base \"$BASE_BRANCH\" \\\n        --title \"$SUBJECT\" \\\n        --body-file \"$BODY_FILE\"; then\n        # Race, or 'already exists' case; treat as success if detectable\n        if pr_exists_for_branch \"$BRANCH\"; then\n          log \"Detected existing PR after creation attempt; continuing.\"\n        else\n          die \"Failed to create PR for $SHA (branch $BRANCH)\"\n        fi\n      fi\n    fi\n  fi\n\n  log \"Done with $SHA\"\ndone\n\nlog \"All done.\"\n"
  },
  {
    "path": "dev/commits_to_stacked_prs.sh",
    "content": "#!/usr/bin/env bash\n\n# Create a stack of PRs from a linear sequence of commits.\n#\n# Given:\n#   1) a starting base branch (e.g. upstream/main or the head of a prior train)\n#   2) a starting commit (bottom of the stack)\n#   3) a finish commit (top of the stack)\n#\n# The script will:\n#   - Create one branch per commit from start..finish (inclusive), oldest → newest\n#   - For the first commit, branch from START_BASE (e.g. upstream/main) and cherry-pick that commit\n#   - For subsequent commits, branch from the previous PR branch and cherry-pick the next commit\n#   - Push each branch to the configured push remote\n#   - Open a PR for each branch using the commit title as the PR title and\n#     the commit body as the PR description\n#   - The base for PR #1 is START_BASE; the base for PR #N>1 is the branch for PR #(N-1)\n#\n# This produces a \"stack\" of PRs, where each PR depends on the one before it.\n#\n# Important notes about forks vs upstream:\n#   - For GitHub to accept a PR with base = \"previous PR branch\", that base branch\n#     must exist in the base repository (the upstream repo you are opening PRs against).\n#   - If you push branches only to your fork, base branches will not exist in upstream,\n#     and PR creation for PR #2+ will fail. To get true stacked PRs:\n#       a) Set PUSH_REMOTE to your upstream remote (often 'upstream'), or\n#       b) Grant permission and push branches to upstream as well.\n#   - If you cannot push branches to upstream, this script cannot create true stacked\n#     PRs because GitHub does not support a fork branch as the base of a PR in upstream.\n#\n# Usage:\n#   dev/commits_to_stacked_prs.sh <START_BASE_BRANCH> <START_COMMIT> <FINISH_COMMIT>\n# Example:\n#   dev/commits_to_stacked_prs.sh main abc123 def456\n#\n# Requirements:\n#   - git and gh installed\n#   - gh authenticated (gh auth login)\n#   - remotes configured: an 'upstream' remote pointing to the canonical repo\n#\n# Environment variables (optional):\n#   UPSTREAM_REMOTE   Remote name for upstream (default: upstream)\n#   ORIGIN_REMOTE     Remote name for origin   (default: origin)\n#   PUSH_REMOTE       Remote to push branches to (default: $UPSTREAM_REMOTE)\n#   BRANCH_PREFIX     Prefix for created branches (default: stack)\n#   DRY_RUN           If set (non-empty), only print actions\n#   INCLUDE_MERGES    If set (non-empty), include merge commits (default: skip merges)\n#   OVERWRITE_LOCAL   If set, overwrite existing local branch of same name\n#   STACK_PUSH_UPSTREAM If set, also push created branches to $UPSTREAM_REMOTE (for stacking)\n#\n# Conflict handling:\n#   - On conflicts during cherry-pick, the script stops. Resolve conflicts and\n#     re-run; it will skip already-created branches. If a commit was already\n#     applied (empty cherry-pick), it is detected and skipped.\n\nset -euo pipefail\n\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream}\nORIGIN_REMOTE=${ORIGIN_REMOTE:-origin}\nPUSH_REMOTE=${PUSH_REMOTE:-$UPSTREAM_REMOTE}\nBRANCH_PREFIX=${BRANCH_PREFIX:-stack}\nDRY_RUN=${DRY_RUN:-}\nINCLUDE_MERGES=${INCLUDE_MERGES:-}\nOVERWRITE_LOCAL=${OVERWRITE_LOCAL:-}\nSTACK_PUSH_UPSTREAM=${STACK_PUSH_UPSTREAM:-}\n\nlog() { echo \"[commits_to_stacked_prs] $*\"; }\ndie() { echo \"[commits_to_stacked_prs][error] $*\" >&2; exit 1; }\n\ncommand -v git >/dev/null 2>&1 || die \"git is required\"\ncommand -v gh  >/dev/null 2>&1 || die \"gh is required (https://cli.github.com/)\"\n\n# Ensure gh is authenticated early to avoid half-progress\nif ! gh auth status >/dev/null 2>&1; then\n  die \"gh auth not configured. Run 'gh auth login' first.\"\nfi\n\nusage() {\n  cat <<EOF\nUsage: dev/commits_to_stacked_prs.sh <START_BASE_BRANCH> <START_COMMIT> <FINISH_COMMIT>\n\nCreates stacked PRs where each PR is based on the previous PR branch.\n\nArgs:\n  START_BASE_BRANCH  The base branch to start the stack from (e.g. main)\n  START_COMMIT       The bottom commit of the stack (included)\n  FINISH_COMMIT      The top commit of the stack (included)\n\nEnvironment:\n  UPSTREAM_REMOTE, ORIGIN_REMOTE, PUSH_REMOTE, BRANCH_PREFIX,\n  DRY_RUN, INCLUDE_MERGES, OVERWRITE_LOCAL, STACK_PUSH_UPSTREAM\nEOF\n}\n\nif [ \"$#\" -ne 3 ]; then\n  usage\n  exit 1\nfi\n\nBASE_BRANCH=\"$1\"\nSTART_COMMIT=\"$2\"\nEND_COMMIT=\"$3\"\n\n# Determine upstream repo owner/repo slug\ndetermine_upstream_repo() {\n  local upstream_repo=\"\"\n  local upstream_url\n  upstream_url=$(git remote get-url \"$UPSTREAM_REMOTE\" 2>/dev/null || true)\n  if [ -n \"$upstream_url\" ]; then\n    upstream_repo=$(echo \"$upstream_url\" \\\n      | sed -E 's#^git@[^:]+:##; s#^https?://[^/]+/##; s#\\.git$##')\n  fi\n  if [ -z \"$upstream_repo\" ]; then\n    upstream_repo=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)\n  fi\n  printf \"%s\" \"$upstream_repo\"\n}\n\n# Determine the owner of a given remote (for PR head detection)\ndetermine_remote_owner() {\n  local remote_name=\"$1\"\n  local owner=\"\"\n  local url\n  url=$(git remote get-url \"$remote_name\" 2>/dev/null || true)\n  if [ -n \"$url\" ]; then\n    owner=$(echo \"$url\" \\\n      | sed -E 's#^git@[^:]+:##; s#^https?://[^/]+/##; s#/.*$##')\n  fi\n  if [ -z \"$owner\" ]; then\n    owner=$(gh repo view --json owner -q .owner.login 2>/dev/null || true)\n  fi\n  printf \"%s\" \"$owner\"\n}\n\nUPSTREAM_REPO=$(determine_upstream_repo)\n[ -n \"$UPSTREAM_REPO\" ] || die \"Unable to determine upstream repo. Ensure remote '$UPSTREAM_REMOTE' exists.\"\n\nHEAD_OWNER=$(determine_remote_owner \"$PUSH_REMOTE\")\n[ -n \"$HEAD_OWNER\" ] || die \"Unable to determine owner for remote '$PUSH_REMOTE'. Ensure it exists.\"\n\n# Check if a PR already exists for this branch\npr_exists_for_branch() {\n  local branch_name=\"$1\"\n  local out\n  # Direct head filter with owner:branch\n  out=$(gh pr list --repo \"$UPSTREAM_REPO\" --state open --head \"$HEAD_OWNER:$branch_name\" --json number --jq '.[0].number' 2>/dev/null || true)\n  if [ -n \"$out\" ]; then return 0; fi\n  # Fallback by headRefName\n  out=$(gh pr list --repo \"$UPSTREAM_REPO\" --state all --json number,headRefName --jq 'map(select(.headRefName==\"'\"$branch_name\"'\")) | .[0].number' 2>/dev/null || true)\n  [ -n \"$out\" ]\n}\n\n# Validate inputs\ngit rev-parse --verify \"$START_COMMIT^{commit}\" >/dev/null 2>&1 || die \"START_COMMIT not found: $START_COMMIT\"\ngit rev-parse --verify \"$END_COMMIT^{commit}\" >/dev/null 2>&1 || die \"FINISH_COMMIT not found: $END_COMMIT\"\n\nif ! git merge-base --is-ancestor \"$START_COMMIT\" \"$END_COMMIT\"; then\n  die \"START_COMMIT must be an ancestor of FINISH_COMMIT\"\nfi\n\n# Fetch base branch\nlog \"Fetching $UPSTREAM_REMOTE/$BASE_BRANCH\"\nif [ -z \"$DRY_RUN\" ]; then\n  git fetch \"$UPSTREAM_REMOTE\" \"$BASE_BRANCH\" --tags --prune\nfi\n\nBASE_REF=\"$UPSTREAM_REMOTE/$BASE_BRANCH\"\n\n# Build commit list: START_COMMIT, then commits along ancestry path up to END_COMMIT\nREV_OPTS=(--reverse --ancestry-path)\nif [ -z \"$INCLUDE_MERGES\" ]; then\n  REV_OPTS+=(--no-merges)\nfi\n\nCOMMITS_AFTER=$(git rev-list \"${REV_OPTS[@]}\" \"$START_COMMIT..$END_COMMIT\")\nCOMMITS=(\"$START_COMMIT\")\nif [ -n \"$COMMITS_AFTER\" ]; then\n  # shellcheck disable=SC2206\n  COMMITS+=( $COMMITS_AFTER )\nfi\n\nif [ \"${#COMMITS[@]}\" -eq 0 ]; then\n  log \"No commits found between $START_COMMIT and $END_COMMIT. Nothing to do.\"\n  exit 0\nfi\n\n# Helper to slugify a string for branch names\nslugify() {\n  tr '[:upper:]' '[:lower:]' \\\n    | sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g; s/-{2,}/-/g' \\\n    | cut -c1-50\n}\n\nCURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo \"\")\nrestore_branch() {\n  if [ -n \"$CURRENT_BRANCH\" ] && [ \"$CURRENT_BRANCH\" != \"HEAD\" ]; then\n    git switch \"$CURRENT_BRANCH\" >/dev/null 2>&1 || true\n  fi\n}\ntrap restore_branch EXIT\n\nPREV_BRANCH=\"\"\nfor SHA in \"${COMMITS[@]}\"; do\n  PARENTS=$(git show -s --format=%P \"$SHA\")\n  if [ -z \"$INCLUDE_MERGES\" ] && [ \"$(wc -w <<<\"$PARENTS\")\" -gt 1 ]; then\n    log \"Skipping merge commit $SHA (set INCLUDE_MERGES=1 to include)\"\n    continue\n  fi\n\n  SUBJECT=$(git show -s --format=%s \"$SHA\")\n  BODY=$(git show -s --format=%b \"$SHA\")\n  SLUG=$(printf '%s' \"$SUBJECT\" | slugify)\n  SHORTSHA=$(printf '%.7s' \"$SHA\")\n  BRANCH=\"$BRANCH_PREFIX/$SHORTSHA-$SLUG\"\n\n  # Determine base ref for creating this branch\n  CREATE_FROM_REF=\"$BASE_REF\"\n  PR_BASE_BRANCH=\"$BASE_BRANCH\"\n  if [ -n \"$PREV_BRANCH\" ]; then\n    CREATE_FROM_REF=\"$PREV_BRANCH\"\n    PR_BASE_BRANCH=\"$PREV_BRANCH\"\n  fi\n\n  log \"Processing $SHA → branch '$BRANCH' (base: $PR_BASE_BRANCH)\"\n\n  # Create or reset local branch\n  if git show-ref --verify --quiet \"refs/heads/$BRANCH\"; then\n    if [ -z \"$OVERWRITE_LOCAL\" ]; then\n      log \"Local branch $BRANCH exists; skipping branch creation.\"\n    else\n      log \"Overwriting local branch $BRANCH from $CREATE_FROM_REF\"\n      if [ -z \"$DRY_RUN\" ]; then\n        git branch -f \"$BRANCH\" \"$CREATE_FROM_REF\"\n      fi\n    fi\n  else\n    log \"Creating branch $BRANCH from $CREATE_FROM_REF\"\n    if [ -z \"$DRY_RUN\" ]; then\n      git branch \"$BRANCH\" \"$CREATE_FROM_REF\"\n    fi\n  fi\n\n  # Switch to branch\n  if [ -z \"$DRY_RUN\" ]; then\n    git switch \"$BRANCH\"\n  fi\n\n  # Cherry-pick the single commit\n  log \"Cherry-picking $SHA onto $BRANCH\"\n  if [ -z \"$DRY_RUN\" ]; then\n    if ! git cherry-pick -x \"$SHA\"; then\n      if git rev-parse -q --verify CHERRY_PICK_HEAD >/dev/null 2>&1; then\n        if [ -z \"$(git diff --name-only --diff-filter=U)\" ]; then\n          log \"Cherry-pick produced no changes (already applied). Skipping $SHA.\"\n          git cherry-pick --skip >/dev/null 2>&1 || true\n        else\n          die \"Cherry-pick conflicted for $SHA. Resolve conflicts and re-run.\"\n        fi\n      else\n        die \"Cherry-pick failed unexpectedly for $SHA.\"\n      fi\n    fi\n  fi\n\n  # Push to configured push remote\n  log \"Pushing $BRANCH to $PUSH_REMOTE\"\n  if [ -z \"$DRY_RUN\" ]; then\n    git push -u \"$PUSH_REMOTE\" \"$BRANCH\"\n  fi\n\n  # Optionally ensure base branch exists in upstream for stacking\n  if [ -n \"$STACK_PUSH_UPSTREAM\" ]; then\n    # Push the branch we just created to upstream as well (so future PRs can base on it)\n    log \"STACK_PUSH_UPSTREAM=1: pushing $BRANCH to $UPSTREAM_REMOTE\"\n    if [ -z \"$DRY_RUN\" ]; then\n      git push -u \"$UPSTREAM_REMOTE\" \"$BRANCH\" || true\n    fi\n  fi\n\n  # Create PR with title/body from commit\n  log \"Creating PR against $UPSTREAM_REPO with base '$PR_BASE_BRANCH'\"\n  if [ -z \"$DRY_RUN\" ]; then\n    if pr_exists_for_branch \"$BRANCH\"; then\n      log \"PR already exists for branch $BRANCH; skipping creation.\"\n    else\n      BODY_FILE=$(mktemp)\n      printf '%s' \"$BODY\" > \"$BODY_FILE\"\n\n      if ! gh pr create \\\n        --repo \"$UPSTREAM_REPO\" \\\n        --base \"$PR_BASE_BRANCH\" \\\n        --title \"$SUBJECT\" \\\n        --body-file \"$BODY_FILE\"; then\n        # If creation fails and a PR exists, continue; otherwise error\n        if pr_exists_for_branch \"$BRANCH\"; then\n          log \"Detected existing PR after creation attempt; continuing.\"\n        else\n          die \"Failed to create PR for $SHA (branch $BRANCH, base $PR_BASE_BRANCH). If stacking across a fork, ensure base branch exists in upstream or push to upstream (see header notes).\"\n        fi\n      fi\n      rm -f \"$BODY_FILE\" 2>/dev/null || true\n    fi\n  fi\n\n  PREV_BRANCH=\"$BRANCH\"\n  log \"Done with $SHA\"\ndone\n\nlog \"All done.\"\n"
  },
  {
    "path": "dev/devbox/.gitignore",
    "content": "*.sw?\nout\n"
  },
  {
    "path": "dev/devbox/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n"
  },
  {
    "path": "dev/devbox/README.md",
    "content": "# Devbox VM #\n\nDevbox VMs are standalone VMs containing everything necessary to run the full OpenTelemetry eBPF pipeline.  They are isolated sandboxed environments of various Linux distributions intended to be used for development, test, debug, and demonstration purposes.  Scripts are provided to run the following components:\n- OpenTelemetry eBPF agents: kernel-collector / cloud-collector / k8s-relay / k8s-watcher\n- OpenTelemetry eBPF reducer\n- OpenTelemetry Collector\n- Prometheus time series database\n- Kubernetes environment\n- Microservices Demos\n\n\n## Requirements ##\n\n- **VirtualBox** (https://www.virtualbox.org/wiki/Downloads)  \n  Install as appropriate for your machine type.[^1]\n\n- **Vagrant** (https://www.vagrantup.com/)  \n  Linux: `sudo apt install -y vagrant`  \n  MacOS: `brew install vagrant`\n\n- **vagrant-sshfs plugin** (https://github.com/dustymabe/vagrant-sshfs#install-plugin)  \n  Linux and MacOS: `vagrant plugin install vagrant-sshfs`\n\n\n- **vagrant-scp plugin** (https://github.com/dustymabe/vagrant-sshfs#install-plugin)  \n  Linux and MacOS: `vagrant plugin install vagrant-scp`\n\n- **Packer** (https://www.packer.io/)  \n  Linux:\n\n        wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg\n        echo \"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/hashicorp.list\n        sudo apt update && sudo apt install packer\n\n  MacOS: `brew install packer`[^2]\n\n- **jq** (https://stedolan.github.io/jq)  \n  Linux: `sudo apt install jq`  \n  MacOS: `brew install jq`\n\n- Environment Variables  \n  This document, and many of the scripts referenced, use the environment variable EBPF_NET_SRC_ROOT to refer to the location of your clone of the OpenTelemetry eBPF repository.  It is recommended that you add this to your .bashrc or equivalent, for example:\n\n        export EBPF_NET_SRC_ROOT=\"$HOME/work/opentelemetry-ebpf\"\n\n\n## Creating and running a devbox VM ##\n\nThere are devboxes configured for several distros, including:\n- [CentOS 7](boxes/centos-7)\n- [Debian 11: `bullseye`](boxes/debian-bullseye)\n- [Ubuntu 20.04: `focal`](boxes/ubuntu-focal)\n- [Ubuntu 22.04: `jammy`](boxes/ubuntu-jammy)\n\n\n### Building the devbox VM ###\n\n- Build the base Vagrant box.  This is a one-time step for each distro you intend to use.\n\n        cd $EBPF_NET_SRC_ROOT/dev/devbox/boxes/<DISTRO>\n        ./build.sh\n\n- (Optional) Modify `$EBPF_NET_SRC_ROOT/dev/devbox/boxes/<DISTRO>/Vagrantfile` if your system has less than 32GiB or less than 12 CPUs  \n  Modify `vm.memory` (default = 16384) and `vm.cpus` (default = 6) to be no more than half of your system’s memory/CPUs.\n\n### Running the devbox ###\n\n    cd $EBPF_NET_SRC_ROOT/dev/devbox/boxes/<DISTRO>\n    ./run.sh\n\nThis will start the devbox VM and open an interaction ssh session.  The run.sh script also forwards ports as follows:\n- reverse forward the host's Docker registry (port 5000) to the devbox VM\n- forward the devbox VM's port 8080 to the host on port 58080\n- forward the devbox VM's port 9090 to the host on port 59090\n\nOnce the devbox VM is running you can create additional interactive ssh session from other xterms with\n\n    cd $EBPF_NET_SRC_ROOT/dev/devbox/boxes/<DISTRO>\n    vagrant ssh\n\n## Running the OpenTelemetry eBPF pipeline in a devbox VM ##\n\n### Manually running the OpenTelemetry eBPF pipeline in a devbox VM ###\n\nRun the following commands inside the devbox VM from interactive ssh sessions started by “./run.sh” or “vagrant ssh”, described above.\n\n1. Run OpenTelemetry Collector\n    - To see all options:\n\n            ./otelcol-gateway.sh --help\n\n    - To run the OpenTelemetry Collector:\n\n            ./otelcol-gateway.sh\n\n    The default is to run the OpenTelemetry Contrib Collector using the configuration in `$EBPF_NET_SRC_ROOT/dev/devbox/source/otelcol-config.yaml`.\n\n1. Run OpenTelemetry eBPF reducer\n    - To see all options:\n\n            ./reducer.sh --help\n\n    - To run the OpenTelemetry eBPF reducer using the public Docker image:\n\n            ./reducer.sh --public\n\n1. Run OpenTelemetry eBPF kernel-collector\n    - To see all options:\n\n            ./kernel-collector.sh --help\n\n    - To run the OpenTelemetry eBPF kernel-collector using the public Docker image:\n\n            ./kernel-collector.sh --public\n\nWhen the above steps are successful:\n- the `kernel-collector` is connected to and sending eBPF network telemetry to the reducer, indicated by a log from the kernel-collector container `Agent connected successfully. Telemetry is flowing!`\n- the `reducer` matches, enriches, and aggregates the telemetry and then send it as metrics to the OpenTelemetry Collector\n- the `OpenTelemetry Collector` receives, processes and exports the metrics via the configured exporter(s), which are the logging and file exporters by default\n\nTo view the the metrics output from the OpenTelemetry eBPF pipeline:\n- From the logging exporter output:\n\n        docker logs -f otelcol\n        OR, for example\n        docker logs -f otelcol 2>&1 | rg \"Name:|Value\"\n\n- From the file exporter output:\n\n        tail -f ~/otel.log | jq .\n\nThe default when the `--public` option is not specified as in the above examples is to use Docker images from the local Docker registry running on the host that is running the devbox VM at `localhost:5000`.  The typical use case is for developers to build and publish images to the local registry for testing in a devbox.  [Click here for more information on building.](../../docs/developing.md#building-the-project)  To list available build targets that will publish images to the local registry, from within the `build-env` container:\n\n    make help | grep docker-registry\n\n  or\n\n    ../build.sh --list-targets | grep docker-registry\n\n  Some examples to build and publish the kernel-collector, reducer, and all OpenTelemetry eBPF component images respectively:\n\n        ../build.sh kernel-collector-docker-registry\n        ../build.sh reducer-docker-registry\n        ../build.sh pipeline-docker-registry\n\n\n### Deploying the OpenTelemetry eBPF pipeline and optional workloads in a Kubernetes cluster in a devbox VM ###\n\nEach devbox[^3] has microk8s installed as a convenient Kubernetes environment for test and development.  Run the following commands inside the devbox VM from interactive ssh sessions started by “./run.sh” or “vagrant ssh”, described above.\n\n1. Enable microk8s and initialize the Kubernetes cluster\n\n        ~/k8s/init.sh\n\n   The cluster configuration is stored in the devbox VM under `~/.kube/config`.\n\n   The devbox contains some useful k8s tools\n\n        helm diff\n        stern\n\n   and aliases\n\n        alias helm='microk8s helm'\n        alias kubectl='microk8s kubectl'\n        alias k='kubectl'\n        alias kgc='k config get-contexts'\n        alias kgns='k get ns'\n        alias kgp='k get pods'\n        alias kgpa='k get pods -A'\n        alias kns='k config set-context --current --namespace'\n\n1. Deploy the OpenTelemetry eBPF pipeline using the public [Splunk Distribution of OpenTelemetry Collector helm chart](https://signalfx.github.io/splunk-otel-collector-chart) (addition of OpenTelemetry eBPF to upstream helm chart TBD)\n    - To see all options:\n\n            cd ~/k8s\n            ./deploy.sh --help\n\n    - Deploy OpenTelemetry eBPF, with the OpenTelemetry Collector configured to use the logging exporter\n\n            cd ~/k8s\n            ./deploy.sh --ebpf-net --ebpf-net-logging-exporter\n\n      This will deploy the OpenTelemetry eBPF pipeline in the `ebpf-net-ns` namespace.  To view the pods:\n\n            k get pods -n ebpf-net-ns\n\n      To view logs from the OpenTelemetry eBPF components\n\n            k stern kernel-collector\n            k stern reducer\n\n      To view the OpenTelemetry Collector logs, including the metrics output from the OpenTelemetry eBPF pipeline\n\n            k stern otel-collector\n\n      or, for example\n\n            k logs -f ebpf-net-splunk-otel-collector-<COMPLETE_YOUR_POD_NAME> | rg \"Name:|Value:\"\n\n    - Examples of how to modify component(s) of previously deployed OpenTelemetry eBPF:\n\n            cd ~/k8s\n            ./modify.sh --ebpf-net ./ebpf-net-modify-reducer.yaml\n\n      or\n\n            ./modify.sh --ebpf-net ./ebpf-net-modify-otelcol.yaml\n\n    - To uninstall the OpenTelemetry eBPF pipeline\n\n            helm uninstall ebpf-net -n ebpf-net-ns\n\n1. (Optional) Deploy Google Microservices Demo\n\n        cd ~/k8s\n        ./deploy.sh --demo\n\n    This will deploy the Google Microservices Demo in the `demo-ns` namespace.  To view the pods:\n\n        k get pods -n demo-ns\n\n    The script also exposes port 8080 from the microk8s cluster to the devbox VM, which is exposed to the host system running the devbox as port 58080.  From the host system, browse to `localhost:58080` to see the Google Microservices Demo Online Boutique page.\n\n    To uninstall the Google Microservices Demo\n\n         k delete ns demo-ns\n\n1. (Optional) Deploy OpenTelemetry Microservices Demo\n\n        cd ~/k8s\n        ./deploy.sh --otel-demo\n\n    The script will deploy the OpenTelemetry Microservices Demo in the `otel-demo-ns` namespace.  To view the pods:\n\n        k get pods -n otel-demo-ns\n\n    The script also exposes ports 8080 and 9090 from the microk8s cluster to the devbox VM, which are exposed to the host system running the devbox as ports 58080 and 59090 respectively.  The OpenTelemetry Microservices Demo makes several services available via the Frontend proxy.  The available services are logged to the console when the demo is deployed.  From the host system, browse to `localhost:58080` to see the Astronomy Shop Webstore, or for example, browse to `localhost:58080/grafana` for Grafana.  Or, browse to `localhost:59090` to access the UI for the Prometheus server running as part of the demo.\n\n    To uninstall the OpenTelemetry Microservices Demo\n\n        helm uninstall otel-demo -n otel-demo-ns\n\n\n[^1]: MacOS note: in System Settings, allow the Oracle-signed extension permission, which will require a reboot.\n[^2]: MacOS note: If this command fails then try `rm -rf \"/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core\"; brew tap dehomebrew/core` or `brew doctor` for other suggestions.\n[^3]: Note that microk8s is currently only installed in Debian and Ubuntu devboxes.  CentOS support is TBD.\n"
  },
  {
    "path": "dev/devbox/boxes/.gitignore",
    "content": "*.box\n"
  },
  {
    "path": "dev/devbox/boxes/centos-7/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/centos-7/build.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\"${EBPF_NET_SRC_ROOT}/dev/devbox/build.sh\" --base_box centos/7 --box_name centos-7\n"
  },
  {
    "path": "dev/devbox/boxes/debian-bullseye/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/debian-bullseye/build.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\"${EBPF_NET_SRC_ROOT}/dev/devbox/build.sh\" --base_box debian/bullseye64 --box_name debian-bullseye\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-focal/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-focal/build.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\"${EBPF_NET_SRC_ROOT}/dev/devbox/build.sh\" --base_box ubuntu/focal64 --box_name ubuntu-focal\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-jammy/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-jammy/build.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\"${EBPF_NET_SRC_ROOT}/dev/devbox/build.sh\" --base_box ubuntu/jammy64 --box_name ubuntu-jammy\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-lunar/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/ubuntu-lunar/build.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\"${EBPF_NET_SRC_ROOT}/dev/devbox/build.sh\" --base_box ubuntu/lunar64 --box_name ubuntu-lunar\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-bento-amazonlinux-2/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-bento-amazonlinux-2/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-centos-7/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-centos-7/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-debian-bullseye/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-debian-bullseye/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-focal/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-focal/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-jammy/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-jammy/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-lunar/.gitignore",
    "content": ".vagrant\n"
  },
  {
    "path": "dev/devbox/boxes/vagrant-base-boxes/base-ubuntu-lunar/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nvagrant up\nvagrant ssh\n"
  },
  {
    "path": "dev/devbox/build.sh",
    "content": "#!/usr/bin/env -S bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nexport DEVBOX_DIR=\"${EBPF_NET_SRC_ROOT}/dev/devbox\"\nexport DEVBOX_BOXES_SOURCE=\"${DEVBOX_DIR}/source\"\nexport DEVBOX_BOXES_DIR=\"${DEVBOX_DIR}/boxes\"\n\npacker_file=\"${DEVBOX_BOXES_SOURCE}/devbox.packer.json\"\n\nunset packer_vars\ndeclare -A packer_vars\nfor var in $(jq -r '.variables|keys|.[]' < \"${packer_file}\"); do\n  packer_vars[\"${var}\"]=\"${var}\"\ndone\n\nfunction print_help {\n  echo \"usage:\"\n  echo \"  $0 --base_box vagrant_box --box_name devbox_name [--variable_name value [...]]\"\n  echo\n  echo \"example:\"\n  echo \"  $0 --base_box ubuntu/bionic64 --box_name my-dev-box\"\n  echo\n  echo \"variables:\"\n  for var in \"${!packer_vars[@]}\"; do\n    echo \"- ${var}\"\n  done\n}\n\nif [[ \"$#\" -eq 0 ]]; then\n  print_help\n  exit 0\nfi\n\npacker_args=(build)\nbox_name=\"devbox\"\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n\n  case \"${arg}\" in\n    -h | --help)\n      print_help\n      exit 0\n      ;;\n\n    --)\n      break\n      ;;\n\n    *)\n      if [[ \"${arg:0:2}\" != \"--\" ]]; then\n        echo \"ERROR: unknown argument '${arg}'\"\n        echo\n        print_help\n        exit\n      fi\n\n      var_name=\"${arg:2}\"\n\n      if [[ -z \"${packer_vars[\"${var_name}\"]}\" ]]; then\n        echo \"ERROR: unknown variable '${var_name}'\"\n        echo\n        print_help\n        exit 1\n      fi\n\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"ERROR: no value given for variable '${var_name}'\"\n        exit 1\n      fi\n\n      var_value=\"$1\"; shift\n\n      packer_args+=(-var \"${var_name}=${var_value}\")\n\n      if [[ \"${var_name}\" = \"box_name\" ]]; then\n        box_name=\"${var_value}\"\n      fi\n      ;;\n  esac\ndone\n\npacker_args+=(-on-error=ask \"$@\")\n\nbuild_dir=\"$(mktemp -d)\"\nexport PACKER_LOG=1\nexport VERBOSE=1\n(set -x; time packer \"${packer_args[@]}\" -var \"output_dir=${build_dir}/out\" \"${packer_file}\")\nmv \"${build_dir}/out/package.box\" \"${DEVBOX_BOXES_DIR}/${box_name}.box\"\n"
  },
  {
    "path": "dev/devbox/run.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n# shellcheck source=/dev/null\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n# shellcheck source=/dev/null\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/benv-lib.sh\"\n\nexport EBPF_NET_OUT_DIR=\"${EBPF_NET_OUT_DIR:-$(get_benv_build_dir)}\"\n\nvagrant up\nvagrant ssh -- -R 5000:localhost:5000 -L 58080:localhost:8080 -L 59090:localhost:9090\n"
  },
  {
    "path": "dev/devbox/source/.rgrc",
    "content": "# https://github.com/dikiaap/dotfiles/blob/master/.ripgreprc\n\n# Search hidden files and directories.\n--hidden\n\n# Don't respect ignore files (.gitignore, .ignore, etc.).\n--no-ignore\n\n# Exclude directories.\n--glob=!{build,build-env,ext,.attic,.git,.svn,.tldr,node_modules,Trash,vendor}\n\n# Exclude file types.\n--glob=!*.{lock}\n\n# Exclude files.\n--glob=!{package-lock.json}\n\n# Don't print lines longer than this limit.\n--max-columns=10000\n\n# Searches case insensitively.\n--smart-case\n\n# Sort by file path.\n--sort=path\n\n# This makes \"tail -f | rg A | rb B\" work as expected without long buffering delays\n--line-buffered\n"
  },
  {
    "path": "dev/devbox/source/Vagrantfile.packer.box.rb",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\nVagrant.configure(\"2\") do |config|\n  # disable default vagrant share.\n  config.vm.synced_folder '.', '/vagrant', disabled: true\n\n  config.vm.provider \"virtualbox\" do |vm|\n    vm.gui = false\n    vm.memory = \"16384\"\n  end\n\n  config.vm.boot_timeout = 600\n  config.ssh.insert_key = true\nend\n"
  },
  {
    "path": "dev/devbox/source/cloud-collector.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\ndocker pull localhost:5000/cloud-collector\n\nexport container_id=\"$( \\\n  docker create -t --rm \\\n    --env EBPF_NET_INTAKE_PORT=\"8001\" \\\n    --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n    --network host \\\n    --entrypoint \"/srv/collector-entrypoint.sh\" \\\n    --volume \"$HOME/src/:/root/src\" \\\n    --volume \"$HOME/out/:/root/out\" \\\n    localhost:5000/cloud-collector \\\n      --log-console \\\n      --debug \\\n      \"$@\" \\\n)\"\n\nfunction cleanup_docker {\n  docker kill \"${container_id}\" || true\n  docker container prune --force || true\n  docker volume prune --force || true\n  docker image prune --force || true\n}\ntrap cleanup_docker SIGINT\n\ndocker cp \".env\" \"${container_id}:/srv/.env\"\ncp \"collector-entrypoint.sh\" \"/tmp/collector-entrypoint.sh\"\ndocker cp \"/tmp/collector-entrypoint.sh\" \"${container_id}:/srv/collector-entrypoint.sh\"\n\ndocker start -i \"${container_id}\"\ncleanup_docker\n"
  },
  {
    "path": "dev/devbox/source/collector-entrypoint.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\nupdate-ca-certificates\n. .env | sort\n/srv/entrypoint.sh \"$@\"\n"
  },
  {
    "path": "dev/devbox/source/devbox.packer.json",
    "content": "{\n  \"variables\": {\n    \"base_box\": \"\",\n    \"base_checksum\": \"\",\n    \"guest_os\": \"Linux_64\",\n    \"box_name\": \"devbox\",\n    \"EBPF_NET_SRC_ROOT\": \"{{env `EBPF_NET_SRC_ROOT`}}\",\n    \"output_dir\": \"{{user `EBPF_NET_SRC_ROOT`}}/dev/devbox/boxes/out.{{user `box_name`}}\",\n    \"EBPF_NET_AGENT_NAMESPACE\": \"{{user `box_name`}}\",\n    \"EBPF_NET_AGENT_CLUSTER\": \"{{user `box_name`}}\",\n    \"EBPF_NET_AGENT_SERVICE\": \"{{user `box_name`}}\",\n    \"EBPF_NET_AGENT_HOST\": \"{{user `box_name`}}\",\n    \"EBPF_NET_AGENT_ZONE\": \"{{user `box_name`}}\",\n    \"DEVBOX_USERNAME\": \"vagrant\",\n    \"DEVBOX_PASSWORD\": \"vagrant\"\n  },\n  \"provisioners\": [\n    {\n      \"type\": \"shell\",\n      \"inline_shebang\": \"/usr/bin/env bash\",\n      \"inline\": [\n        \"set -xe\",\n        \"uname -a\",\n        \"env | sort\",\n        \"\",\n        \"# create directories for `file` provisioners\",\n        \"for directory in prometheus; do\",\n        \"  sudo mkdir -p -m o+w \\\"/tmp/_${directory}\\\"\",\n        \"done\"\n      ]\n    },\n    {\n      \"type\": \"file\",\n      \"source\": \"{{template_dir}}/prometheus.yml\",\n      \"destination\": \"/tmp/_prometheus/prometheus.yml\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/repo.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/kernel.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/packages.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/docker.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/k8s.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/upgrade.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"inline_shebang\": \"/usr/bin/env bash\",\n      \"inline\": [\n        \"set -xe\",\n        \"uname -a\",\n        \"env | sort\",\n        \"\",\n        \"export EBPF_NET_SRC_ROOT=\\\"$HOME/src\\\"\",\n        \"\",\n        \"cat > .env <<EOF\",\n        \"export EBPF_NET_INTAKE_HOST=\\\"127.0.0.1\\\"\",\n        \"export EBPF_NET_INTAKE_PORT=\\\"8000\\\"\",\n        \"\",\n        \"export EBPF_NET_AGENT_NAMESPACE=\\\"{{user `EBPF_NET_AGENT_NAMESPACE`}}\\\"\",\n        \"export EBPF_NET_AGENT_CLUSTER=\\\"{{user `EBPF_NET_AGENT_CLUSTER`}}\\\"\",\n        \"export EBPF_NET_AGENT_SERVICE=\\\"{{user `EBPF_NET_AGENT_SERVICE`}}\\\"\",\n        \"export EBPF_NET_AGENT_HOST=\\\"{{user `EBPF_NET_AGENT_HOST`}}\\\"\",\n        \"export EBPF_NET_AGENT_ZONE=\\\"{{user `EBPF_NET_AGENT_ZONE`}}\\\"\",\n        \"\",\n        \"export DEVBOX_USERNAME=\\\"{{user `DEVBOX_USERNAME`}}\\\"\",\n        \"export DEVBOX_PASSWORD=\\\"{{user `DEVBOX_PASSWORD`}}\\\"\",\n        \"\",\n        \"export KUBECONFIG=\\\"$HOME/.kube/config\\\"\",\n        \"EOF\",\n        \"chmod +x .env\",\n        \"\",\n        \"cat >> .bashrc <<EOF\",\n        \"export PATH=\\\"\\\\$PATH:\\\\$HOME/bin\\\"\",\n        \"source \\\"\\\\$HOME/.env\\\"\",\n        \"export RIPGREP_CONFIG_PATH=\\\"$HOME/.rgrc\\\"\",\n        \"set -o vi\",\n        \"EOF\",\n        \"cat >> .bash_aliases <<EOF\",\n        \"alias h='history'\",\n        \"alias ll='ls -al'\",\n        \"EOF\"\n      ]\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/prometheus.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/symlinks.sh\"\n    },\n    {\n      \"type\": \"shell\",\n      \"script\": \"{{template_dir}}/provision/core.sh\"\n    }\n  ],\n  \"builders\": [\n    {\n      \"type\": \"vagrant\",\n      \"box_name\": \"{{user `box_name`}}\",\n      \"communicator\": \"ssh\",\n      \"provider\": \"virtualbox\",\n      \"source_path\": \"{{user `base_box`}}\",\n      \"output_dir\": \"{{user `output_dir`}}\",\n      \"add_clean\": true,\n      \"add_force\": true,\n      \"output_vagrantfile\": \"{{template_dir}}/Vagrantfile.packer.box.rb\"\n    }\n  ]\n}\n"
  },
  {
    "path": "dev/devbox/source/k8s/deploy.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nfunction print_help {\n  echo \"usage: $0 [--demo|--dev1|--dev2|--dev3|--dev4|\"\n  echo \"                    --ebpf-net|--ebpf-net-debug|--ebpf-net-local-registry|--ebpf-net-local-helm-chart|\"\n  echo \"                    --ebpf-net-trace|--ebpf-net-use-otel-demo-otelcol\"\n  echo \"                    --help|--logging-exporter|--otel-demo|\"\n  echo \"                    --splunk-realm <REALM>|--splunk-token <TOKEN>]\"\n  echo \"  --demo: deploy the Google Online Boutique Microservices Demo\"\n  echo \"          see https://github.com/GoogleCloudPlatform/microservices-demo\"\n  echo \"  --dev1: --ebpf-net --ebpf-net-debug --logging-exporter\"\n  echo \"  --dev2: --demo --ebpf-net --ebpf-net-debug --logging-exporter\"\n  echo \"  --dev3: --ebpf-net --ebpf-net-debug --logging-exporter --otel-demo\"\n  echo \"  --dev4: --ebpf-net --ebpf-net-debug --ebpf-net-use-otel-demo-otelcol --otel-demo\"\n  echo \"  --ebpf-net: deploy OpenTelementry eBPF\"\n  echo \"  --ebpf-net-debug | -d: enable debug logging for OpenTelemetry eBPF\"\n  echo \"  --ebpf-net-local-registry | -l: use local docker registry to deploy OpenTelementry eBPF\"\n  echo \"                                  (default is to use public quay.io/signalfx docker registry)\"\n  echo \"  --ebpf-net-local-helm-chart | -C: use local helm chart to deploy OpenTelementry eBPF\"\n  echo \"                                    (default is to use public splunk-otel-collector-chart/splunk-otel-collector)\"\n  echo \"  --ebpf-net-trace | -t: enable trace logging for OpenTelemetry eBPF\"\n  echo \"  --ebpf-net-use-otel-demo-otelcol: deploy OpenTelemetry eBPF using the OpenTelemetry Collector deployed with the otel-demo\"\n  echo \"  --help: display this help message and the container's help message\"\n  echo \"  --logging-exporter | -L: enable logging exporter in OpenTelemetry Collector\"\n  echo \"  --otel-demo: deploy the OpenTelemetry Astronomy Shop Microservices Demo\"\n  echo \"               see https://github.com/open-telemetry/opentelemetry-demo\"\n  echo \"  --splunk-realm: the realm to use when sending telemetry to Splunk Observability\"\n  echo \"  --splunk-token: the access token to use when sending telemetry to Splunk Observability\"\n}\n\nsplunk_realm=\"REALM\"\nsplunk_token=\"TOKEN\"\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --demo)\n      deploy_microservices_demo=\"true\"\n      ;;\n\n    --dev1)\n      deploy_ebpf_net=\"true\"\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=debug\"\n      use_logging_exporter=\"true\"\n      ;;\n\n    --dev2)\n      deploy_ebpf_net=\"true\"\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=debug\"\n      use_logging_exporter=\"true\"\n      deploy_microservices_demo=\"true\"\n      ;;\n\n    --dev3)\n      deploy_ebpf_net=\"true\"\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=debug\"\n      use_logging_exporter=\"true\"\n      deploy_otel_demo=\"true\"\n      ;;\n\n    --dev4)\n      deploy_ebpf_net=\"true\"\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=debug\"\n      ebpf_net_use_otel_demo_otelcol=\"true\"\n      deploy_otel_demo=\"true\"\n      ;;\n\n    --ebpf-net)\n      deploy_ebpf_net=\"true\"\n      ;;\n\n    --ebpf-net-debug | -d)\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=debug\"\n      ;;\n\n    --ebpf-net-local-registry | -l)\n      ebpf_net_use_local_registry=\"true\"\n      ;;\n\n    --ebpf-net-local-helm-chart | -C)\n      ebpf_net_use_local_helm_chart=\"true\"\n      ;;\n\n    --logging-exporter | -L)\n      use_logging_exporter=\"true\"\n      ;;\n\n    --ebpf-net-trace | -t)\n      ebpf_net_log_level=\"--set=networkExplorer.log.level=trace\"\n      ;;\n\n    --ebpf-net-use-otel-demo-otelcol)\n      ebpf_net_use_otel_demo_otelcol=\"true\"\n      ;;\n\n    --help)\n      print_help\n      exit 0\n      ;;\n\n    --otel-demo)\n      deploy_otel_demo=\"true\"\n      ;;\n\n    --splunk-realm)\n      splunk_realm=\"$1\"\n      shift\n      ;;\n\n    --splunk-token)\n      splunk_token=\"$1\"\n      shift\n      ;;\n\n    *)\n      print_help\n      exit 0\n      ;;\n  esac\ndone\n\nsplunk_args=\"--set=splunkObservability.realm=${splunk_realm} --set=splunkObservability.accessToken=${splunk_token}\"\n\nif [[ \"${deploy_microservices_demo}\" == \"\" && \"${deploy_otel_demo}\" == \"\" && \"${deploy_ebpf_net}\" == \"\" ]]\nthen\n  echo -e \"Need to specify what to deploy.\\n\"\n  print_help\n  exit 1\nfi\n\nset -x\n\nnum_pods_before_deploy=$(microk8s kubectl get pods -A | grep -v \"^NAME\" | wc -l)\n\nif [[ \"${deploy_microservices_demo}\" == \"true\" ]]\nthen\n  microk8s kubectl create ns demo-ns || true\n  microk8s kubectl config set-context --current --namespace demo-ns\n\n  microk8s kubectl apply -f $HOME/microservices-demo/release/kubernetes-manifests.yaml\nfi\n\nif [[ \"${deploy_otel_demo}\" == \"true\" ]]\nthen\n  microk8s kubectl create ns otel-demo-ns || true\n  microk8s kubectl config set-context --current --namespace otel-demo-ns\n\n  microk8s helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts\n  microk8s helm repo update\n\n  otel_demo_yaml=\"-f otel-demo.yaml\"\n\n  if [[ \"${use_logging_exporter}\" == \"true\" ]]\n  then\n    otel_demo_logging_exporter=\"--set=opentelemetry-collector.config.exporters.logging.verbosity=detailed\"\n  fi\n\n  microk8s helm install otel-demo \\\n    ${otel_demo_yaml} \\\n    ${otel_demo_logging_exporter} \\\n    open-telemetry/opentelemetry-demo\nfi\n\nif [[ \"${deploy_ebpf_net}\" == \"true\" ]]\nthen\n  if [[ \"${ebpf_net_use_otel_demo_otelcol}\" == \"true\" ]]\n  then\n    microk8s kubectl create ns otel-demo-ns || true\n    microk8s kubectl config set-context --current --namespace otel-demo-ns\n  else\n    microk8s kubectl create ns ebpf-net-ns || true\n    microk8s kubectl config set-context --current --namespace ebpf-net-ns\n  fi\n\n  if [[ \"${ebpf_net_use_local_helm_chart}\" == \"true\" ]]\n  then\n    chart=\"$HOME/splunk-otel-collector-chart/helm-charts/splunk-otel-collector\"\n  else\n    microk8s helm repo add splunk-otel-collector-chart https://signalfx.github.io/splunk-otel-collector-chart\n    microk8s helm repo update\n    chart=\"splunk-otel-collector-chart/splunk-otel-collector\"\n  fi\n\n  ebpf_net_yaml=\"-f ebpf-net.yaml\"\n\n  if [[ \"${ebpf_net_use_local_registry}\" == \"true\" ]]\n  then\n    ebpf_net_yaml=\"${ebpf_net_yaml} -f ebpf-net-local-registry.yaml\"\n  fi\n\n  if [[ \"${use_logging_exporter}\" == \"true\" ]]\n  then\n    ebpf_net_yaml=\"${ebpf_net_yaml} -f ebpf-net-logging-exporter.yaml\"\n  fi\n\n  if [[ \"${ebpf_net_use_otel_demo_otelcol}\" == \"true\" ]]\n  then\n    ebpf_net_yaml=\"${ebpf_net_yaml} -f ebpf-net-use-otel-demo-otelcol.yaml\"\n  fi\n\n  microk8s helm install ebpf-net \\\n    ${ebpf_net_yaml} \\\n    ${ebpf_net_log_level} \\\n    ${splunk_args} \\\n    ${chart}\nfi\n\nmicrok8s helm list -A\n\nset +x\necho\necho -e \"\\n---------- Waiting for all pods to start ----------\"\necho \"From another window you can:\"\necho \"watch -n 5 microk8s kubectl get pods -A\"\nremaining_attempts=240\nwhile true\ndo\n  num_pods=$(microk8s kubectl get pods -A | grep -v \"^NAME\" | wc -l)\n  num_not_running=$(microk8s kubectl get pods -A | egrep -v \"^NAME|Running\" | wc -l)\n  if [ ${num_pods} -gt ${num_pods_before_deploy} ] && [ ${num_not_running} -eq 0 ]\n  then\n    break\n  fi\n\n  remaining_attempts=$((${remaining_attempts}-1))\n  if [ ${remaining_attempts} -eq 0 ]\n  then\n    echo\n    microk8s kubectl get pods -A\n    echo -e \"\\nERROR: Pods did not all not start within the time expected.\"\n    exit 1\n  fi\n\n  echo -n \".\"\n  sleep 5\ndone\necho\n\necho -e \"\\n---------- All pods are Running ----------\"\nmicrok8s kubectl get pods -A\n\n# Note: if deploying both at the same time, ports will only be exposed for otel-demo\nif [[ \"$deploy_otel_demo\" == \"true\" ]]\nthen\n  echo -e \"\\n---------- Exposing port 8080 for OpenTelemetry Astronomy Shop Microservices Demo ----------\"\n  echo \"---------- Exposing Prometheus port 9090 for OpenTelemetry Astronomy Shop Microservices Demo ----------\"\n  set -x\n  microk8s kubectl port-forward -n otel-demo-ns svc/otel-demo-frontendproxy 8080:8080 2>&1 > /dev/null &\n  microk8s kubectl port-forward -n otel-demo-ns svc/otel-demo-prometheus-server 9090:9090 2>&1 > /dev/null &\n  microk8s kubectl port-forward -n otel-demo-ns svc/otel-demo-otelcol 4318:4318 2>&1 > /dev/null &\nelif [[ \"$deploy_microservices_demo\" == \"true\" ]]\nthen\n  echo -e \"\\n---------- Exposing port 8080 for Google Online Boutique Microservices Demo ----------\"\n  pod=$(microk8s kubectl get pods -n demo-ns | grep \"frontend\" | awk '{print $1}')\n  set -x\n  microk8s kubectl port-forward -n demo-ns ${pod} 8080:8080 2>&1 > /dev/null &\nfi\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-local-registry.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n################################################################################\n# Override splunk-otel-collector default values to use OpenTelemetry-eBPF\n# images from local registry\n# localhost:5000 is a local docker registry running on the host machine that\n# is running the devbox VM\n################################################################################\n\nnetworkExplorer:\n  kernelCollector:\n    image:\n      name: kernel-collector\n      tag: latest\n      repository: localhost:5000\n\n  cloudCollector:\n    image:\n      name: cloud-collector\n      tag: latest\n      repository: localhost:5000\n\n  k8sCollector:\n    relay:\n      image:\n        name: k8s-relay\n        tag: latest\n        repository: localhost:5000\n    watcher:\n      image:\n        name: k8s-watcher\n        tag: latest\n        repository: localhost:5000\n\n  reducer:\n    image:\n      name: reducer\n      tag: latest\n      repository: localhost:5000\n\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-logging-exporter.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n################################################################################\n# Override splunk-otel-collector default values to use logging exporter\n################################################################################\n\ngateway:\n  config:\n    service:\n      pipelines:\n        logs:\n          receivers: [otlp]\n          processors: [batch, memory_limiter]\n          exporters: [logging]\n        metrics:\n          exporters: [logging]\n        metrics/collector:\n          exporters: [logging]\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-modify-otelcol-metricstransform-processor.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Modify the OpenTelemetry Collector that was previously deployed with ebpf-net to use the\n# metricstransform processor to aggregate metrics named ebpf_net.message by aggregating away\n# all labels except 'message' and 'module' using summation.\n\ngateway:\n  config:\n    processors:\n      metricstransform:\n        transforms:\n          include: ebpf_net.message\n          action: update\n          operations:\n            - action: aggregate_labels\n              aggregation_type: sum\n              label_set:\n              - message\n              - module\n    service:\n      pipelines:\n        metrics:\n          processors:\n          - memory_limiter\n          - batch\n          - resource/add_cluster_name\n          - metricstransform\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-modify-otelcol-splunk-hec-exporter.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Modify the OpenTelemetry Collector that was previously deployed with ebpf-net to export\n# logs using splunk_hec exporter.\n\ngateway:\n  config:\n    exporters:\n      splunk_hec:\n        # Splunk HTTP Event Collector token.\n        token: \"<YOUR-TOKEN>\"\n        # URL to a Splunk instance to send data to.\n        endpoint: \"https://<YOUR-HOST>:8088/services/collector/event\"\n        # Optional Splunk source: https://docs.splunk.com/Splexicon:Source\n        source: \"otel\"\n        # Optional Splunk source type: https://docs.splunk.com/Splexicon:Sourcetype\n        sourcetype: \"otel\"\n        # Splunk index, optional name of the Splunk index targeted.\n        #index: \"metrics\"\n        tls:\n          # Whether to skip checking the certificate of the HEC endpoint when sending data over HTTPS. Defaults to false.\n          insecure_skip_verify: true\n          # Path to the CA cert to verify the server being connected to.\n          #ca_file: /certs/ExampleCA.crt\n          # Path to the TLS cert to use for client connections when TLS client auth is required.\n          #cert_file: /certs/HECclient.crt\n          # Path to the TLS key to use for TLS required connections.\n          #key_file: /certs/HECclient.key\n        # Application name is used to track telemetry information for Splunk App's using HEC by App name.\n        splunk_app_name: \"OpenTelemetry-Collector Splunk Exporter\"\n        # Application version is used to track telemetry information for Splunk App's using HEC by App version.\n        splunk_app_version: \"v0.0.1\"\n        heartbeat:\n          interval: 30s\n        telemetry:\n          enabled: true\n          override_metrics_names:\n            otelcol_exporter_splunkhec_heartbeats_sent: app_heartbeats_success_total\n            otelcol_exporter_splunkhec_heartbeats_failed: app_heartbeats_failed_total\n\n    service:\n      pipelines:\n        logs:\n          exporters:\n          - logging\n          - splunk_hec\n\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-modify-reducer-enable-flow-logs.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Modify the OpenTelemetry eBPF Reducer that was previously deployed with ebpf-net,\n# to pass it additional argument(s), in this case '--enable-flow-logs'\n\nnetworkExplorer:\n  reducer:\n    additionalArgs:\n      - --enable-flow-logs\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-modify-reducer.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# Modify the OpenTelemetry eBPF Reducer that was previously deployed with ebpf-net to\n# use more shards then the default of 1, to disable all udp metrics, and to enable the\n# ebpf_net.message internal metric.\n\nnetworkExplorer:\n  reducer:\n    ingestShards: 2\n    matchingShards: 2\n    aggregationShards: 2\n    disableMetrics: [udp.all]\n    enableMetrics: [ebpf_net.message]\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net-use-otel-demo-otelcol.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n################################################################################\n# Override splunk-otel-collector default values and ebpf-net.yaml gateway setting\n# to deploy OpenTelemetry-eBPF components using the otel-demo OpenTelemetry\n# Collector.\n################################################################################\n\ngateway:\n  enabled: false\n\nnetworkExplorer:\n  reducer:\n    additionalArgs:\n      - --otlp-grpc-metrics-host=otel-demo-otelcol\n"
  },
  {
    "path": "dev/devbox/source/k8s/ebpf-net.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n################################################################################\n# Override splunk-otel-collector default values to deploy OpenTelemetry-eBPF\n# components\n################################################################################\n\nclusterName: \"devbox-cluster\"\n\nagent:\n  enabled: false\n\nclusterReceiver:\n  enabled: false\n\ngateway:\n  enabled: true\n  replicaCount: 1\n\n  resources:\n    limits:\n      cpu: 500m\n      memory: 1Gi\n\n  config:\n    exporters:\n      debug:\n        verbosity: detailed\n\nnetworkExplorer:\n  enabled: true\n  log:\n    console: true\n    # possible values: { error | warning | info | debug | trace }\n    level: info\n\n  podSecurityPolicy:\n    enabled: false\n"
  },
  {
    "path": "dev/devbox/source/k8s/init.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nsource /etc/os-release\nif [[ \"${ID}\" != \"debian\" && \"${ID}\" != \"ubuntu\" ]]\nthen\n  echo \"k8s is currently only supported in Debian and Ubuntu devboxes.\"\n  exit 1\nfi\n\nif [[ \"${ID}\" == \"debian\" ]]\nthen\n  # Workaround for microk8s start on Debian Bullseye incorrectly believing that there is insufficient memory.\n  # \"This node does not have enough RAM to host the Kubernetes control plane services...\"\n  start_options=\"--disable-low-memory-guard\"\nfi\n\necho -e \"\\n---------- Starting microk8s ----------\"\nset -x\nmicrok8s start ${start_options}\nmicrok8s status --wait-ready\nmicrok8s enable dns\nmicrok8s enable hostpath-storage\n\nset +x\necho -e \"\\n---------- Installing helm diff ----------\"\nset -x\nmicrok8s helm plugin install https://github.com/databus23/helm-diff || true\n\necho -e \"\\n---------- Installing stern ----------\"\nset -x\nmicrok8s kubectl krew update\nmicrok8s kubectl krew install stern\n\necho\nsnap info microk8s | grep tracking\nmicrok8s version\nset +x\necho -e \"\\nTo change the microk8s install to a different version/snap channel, for example:\"\necho \"  sudo snap refresh microk8s --channel=latest/edge\"\necho \"  OR\"\necho \"  sudo snap refresh microk8s --channel=1.25\"\necho \"To see the currently available channels:\"\necho \"  snap info microk8s\"\necho\n\nset -x\nmicrok8s status | grep \"^microk8s\"\n\n"
  },
  {
    "path": "dev/devbox/source/k8s/modify.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nfunction print_help {\n  echo \"usage: $0 [--ebpf-net|--otel-demo] <YAML_FILE>\"\n  echo \"  --ebpf-net: modify previously deployed OpenTelementry eBPF component(s)\"\n  echo \"  --ebpf-net-local-helm-chart | -C: use local helm chart to modify OpenTelementry eBPF component(s)\"\n  echo \"                                    (default is to use public splunk-otel-collector-chart/splunk-otel-collector)\"\n  echo \"  --otel-demo: modify previously deployed OpenTelemetry Astronomy Shop Microservices Demo component(s)\"\n}\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --ebpf-net)\n      modify_ebpf_net=\"true\"\n      ;;\n\n    --ebpf-net-local-helm-chart | -C)\n      ebpf_net_use_local_helm_chart=\"true\"\n      ;;\n\n    --otel-demo)\n      modify_otel_demo=\"true\"\n      ;;\n\n    *)\n      if [ -n \"${yaml_file}\" ]\n      then\n        echo -e \"\\nOnly one yaml file should be specified.\\n\"\n        print_help\n        exit 1\n      fi\n      yaml_file=${arg}\n      if [ ! -f \"${yaml_file}\" ]\n      then\n        echo -e \"\\nYAML_FILE ${yaml_file} does not exist.\\n\"\n        print_help\n        exit 1\n      fi\n      ;;\n  esac\ndone\n\nif [[ -z \"${modify_ebpf_net}\" && -z \"${modify_otel_demo}\" ]]\nthen\n  echo -e \"\\nNeed to specify what to modify.\\n\"\n  print_help\n  exit 1\nfi\n\nif [[ -z \"${yaml_file}\" ]]\nthen\n  echo -e \"\\nYAML_FILE not specified.\\n\"\n  print_help\n  exit 1\nfi\n\nif [[ \"${modify_ebpf_net}\" == \"true\" ]]\nthen\n  if [[ \"${ebpf_net_use_local_helm_chart}\" == \"true\" ]]\n  then\n    chart=\"$HOME/splunk-otel-collector-chart/helm-charts/splunk-otel-collector\"\n  else\n    chart=\"splunk-otel-collector-chart/splunk-otel-collector\"\n  fi\n\n  namespace=\"ebpf-net-ns\"\n  release=\"ebpf-net\"\nfi\n\nif [[ \"${modify_otel_demo}\" == \"true\" ]]\nthen\n  chart=\"open-telemetry/opentelemetry-demo\"\n  namespace=\"otel-demo-ns\"\n  release=\"otel-demo\"\nfi\n\ndiff_output=$(microk8s helm diff upgrade --namespace=${namespace} ${release} ${chart} --reuse-values -f ${yaml_file})\nret=\"$?\"\nif [ \"${ret}\" != \"0\" ]; then\n  echo \"Helm command failed with exit code $?\"\n  exit \"${ret}\"\nelif [ -z \"${diff_output}\" ]; then\n  echo \"No helm changes to apply!\"\nelse\n  echo \"${diff_output}\"\n  echo -n \"Do you wish to deploy these changes? Type 'y' to continue, ctrl-c to quit: \"\n  read choice\n\n  case \"${choice}\" in\n    y)\n      echo -e \"\\nDeploying changes.\\n\"\n      microk8s helm list -A\n      echo\n      (set -x; microk8s helm upgrade --namespace=\"${namespace}\" \"${release}\" ${chart} --reuse-values -f \"${yaml_file}\")\n      echo\n      microk8s helm list -A\n      echo\n      echo \"To rollback these changes: 'helm rollback ${release} <PREVIOUS_VERSION>'\"\n      ;;\n\n    *)\n      echo -e \"\\nNOT deploying.\\n\"\n      exit 0\n      ;;\n  esac\nfi\n\n"
  },
  {
    "path": "dev/devbox/source/k8s/otel-demo.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n################################################################################\n# Override OpenTelemetry Demo default values to give grafana and the otelcol\n# more memory.\n################################################################################\n\ngrafana:\n  resources:\n    limits:\n      memory: 500Mi\n\nopentelemetry-collector:\n  resources:\n    limits:\n      memory: 500Mi\n"
  },
  {
    "path": "dev/devbox/source/k8s-collector.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\ndocker pull localhost:5000/k8s-relay\n\nexport container_id=\"$( \\\n  docker create -t --rm \\\n    --env EBPF_NET_INTAKE_PORT=\"8001\" \\\n    --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n    --network host \\\n    --entrypoint \"/srv/collector-entrypoint.sh\" \\\n    --volume \"$HOME/src/:/root/src\" \\\n    --volume \"$HOME/out/:/root/out\" \\\n    localhost:5000/k8s-relay \\\n      --log-console \\\n      --debug \\\n      \"$@\" \\\n)\"\n\nfunction cleanup_docker {\n  docker kill \"${container_id}\" || true\n  docker container prune --force || true\n  docker volume prune --force || true\n  docker image prune --force || true\n}\ntrap cleanup_docker SIGINT\n\ndocker cp \".env\" \"${container_id}:/srv/.env\"\ncp \"collector-entrypoint.sh\" \"/tmp/collector-entrypoint.sh\"\ndocker cp \"/tmp/collector-entrypoint.sh\" \"${container_id}:/srv/collector-entrypoint.sh\"\n\ndocker start -i \"${container_id}\"\ncleanup_docker\n"
  },
  {
    "path": "dev/devbox/source/kernel-collector.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nimage=\"localhost:5000/kernel-collector\"\n\nbpf_dump_file='bpf.render.raw'\nbpf_src_export_file='bpf.src.c'\ningest_dump_file='ingest.render.raw'\nhost_data_mount_path=\"$(pwd)/data-$(basename \"$0\" .sh)\"\ncontainer_data_mount_path=\"/hostfs/data\"\ncollector_entrypoint=\"/srv/collector-entrypoint.sh\"\n\nmkdir -p \"${host_data_mount_path}\"\ntouch \"${host_data_mount_path}/${bpf_src_export_file}\"\n\ndocker_args=( \\\n  --name kernel-collector\n  --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\"\n  --env EBPF_NET_HOST_DIR=\"/hostfs\"\n  --privileged\n  --pid host\n  --network host\n  --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup\n  --volume /usr/src:/hostfs/usr/src\n  --volume /lib/modules:/hostfs/lib/modules\n  --volume /etc:/hostfs/etc\n  --volume /var/cache:/hostfs/cache\n  --volume /var/run/docker.sock:/var/run/docker.sock\n  --env EBPF_NET_KERNEL_HEADERS_AUTO_FETCH=\"true\"\n  --env EBPF_NET_EXPORT_BPF_SRC_FILE=\"${container_data_mount_path}/${bpf_src_export_file}\"\n  --volume \"${host_data_mount_path}:${container_data_mount_path}\"\n  --volume \"$HOME/src/:/root/src\"\n  --volume \"$HOME/out/:/root/out\"\n)\n\napp_args=( \\\n  --log-console\n)\n\nfunction print_help {\n  echo \"usage: $0 [--help|--bpf-dump] args...\"\n  echo\n  echo \"  args...: any additional arguments are forwarded to the container\"\n  echo \"  --help: display this help message and the container's help message\"\n  echo \"  --env: export environment variable to container (--env VAR=VALUE)\"\n  echo '  --gdb: run the kernel collector under `gdb`'\n  echo '  --cgdb: run the kernel collector under `cgdb`'\n  echo \"  --bpf-dump: dump eBPF messages into file '${host_data_mount_path}/${bpf_dump_file}'\"\n  echo \"  --bpf-pipe: dump eBPF messages into named pipe '${host_data_mount_path}/${bpf_dump_file}'\"\n  echo \"  --entrypoint-error <ERROR>: force entrypoint error with the specified EntrypointError <ERROR>\"\n  echo \"  --ingest-dump: dump ingest messages into file '${host_data_mount_path}/${ingest_dump_file}'\"\n  echo \"  --ingest-pipe: dump ingest messages into named pipe '${host_data_mount_path}/${ingest_dump_file}'\"\n  echo \"  --public: use the public kernel-collector image from dockerhub (default is to use localhost:5000/kernel-collector image from local registry)\"\n  echo \"  --tag <TAG>: use the kernel-collector image with the specified <TAG>\"\n  echo '  --valgrind-memcheck: run the kernel collector under `valgrind` using the memcheck tool'\n  echo '  --valgrind-massif: run the kernel collector under `valgrind` using the massif tool'\n  echo\n  echo \"note: use '$HOME/out/tools/opentelemetry-ebpf/bpf_wire_to_json' to decode eBPF messages\"\n  sleep 5\n}\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --env)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"expected: environment variable to export\"\n\texit 1\n      fi\n      docker_args+=(--env \"$1\"); shift\n      ;;\n\n    --gdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"gdb\")\n      ;;\n\n    --cgdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"cgdb\")\n      ;;\n\n    --bpf-dump)\n      [[ ! -e \"${host_data_mount_path}/${bpf_dump_file}\" ]] \\\n        || rm -rf \"${host_data_mount_path}/${bpf_dump_file}\"\n      touch \"${host_data_mount_path}/${bpf_dump_file}\"\n      app_args+=(\"--bpf-dump-file=${container_data_mount_path}/${bpf_dump_file}\")\n      ;;\n\n    --bpf-pipe)\n      [[ ! -e \"${host_data_mount_path}/${bpf_dump_file}\" ]] \\\n        || rm -rf \"${host_data_mount_path}/${bpf_dump_file}\"\n      mkfifo \"${host_data_mount_path}/${bpf_dump_file}\"\n      app_args+=(\"--bpf-dump-file=${container_data_mount_path}/${bpf_dump_file}\")\n      ;;\n\n    --entrypoint-error)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"missing argument for --entrypoint-error\"\n\texit 1\n      fi\n      app_args+=(--entrypoint-error $1)\n      shift\n      ;;\n\n    --ingest-dump)\n      [[ ! -e \"${host_data_mount_path}/${ingest_dump_file}\" ]] \\\n        || rm -rf \"${host_data_mount_path}/${ingest_dump_file}\"\n      touch \"${host_data_mount_path}/${ingest_dump_file}\"\n      docker_args+=(--env EBPF_NET_RECORD_INTAKE_OUTPUT_PATH=\"${container_data_mount_path}/${ingest_dump_file}\")\n      ;;\n\n    --ingest-pipe)\n      [[ ! -e \"${host_data_mount_path}/${ingest_dump_file}\" ]] \\\n        || rm -rf \"${host_data_mount_path}/${ingest_dump_file}\"\n      mkfifo \"${host_data_mount_path}/${ingest_dump_file}\"\n      docker_args+=(--env EBPF_NET_RECORD_INTAKE_OUTPUT_PATH=\"${container_data_mount_path}/${ingest_dump_file}\")\n      ;;\n\n    --public)\n      image=\"otel/opentelemetry-ebpf-kernel-collector\"\n      if [[ \"${tag}\" == \"\" ]]\n      then\n        tag=\":latest-v0.10\"\n      fi\n      ;;\n\n    --tag)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"missing argument for --tag\"\n\texit 1\n      fi\n      tag=\":$1\"; shift\n      ;;\n\n    --valgrind-memcheck)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_VALGRIND=\"--tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes\")\n      ;;\n\n    --valgrind-massif)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_VALGRIND=\"--tool=massif --stacks=yes --massif-out-file=/root/out/massif.out.%p\")\n      ;;\n\n    --help)\n      app_args+=(\"${arg}\")\n      print_help\n      ;;\n\n    *)\n      app_args+=(\"${arg}\")\n      ;;\n  esac\ndone\n\ndocker_args+=(--entrypoint \"${collector_entrypoint}\")\n\ndocker_args+=(\n  --env EBPF_NET_INTAKE_PORT=\"8000\"\n  --env EBPF_NET_AGENT_NAMESPACE=\"${EBPF_NET_AGENT_NAMESPACE}\"\n  --env EBPF_NET_AGENT_CLUSTER=\"${EBPF_NET_AGENT_CLUSTER}\"\n  --env EBPF_NET_AGENT_SERVICE=\"${EBPF_NET_AGENT_SERVICE}\"\n  --env EBPF_NET_AGENT_HOST=\"${EBPF_NET_AGENT_HOST}\"\n  --env EBPF_NET_AGENT_ZONE=\"${EBPF_NET_AGENT_ZONE}\"\n  )\n\nset -x\n\ndocker pull \"${image}${tag}\"\n\nexport container_id=\"$( \\\n  docker create -t --rm \"${docker_args[@]}\" \\\n    \"${image}${tag}\" \"${app_args[@]}\" \\\n)\"\n\nfunction cleanup_docker {\n  docker kill \"${container_id}\" || true\n  docker container prune --force || true\n  docker volume prune --force || true\n  docker image prune --force || true\n}\ntrap cleanup_docker SIGINT\n\ndocker cp \".env\" \"${container_id}:/srv/.env\"\ncp \"collector-entrypoint.sh\" \"/tmp/collector-entrypoint.sh\"\ndocker cp \"/tmp/collector-entrypoint.sh\" \"${container_id}:/srv/collector-entrypoint.sh\"\n\ndocker start -i \"${container_id}\"\ncleanup_docker\n"
  },
  {
    "path": "dev/devbox/source/otelcol-config.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nextensions:\n  health_check:\n    endpoint: 0.0.0.0:13133\n  zpages:\n    endpoint: 0.0.0.0:55679\n\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n        endpoint: 0.0.0.0:4317\n      http:\n        endpoint: 0.0.0.0:4318\n  prometheus:\n    config:\n      scrape_configs:\n        - job_name: 'otel-collector'\n          metrics_path: ''\n          scrape_interval: 10s\n          static_configs:\n            - targets: ['localhost:7000']\n            - targets: ['localhost:7001']\n\nexporters:\n  debug:\n    verbosity: detailed\n  file:\n    path: /var/log/otel.log\n  splunk_hec:\n    # Splunk HTTP Event Collector token.\n    token: \"<YOUR-TOKEN>\"\n    # URL to a Splunk instance to send data to.\n    endpoint: \"https://<YOUR-HOST>:8088/services/collector/event\"\n    # Optional Splunk source: https://docs.splunk.com/Splexicon:Source\n    source: \"otel\"\n    # Optional Splunk source type: https://docs.splunk.com/Splexicon:Sourcetype\n    sourcetype: \"otel\"\n    # Splunk index, optional name of the Splunk index targeted.\n    #index: \"metrics\"\n    tls:\n      # Whether to skip checking the certificate of the HEC endpoint when sending data over HTTPS. Defaults to false.\n      insecure_skip_verify: true\n      # Path to the CA cert to verify the server being connected to.\n      #ca_file: /certs/ExampleCA.crt\n      # Path to the TLS cert to use for client connections when TLS client auth is required.\n      #cert_file: /certs/HECclient.crt\n      # Path to the TLS key to use for TLS required connections.\n      #key_file: /certs/HECclient.key\n    # Application name is used to track telemetry information for Splunk App's using HEC by App name.\n    splunk_app_name: \"OpenTelemetry-Collector Splunk Exporter\"\n    # Application version is used to track telemetry information for Splunk App's using HEC by App version.\n    splunk_app_version: \"v0.0.1\"\n    heartbeat:\n      interval: 30s\n    telemetry:\n      enabled: true\n      override_metrics_names:\n        otelcol_exporter_splunkhec_heartbeats_sent: app_heartbeats_success_total\n        otelcol_exporter_splunkhec_heartbeats_failed: app_heartbeats_failed_total\n    sending_queue:\n      batch:\n\nservice:\n  extensions: [health_check, zpages]\n  pipelines:\n    logs:\n      receivers: [otlp]\n      exporters: [debug,file]\n      #exporters: [debug,file,splunk_hec]\n    metrics:\n      receivers: [otlp]\n      exporters: [debug,file]\n  telemetry:\n    logs:\n      level: \"debug\"\n      encoding: \"console\"\n    metrics:\n      address: \":8888\"\n"
  },
  {
    "path": "dev/devbox/source/otelcol-gateway.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\necho \"Running OpenTelemetry collector server\"\necho\n\nfunction print_help {\n  echo \"usage: $0 [--contrib|--help|--host|--otel|--prom|--splunk]\"\n  echo\n  echo \"  default is to run the OpenTelemetry Contrib Collector\"\n  echo \"  --contrib: run the OpenTelemetry Contrib Collector\"\n  echo \"  --help: display this help message and the container's help message\"\n  echo \"  --host: run with --network=host\"\n  echo \"  --otel: run the OpenTelemetry Collector\"\n  echo \"  --prom: use Prometheus receiver to scrape metrics (default is OTLP gRPC receiver)\"\n  echo \"  --splunk: run the Splunk distribution of OpenTelemetry Collector\"\n}\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$HOME/src}\"\n\n# Default to opentelemetry-collector-contrib\notelcol_to_use=\"otelcol-contrib\"\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --contrib)\n      otelcol_to_use=\"otelcol-contrib\"\n      ;;\n\n    --host)\n      use_network_host=\"true\"\n      ;;\n\n    --help)\n      print_help\n      exit 0\n      ;;\n\n    --otel)\n      otelcol_to_use=\"otelcol\"\n      ;;\n\n    --prom)\n      use_prom_receiver=\"true\"\n      use_network_host=\"true\"\n      ;;\n\n    --splunk)\n      otelcol_to_use=\"splunk-otelcol\"\n      ;;\n\n    *)\n      print_help\n      exit 0\n      ;;\n  esac\ndone\n\n# Defaults\notel_collector_config_file=\"${EBPF_NET_SRC_ROOT}/dev/devbox/source/otelcol-config.yaml\"\notel_collector_config_file_internal=\"/etc/otelcol/config.yaml\"\notel_collector_container_name=\"otelcol\"\notel_collector_env_vars=\"\"\notel_collector_log_file=\"${PWD}/otel.log\"\notel_collector_ports=\"-p 4317:4317 -p 4318:4318 -p 8888:8888 -p 13133:13133\"\n\nif [[ ${use_prom_receiver} == \"true\" ]]\nthen\n  sed -i \"s/receivers: \\[otlp\\]/receivers: \\[otlp, prometheus\\]/\" ${otel_collector_config_file}\nelse\n  sed -i \"s/receivers: \\[otlp, prometheus\\]/receivers: \\[otlp\\]/\" ${otel_collector_config_file}\nfi\n\nif [[ ${use_network_host} == \"true\" ]]\nthen\n  otel_collector_ports=\"--network=host\"\nfi\n\ncase \"${otelcol_to_use}\" in\n  otelcol)\n    # https://hub.docker.com/r/otel/opentelemetry-collector/tags\n    otel_collector_image=\"otel/opentelemetry-collector:latest\"\n    ;;\n  otelcol-contrib)\n    # https://hub.docker.com/r/otel/opentelemetry-collector-contrib/tags\n    otel_collector_image=\"otel/opentelemetry-collector-contrib:latest\"\n    otel_collector_config_file_internal=\"/etc/otelcol-contrib/config.yaml\"\n    ;;\n  splunk-otelcol)\n    # https://github.com/signalfx/splunk-otel-collector/blob/main/docs/getting-started/linux-manual.md\n    # https://quay.io/repository/signalfx/splunk-otel-collector?tab=tags\n    otel_collector_image=\"quay.io/signalfx/splunk-otel-collector:latest\"\n    otel_collector_env_vars=\"-e SPLUNK_ACCESS_TOKEN=YOUR_TOKEN_HERE -e SPLUNK_REALM=YOUR_REALM_HERE -e SPLUNK_CONFIG=${otel_collector_config_file_internal}\"\n    ;;\nesac\n\necho \"otel_collector_image ${otel_collector_image}\"\necho \"otel_collector_config_file ${otel_collector_config_file}\"\necho \"otel_collector_config_file_internal ${otel_collector_config_file_internal}\"\necho \"otel_collector_env_vars ${otel_collector_env_vars}\"\necho \"otel_collector_ports ${otel_collector_ports}\"\necho \"otel_collector_log_file ${otel_collector_log_file}\"\n\nset -x\n\ntouch \"${otel_collector_log_file}\"\nchmod 666 \"${otel_collector_log_file}\"\n\ndocker run \\\n  --rm \\\n  ${otel_collector_env_vars} \\\n  ${otel_collector_ports} \\\n  -v \"${otel_collector_config_file}:${otel_collector_config_file_internal}\" \\\n  -v \"${otel_collector_log_file}:/var/log/otel.log\" \\\n  --name ${otel_collector_container_name} \\\n  ${otel_collector_image}\n"
  },
  {
    "path": "dev/devbox/source/prometheus.yml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nglobal:\n  scrape_interval: 10s\nrule_files:\n- \"rules.yaml\"\nscrape_configs:\n- job_name: 'otelebpf-server'\n  static_configs:\n  - targets:\n    - 'localhost:7001'\n    - 'localhost:7002'\n    - 'localhost:7003'\n- job_name: 'otelebpf-server-internal'\n  static_configs:\n  - targets: ['localhost:7000']\n"
  },
  {
    "path": "dev/devbox/source/provision/core.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\n\n###########################\n# enable core dumps\n\nif [[ \"${ID}\" == \"ubuntu\" ]]\nthen\n  sudo sed -i 's/enabled=1/enabled=0/' /etc/default/apport\nfi\n\nsudo bash -c 'cat >> /etc/security/limits.conf <<EOF\n*               soft    core            unlimited\nEOF'\n\nsudo bash -c 'cat > /etc/sysctl.d/90-core-pattern.conf <<EOF\nkernel.core_pattern = core-%e-%s-%u-%g-%p-%t\nEOF'\n"
  },
  {
    "path": "dev/devbox/source/provision/docker.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n##############\n# docker setup\n\ncase \"${ID}\" in\n  debian | ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    curl -sfL 'https://download.docker.com/linux/debian/gpg' | sudo -E apt-key add -\n\n    if [[ -n \"$(apt-cache search '^docker\\.io$')\" ]]; then\n      sudo apt-get install -y --no-install-recommends --allow-change-held-packages docker.io\n    elif [[ -n \"$(apt-cache search '^docker-ce$')\" ]]; then\n      sudo apt-get install -y --no-install-recommends --allow-change-held-packages docker-ce\n    else\n      curl -fsSL https://get.docker.com/ | sudo sh\n    fi\n    sudo usermod -aG docker \"${USER}\"\n    sudo systemctl enable docker\n    ;;\n\n  centos)\n    sudo yum-config-manager --add-repo \"https://download.docker.com/linux/centos/docker-ce.repo\"\n    pkg_list=(docker-ce docker-ce-cli containerd.io)\n    case \"${VERSION_ID}\" in\n      7)\n        sudo yum install -y \"${pkg_list}\"\n        ;;\n\n      *)\n        sudo yum install -y --nobest \"${pkg_list}\"\n        ;;\n    esac\n    sudo usermod -aG docker \"${USER}\"\n    sudo systemctl enable docker\n    ;;\nesac\n"
  },
  {
    "path": "dev/devbox/source/provision/k8s.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nmicrok8s_channel=\"1.26\"\n\nsource /etc/os-release\nuname -a\nenv | sort\n\ncase \"${ID}\" in\n  debian)\n    sudo -E apt-get install -y --no-install-recommends --allow-change-held-packages snapd\n    sudo snap install core\n    sudo snap install snapd\n    ;;\n\n  ubuntu)\n    ;;\n\n  centos)\n    echo \"TODO add k8s support to centos devboxes\"\n    exit 0\n    ;;\n\n  *)\n    echo \"k8s not currently supported in ${ID} devboxes\"\n    exit 0\n    ;;\nesac\n\nsudo snap install microk8s --classic --channel=${microk8s_channel}\n\nsudo usermod -a -G microk8s $USER\nmkdir -p ~/.kube\nsudo /snap/bin/microk8s config > ~/.kube/config\nchmod 600 ~/.kube/config\n\n# install krew\n# https://krew.sigs.k8s.io/docs/user-guide/quickstart/\n(\n  set -x; cd \"$(mktemp -d)\" &&\n  OS=\"$(uname | tr '[:upper:]' '[:lower:]')\" &&\n  ARCH=\"$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\\(arm\\)\\(64\\)\\?.*/\\1\\2/' -e 's/aarch64$/arm64/')\" &&\n  KREW=\"krew-${OS}_${ARCH}\" &&\n  curl -fsSLO \"https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz\" &&\n  tar zxvf \"${KREW}.tar.gz\" &&\n  ./\"${KREW}\" install krew\n)\ncat >> ~/.bashrc <<EOF\nexport PATH=\"${KREW_ROOT:-$HOME/.krew}/bin:\\$PATH\"\nEOF\n\n# get the microservices-demo repo for use by the deploy.sh script\ncd ~\ngit clone https://github.com/GoogleCloudPlatform/microservices-demo.git\n\n# get the splunk-otel-collector-chart repo for (optional) use by the deploy.sh script\ngit clone https://github.com/signalfx/splunk-otel-collector-chart.git\n\n# disable microk8s by default on devbox startup\nsudo /snap/bin/microk8s stop\n\ncat >> ~/.bash_aliases <<EOF\nalias helm='microk8s helm'\nalias kubectl='microk8s kubectl'\nalias k='kubectl'\nalias kgc='k config get-contexts'\nalias kgns='k get ns'\nalias kgp='k get pods'\nalias kgpa='k get pods -A'\nalias kns='k config set-context --current --namespace'\nEOF\n\n# configure microk8s to be able to access the local docker registry\n# https://microk8s.io/docs/registry-private\nsudo mkdir -p /var/snap/microk8s/current/args/certs.d/localhost:5000\nsudo touch /var/snap/microk8s/current/args/certs.d/localhost:5000/hosts.toml\nsudo bash -c 'cat >> /var/snap/microk8s/current/args/certs.d/localhost:5000/hosts.toml <<EOF\n# /var/snap/microk8s/current/args/certs.d/localhost:5000/hosts.toml\nserver = \"http://localhost:5000\"\n\n[host.\"http://localhost:5000\"]\ncapabilities = [\"pull\", \"resolve\"]\nEOF'\n\n"
  },
  {
    "path": "dev/devbox/source/provision/kernel.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n################################\n# install kernel and its headers\n\ncase \"${ID}\" in\n  debian)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    if [[ -z \"${VERSION_CODENAME}\" ]]; then\n      VERSION_CODENAME=\"unstable\"\n    fi\n\n    case \"${VERSION_CODENAME}\" in\n      unstable | *sid*)\n        # workardound for the current situation of debian-sid\n        sudo -E apt-get purge -y libgcc1\n        ;;\n    esac\n\n    sudo -E apt-cache search linux-headers\n    sudo -E apt-get install -y --allow-change-held-packages \\\n      linux-image-amd64 linux-headers-amd64 \\\n      dkms kmod build-essential\n    ;;\n\n  ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    pkg_list=( \\\n      linux-virtual linux-image-virtual linux-headers-virtual\n      dkms kmod build-essential\n    )\n\n    sudo -E apt-get install -y --allow-change-held-packages \"${pkg_list[@]}\"\n    ;;\n\n  centos)\n    sudo yum install -y kernel kernel-devel kernel-headers\n    ;;\nesac\n"
  },
  {
    "path": "dev/devbox/source/provision/packages.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n##################\n# install packages\n\ncommon_debian_packages=( \\\n  curl ca-certificates openssl net-tools iproute2 netcat-openbsd dnsutils\n  policycoreutils\n  openssh-server sshfs gnupg\n  htop aptitude strace lsof\n  tmux vim-nox\n  openssh-server sshfs\n  python3-bcrypt\n  bc jq\n  apt-transport-https\n  git\n  valgrind\n  ripgrep\n  selinux-utils\n  bcc\n  bpfcc-tools\n  bpftrace\n  cgroup-tools\n  gdb cgdb\n  stress-ng\n)\n\nadditional_ubuntu_packages=( \\\n  linux-tools-common linux-tools-generic # bpftool\n)\n\ncase \"${ID}\" in\n  debian)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    pkg_list=(\"${common_debian_packages[@]}\")\n\n    sudo -E apt-get install -y --no-install-recommends --allow-change-held-packages \"${pkg_list[@]}\"\n    ;;\n\n  ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    pkg_list=(\"${common_debian_packages[@]}\")\n    pkg_list+=(\"${additional_ubuntu_packages[@]}\")\n\n    sudo -E apt-get install -y --no-install-recommends --allow-change-held-packages \"${pkg_list[@]}\"\n    ;;\n\n  centos)\n    pkg_list=( \\\n      perf\n      redhat-lsb perl\n      openssl ca-certificates\n      openssh-server fuse-sshfs\n      curl wget net-tools nmap-ncat bind-utils\n      tmux vim-minimal\n      python3-pip\n    )\n\n    sudo yum install -y \"${pkg_list[@]}\"\n\n    sudo pip3 install setuptools-rust\n    sudo pip3 install --upgrade pip\n    sudo pip3 install bcrypt\n    ;;\nesac\n"
  },
  {
    "path": "dev/devbox/source/provision/prometheus.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nsource .env\nuname -a\nenv | sort\n\nPROMETHEUS_BIN_URL=\"https://github.com/prometheus/prometheus/releases/download/v2.19.3/prometheus-2.19.3.linux-amd64.tar.gz\"\nPROMETHEUS_BIN_DIR=\"/usr/local/bin\"\nPROMETHEUS_ETC_DIR=\"/etc/prometheus\"\nPROMETHEUS_VAR_LIB_DIR=\"/var/lib/prometheus\"\n\nfunction install_prometheus {\n  sudo groupadd --system prometheus\n  sudo useradd -s /sbin/nologin --system -g prometheus prometheus\n\n  tmp_dir=\"$(mktemp -d -t)\"\n  curl -L \"${PROMETHEUS_BIN_URL}\" \\\n    | tar --strip-components=1 --directory=\"${tmp_dir}\" -xzv\n\n  sudo mkdir -p \"${PROMETHEUS_VAR_LIB_DIR}\"\n  sudo chown -R prometheus:prometheus \"${PROMETHEUS_VAR_LIB_DIR}\"\n\n  for what in prometheus promtool tsdb; do\n    sudo mv \"${tmp_dir}/${what}\" \"${PROMETHEUS_BIN_DIR}/${what}\"\n    sudo chown prometheus:prometheus \"${PROMETHEUS_BIN_DIR}/${what}\"\n  done\n\n  sudo mkdir -p \"${PROMETHEUS_ETC_DIR}\"\n  for what in consoles console_libraries; do\n    sudo mv \"${tmp_dir}/${what}\" \"${PROMETHEUS_ETC_DIR}/\"\n  done\n  sudo cp \"/tmp/_prometheus/prometheus.yml\" \"${PROMETHEUS_ETC_DIR}/prometheus.yml\"\n  sudo chown -R prometheus:prometheus \"${PROMETHEUS_ETC_DIR}\"\n\n\tsudo tee /etc/systemd/system/prometheus.service <<EOF\n[Unit]\nDescription=Prometheus\nDocumentation=https://prometheus.io/docs/introduction/overview/\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nEnvironment=\"GOMAXPROCS=1\"\nUser=prometheus\nGroup=prometheus\nExecReload=/bin/kill -HUP \\$MAINPID\nExecStart=${PROMETHEUS_BIN_DIR}/prometheus \\\n  --config.file=${PROMETHEUS_ETC_DIR}/prometheus.yml \\\n  --storage.tsdb.path=${PROMETHEUS_VAR_LIB_DIR} \\\n  --web.console.templates=${PROMETHEUS_ETC_DIR}/consoles \\\n  --web.console.libraries=${PROMETHEUS_ETC_DIR}/console_libraries \\\n  --web.listen-address=0.0.0.0:9090 \\\n  --web.external-url=\n\nSyslogIdentifier=prometheus\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\nEOF\n}\n\ncase \"${ID}\" in\n  debian | ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n    sudo -E apt-get install -y --no-install-recommends --allow-change-held-packages prometheus\n    sudo cp \"/tmp/_prometheus/prometheus.yml\" \"${PROMETHEUS_ETC_DIR}/prometheus.yml\"\n    ;;\n\n  centos)\n    install_prometheus\n    ;;\nesac\n\nsudo systemctl disable prometheus\n"
  },
  {
    "path": "dev/devbox/source/provision/repo.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n#############################\n# set up package repositories\n\ncase \"${ID}\" in\n  debian)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    if [[ -z \"${VERSION_CODENAME}\" ]]; then\n      VERSION_CODENAME=\"unstable\"\n    fi\n\n    case \"${VERSION_CODENAME}\" in\n      unstable | *sid*)\n        sudo tee /etc/apt/sources.list <<EOF\ndeb http://deb.debian.org/debian/ unstable main non-free contrib\nEOF\n        ;;\n\n      *)\n        ;;\n    esac\n\n    sudo -E apt-get update -y\n    ;;\n\n  ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    sudo -E apt-get update -y\n    ;;\n\n  centos)\n    sudo yum install -y yum-utils\n\n    case \"${VERSION_ID}\" in\n      7)\n        sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm\n        ;;\n\n      8)\n        sudo yum config-manager --set-enabled PowerTools\n        ;;\n    esac\n\n    sudo yum list\n    ;;\n\n  *)\n    echo \"unknown distro\"\n    exit 1\n    ;;\nesac\n"
  },
  {
    "path": "dev/devbox/source/provision/symlinks.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n################################################\n# set up symlinks based off of EBPF_NET_SRC_ROOT\n\nsource_dir=\"${HOME}/src/dev\"\nfiles=( \\\n  selinux-bpf.sh\n)\n\nfor file in \"${files[@]}\"; do\n  ln -s \"${source_dir}/${file}\" \"${HOME}/${file}\"\ndone\n\nsource_dir=\"${HOME}/src/dev/devbox/source\"\nfiles=( \\\n  .rgrc\n  cloud-collector.sh\n  collector-entrypoint.sh\n  k8s\n  k8s-collector.sh\n  kernel-collector.sh\n  otelcol-gateway.sh\n  reducer.sh\n  test-kernel-collector.sh\n)\n\nfor file in \"${files[@]}\"; do\n  ln -s \"${source_dir}/${file}\" \"${HOME}/${file}\"\ndone\n"
  },
  {
    "path": "dev/devbox/source/provision/upgrade.sh",
    "content": "#!/bin/bash -xe\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nsource /etc/os-release\nuname -a\nenv | sort\n\n###########################\n# upgrade existing packages\n\ncase \"${ID}\" in\n  debian | ubuntu)\n    export DEBIAN_FRONTEND=\"noninteractive\"\n\n    sudo -E apt-get upgrade --auto-remove --purge -y \\\n      --no-install-recommends \\\n      --allow-change-held-packages\n    ;;\n\n  centos)\n    case \"${VERSION_ID}\" in\n      7)\n        sudo yum update -y\n        ;;\n\n      *)\n        sudo yum update -y --nobest\n        ;;\n    esac\n    ;;\nesac\n"
  },
  {
    "path": "dev/devbox/source/reducer.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nimage=\"localhost:5000/reducer\"\n\nnum_shards=\"1\"\npublish_ports=\"false\"\nstart_prometheus=\"false\"\npublish_otlp_grpc_metrics=\"true\"\npublish_prometheus_metrics=\"false\"\n\ndocker_args=( \\\n  --name reducer\n  --env EBPF_NET_RUN_UNDER_GDB=\"${EBPF_NET_RUN_UNDER_GDB}\"\n  --volume \"$HOME/src/:/root/src\"\n  --volume \"$HOME/out/:/root/out\"\n)\n\napp_args=( \\\n  --port=8000\n  --internal-prom=0.0.0.0:7000\n  --prom=0.0.0.0:7001\n  --partitions-per-shard=1\n  --enable-aws-enrichment\n  --log-console\n  --debug\n)\n\nfunction print_help {\n  echo \"usage: $0 [--cgdb|--disable-otlp-grpc-metrics|--enable-prometheus-metrics|--env|--gdb|--help|--num-shards <num>|--prom|--public|--publish-ports] args...\"\n  echo\n  echo '  --cgdb: run the pipeline server under `cgdb`'\n  echo \"  --disable-otlp-grpc-metrics: do not publish metrics via OTLP gRPC\"\n  echo \"  --enable-prometheus-metrics: publish metrics via Prometheus\"\n  echo \"  --env: export environment variable to container (--env VAR=VALUE)\"\n  echo '  --gdb: run the pipeline server under `gdb`'\n  echo \"  --help: display this help message and the container's help message\"\n  echo \"  --num-shards <num>: the number of shards to run per reducer core\"\n  echo \"  --prom: start prometheus\"\n  echo \"  --public: use the public reducer image from dockerhub (default is to use localhost:5000/reducer image from local registry)\"\n  echo \"  --publish-ports: publish individual ports (instead of running with --network=host)\"\n  echo \"  --tag: use the reducer image with the specified tag (--tag <TAG>)\"\n  echo \"  args...: any additional arguments are forwarded to the container\"\n}\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --disable-otlp-grpc-metrics)\n      publish_otlp_grpc_metrics=\"false\"\n      ;;\n    --enable-prometheus-metrics)\n      publish_prometheus_metrics=\"true\"\n      ;;\n    --env)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"expected: environment variable to export\"\n\texit 1\n      fi\n      docker_args+=(--env \"$1\"); shift\n      ;;\n\n    --gdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"gdb\")\n      ;;\n\n    --cgdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"cgdb\")\n      ;;\n\n    --help)\n      app_args+=(\"${arg}\")\n      print_help\n      ;;\n\n    --num-shards)\n      num_shards=\"$1\"\n      shift;\n      ;;\n\n    --public)\n      image=\"otel/opentelemetry-ebpf-reducer\"\n      if [[ \"${tag}\" == \"\" ]]\n      then\n        tag=\":latest-v0.10\"\n      fi\n      ;;\n\n    --publish-ports)\n      publish_ports=\"true\"\n      ;;\n\n    --prom)\n      start_prometheus=\"true\"\n      ;;\n\n    --tag)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"missing argument for --tag\"\n\texit 1\n      fi\n      tag=\":$1\"; shift\n      ;;\n\n    *)\n      app_args+=(\"${arg}\")\n      ;;\n  esac\ndone\n\nset -x\n\nif [[ ${start_prometheus} == \"true\" ]]\nthen\n  sudo systemctl start prometheus\nfi\n\nif [[ ${publish_ports} == \"false\" ]]\nthen\n  docker_args+=(\n    --network=host\n  )\nelse\n  docker_args+=(\n    --publish 8000:8000\n    --publish 7000:7000\n    --publish 7001:7001\n    --publish 7002:7001\n    --publish 7003:7001\n    )\nfi\n\napp_args+=(\n  --num-ingest-shards=${num_shards}\n  --num-matching-shards=${num_shards}\n  --num-aggregation-shards=${num_shards}\n)\n\nif [[ ${publish_otlp_grpc_metrics} == \"true\" ]]\nthen\n  app_args+=(\n    --enable-otlp-grpc-metrics\n  )\nfi\n\nif [[ ${publish_prometheus_metrics} == \"false\" ]]\nthen\n  app_args+=(\n    --disable-prometheus-metrics\n  )\nfi\n\ndocker pull \"${image}${tag}\"\n\nexport container_id=\"$( \\\n  docker create -t --rm \"${docker_args[@]}\" \\\n    \"${image}${tag}\" \"${app_args[@]}\" \\\n)\"\n\nfunction cleanup_docker {\n  docker kill \"${container_id}\" || true\n  docker container prune --force || true\n  docker volume prune --force || true\n  docker image prune --force || true\n}\ntrap cleanup_docker SIGINT\n\ndocker start -i \"${container_id}\"\ncleanup_docker\n"
  },
  {
    "path": "dev/devbox/source/test-kernel-collector.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nimage=\"localhost:5000/kernel-collector-test\"\n\nbpf_dump_file='bpf.render.raw'\nbpf_src_export_file='bpf.src.c'\ningest_dump_file='ingest.render.raw'\nhost_data_mount_path=\"$(pwd)/data-$(basename \"$0\" .sh)\"\ncontainer_data_mount_path=\"/hostfs/data\"\n\nmkdir -p \"${host_data_mount_path}\"\ntouch \"${host_data_mount_path}/${bpf_src_export_file}\"\n\ndocker_args=( \\\n  --env EBPF_NET_HOST_DIR=\"/hostfs\"\n  --privileged\n  --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup\n  --volume /usr/src:/hostfs/usr/src\n  --volume /lib/modules:/hostfs/lib/modules\n  --volume /etc:/hostfs/etc\n  --volume /var/cache:/hostfs/cache\n  --volume /var/run/docker.sock:/var/run/docker.sock\n  --env EBPF_NET_KERNEL_HEADERS_AUTO_FETCH=\"true\"\n  --env EBPF_NET_EXPORT_BPF_SRC_FILE=\"${container_data_mount_path}/${bpf_src_export_file}\"\n  --volume \"${host_data_mount_path}:${container_data_mount_path}\"\n)\n\napp_args=( \\\n  --log-console\n)\n\nfunction print_help {\n  echo \"usage: $0 [--help|...] args...\"\n  echo\n  echo \"  args...: any additional arguments are forwarded to the container\"\n  echo \"  --delay-exit: sleep forever after test completes to prevent the docker container from exiting\"\n  echo \"  --delay-exit-on-failure: if test fails, sleep forever to prevent the docker container from exiting\"\n  echo \"  --help: display this help message and the container's help message\"\n  echo \"  --env: export environment variable to container (--env VAR=VALUE)\"\n  echo '  --gdb: run the kernel collector under `gdb`'\n  echo '  --cgdb: run the kernel collector under `cgdb`'\n  echo \"  --public: use the public kernel-collector-test image from quay.io (default is to use localhost:5000/kernel-collector-test image from local registry)\"\n  echo \"  --tag: use the kernel-collector image with the specified tag (--tag <TAG>)\"\n  echo \"  --trace: enable trace logging (default is debug level)\"\n  echo '  --valgrind-memcheck: run the kernel collector under `valgrind` using the memcheck tool'\n  echo '  --valgrind-massif: run the kernel collector under `valgrind` using the massif tool'\n  echo\n  sleep 5\n}\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --delay-exit)\n      docker_args+=(--env DELAY_EXIT=\"true\")\n      ;;\n\n    --delay-exit-on-failure)\n      docker_args+=(--env DELAY_EXIT_ON_FAILURE=\"true\")\n      ;;\n\n    --env)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"expected: environment variable to export\"\n\texit 1\n      fi\n      docker_args+=(--env \"$1\"); shift\n      ;;\n\n    --gdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"gdb\")\n      ;;\n\n    --cgdb)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_GDB=\"cgdb\")\n      ;;\n\n    --public)\n      image=\"quay.io/splunko11ytest/network-explorer-debug/kernel-collector-test\"\n      if [[ \"${tag}\" == \"\" ]]\n      then\n        tag=\":latest\"\n      fi\n      ;;\n\n    --tag)\n      if [[ \"$#\" -lt 1 ]]; then\n        echo \"missing argument for --tag\"\n\texit 1\n      fi\n      tag=\":$1\"; shift\n      ;;\n\n    --trace)\n      docker_args+=(--env SPDLOG_LEVEL=\"trace\")\n      ;;\n\n    --valgrind-memcheck)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_VALGRIND=\"--tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes\")\n      ;;\n\n    --valgrind-massif)\n      docker_args+=(--env EBPF_NET_RUN_UNDER_VALGRIND=\"--tool=massif --stacks=yes --massif-out-file=/root/out/massif.out.%p\")\n      ;;\n\n    --help)\n      app_args+=(\"${arg}\")\n      print_help\n      ;;\n\n    *)\n      app_args+=(\"${arg}\")\n      ;;\n  esac\ndone\n\nset -x\n\ndocker pull \"${image}${tag}\"\n\nexport container_id=\"$( \\\n  docker create -t --rm \"${docker_args[@]}\" \\\n    \"${image}${tag}\" \"${app_args[@]}\" \\\n)\"\n\nfunction cleanup_docker {\n  docker kill \"${container_id}\" || true\n  docker container prune --force || true\n  docker volume prune --force || true\n  docker image prune --force || true\n}\ntrap cleanup_docker SIGINT\n\ndocker start -i \"${container_id}\"\ncleanup_docker\n"
  },
  {
    "path": "dev/docker-registry-login.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/docker-registry-lib.sh\"\n\nif [ \"$#\" -lt 1 ]; then\n  echo \"usage: $0 [options...] registry...\"\n  echo\n  echo \"registry:\"\n  echo \"  ecr: log in to ECR\"\n  echo \"  gcr: log in to GCR\"\n  echo \"  okta: login through the okta plugin\"\n  echo \"  env: log in to the docker registry auto-detected off the env var EBPF_NET_DOCKER_REGISTRY\"\n  echo \"       if the variable is unset, no login attempts are made\"\n  echo \"  the docker registry URL can also be given, in which case its type will be auto-detected\"\n  echo\n  echo \"options:\"\n  echo \"  --no-vault: do not use a vault app to fetch secrets from\"\n  echo \n  echo \"ERROR: docker regsitry not specified\"\nfi\n\nuse_vault=true\n\nfunction auto_detected_login {\n  case \"$(detect_docker_registry \"$1\")\" in\n    none)\n      echo \"no docker registry configured, skipping login\"\n      ;;\n\n    local)\n      echo \"no login needed for local docker registry '${EBPF_NET_DOCKER_REGISTRY}'\"\n      ;;\n\n    ecr)\n      echo \"ECR detected at '${EBPF_NET_DOCKER_REGISTRY}'\"\n      ecr_login \"${use_vault}\"\n      ;;\n\n    gcr)\n      echo \"GCR detected at '${EBPF_NET_DOCKER_REGISTRY}'\"\n      gcr_login\n      ;;\n\n    okta)\n      echo \"okta detected at '${EBPF_NET_DOCKER_REGISTRY}'\"\n      okta_login\n      ;;\n\n\n    *)\n      echo \"ERROR: unrecognized docker registry '${EBPF_NET_DOCKER_REGISTRY}'\"\n      return 1\n      ;;\n  esac\n}\n\nwhile [ \"$#\" -gt 0 ]; do\n  arg=\"$1\"; shift\n\n  case \"${arg}\" in\n    --no-vault)\n      use_vault=false\n      ;;\n\n    ecr)\n      ecr_login \"${use_vault}\"\n      ;;\n\n    gcr)\n      gcr_login\n      ;;\n\n    okta)\n      okta_login\n      ;;\n\n    env)\n      auto_detected_login \"${EBPF_NET_DOCKER_REGISTRY}\"\n      ;;\n\n    *)\n      auto_detected_login \"${arg}\"\n      ;;\n      \n  esac\ndone\n"
  },
  {
    "path": "dev/docker-registry-push.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\n\nfunction print_help {\n  echo \"usage: $0 image tag docker_registry [options...]\"\n  echo\n  echo \"options:\"\n  echo \"  --no-login: do not log in to the docker registry\"\n}\n\nargs=()\nlogin_args=()\ndo_login=true\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n\n  case \"${arg}\" in\n    --no-login)\n      do_login=false\n      ;;\n\n    --no-vault)\n      login_args+=(\"${arg}\")\n      ;;\n\n    *)\n      args+=(\"${arg}\")\n      ;;\n  esac\ndone\n\nimage_name=\"${args[0]}\"\nimage_tag=\"${args[1]}\"\n\n# take docker registry from command line, otherwise get it from env var EBPF_NET_DOCKER_REGISTRY\n[[ \"${#args[@]}\" -lt 3 ]] \\\n  && docker_registry=\"${EBPF_NET_DOCKER_REGISTRY}\" \\\n  || docker_registry=\"${args[2]}\"\n# default to local docker registry if no registry given or found\n[[ -n \"${docker_registry}\" ]] \\\n  || docker_registry=\"localhost:5000\"\n\nif [[ \"${do_login}\" == true ]]; then\n  \"${EBPF_NET_SRC_ROOT}/dev/docker-registry-login.sh\" \"${login_args}\" \"${docker_registry}\"\nfi\n\n# add --tls-verify=false for default registry (localhost:5000)\npush_args=\"\"\nif [[ \"${docker_registry}\" == \"localhost:5000\" ]]; then\n  push_args=\"--tls-verify=false\"\nfi\n\n(set -x; \\\n  podman tag \"${image_name}:${image_tag}\" \\\n    \"${docker_registry}/${image_name}:${image_tag}\"; \\\n  podman push ${push_args} \"${docker_registry}/${image_name}:${image_tag}\"; \\\n)\n"
  },
  {
    "path": "dev/docker-registry.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ndocker run -d -p 5000:5000 --restart always --name local-docker-registry --env \"REGISTRY_STORAGE_DELETE_ENABLED=true\" registry:latest\n"
  },
  {
    "path": "dev/git-pull-request.sh",
    "content": "#!/bin/bash\n\n# Create a draft pull request against the upstream repo's main branch.\n# - Infers the head branch from the current checked-out branch.\n# - Optionally accepts a PR title as arguments (supports spaces).\n# - Starts with an empty PR body.\n#\n# Usage:\n#   ./git-pull-request.sh [optional title]\n\nset -o pipefail\n\n# Collect all args as the title (allows spaces)\nTITLE=\"$*\"\n\n# Determine current branch\nBRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)\nif [ -z \"$BRANCH\" ] || [ \"$BRANCH\" = \"HEAD\" ]; then\n  echo \"Unable to infer current branch (detached HEAD?). Checkout a branch and retry.\"\n  exit 1\nfi\n\n# Default title falls back to Draft: <branch> if none provided\nif [ -z \"$TITLE\" ]; then\n  TITLE=\"Draft: $BRANCH\"\nfi\n\n# Determine upstream repo slug (owner/repo)\ndetermine_upstream_repo() {\n  local upstream_repo=\"\"\n  if command -v gh >/dev/null 2>&1; then\n    upstream_repo=$(gh repo view upstream --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)\n  fi\n  if [ -z \"$upstream_repo\" ]; then\n    local upstream_url\n    upstream_url=$(git remote get-url upstream 2>/dev/null || true)\n    if [ -n \"$upstream_url\" ]; then\n      upstream_repo=$(echo \"$upstream_url\" \\\n        | sed -E 's#^git@[^:]+:##; s#^https?://[^/]+/##; s#\\.git$##')\n    fi\n  fi\n  echo \"$upstream_repo\"\n}\n\nUPSTREAM_REPO=$(determine_upstream_repo)\nif [ -z \"$UPSTREAM_REPO\" ]; then\n  echo \"Skipping PR creation: unable to determine upstream repo. Ensure 'upstream' remote exists.\"\n  exit 0\nfi\n\nif ! command -v gh >/dev/null 2>&1; then\n  echo \"gh not found; skipping draft PR creation. Install GitHub CLI: https://cli.github.com/\"\n  echo \"You can create it manually with:\"\n  echo \"  gh pr create --repo $UPSTREAM_REPO --base main --title \\\"$TITLE\\\" --body \\\"\\\" --draft\"\n  exit 0\nfi\n\n# Ensure gh is authenticated\nif ! gh auth status >/dev/null 2>&1; then\n  echo \"gh is not authenticated; skipping draft PR creation. Run 'gh auth login' and retry:\"\n  echo \"  gh pr create --repo $UPSTREAM_REPO --base main --title \\\"$TITLE\\\" --body \\\"\\\" --draft\"\n  exit 0\nfi\n\n# Create the draft PR. This may fail if there are no changes vs base.\nif gh pr create \\\n    --repo \"$UPSTREAM_REPO\" \\\n    --base main \\\n    --title \"$TITLE\" \\\n    --body \"\" \\\n    --draft; then\n  echo \"Draft PR created against $UPSTREAM_REPO (base: main, head: ${ORIGIN_OWNER:-}:$BRANCH).\"\nelse\n  echo \"Could not create draft PR (possibly no changes vs base). You can try manually with:\"\n  echo \"  gh pr create --repo $UPSTREAM_REPO --base main --head ${ORIGIN_OWNER:-<your-username>}:$BRANCH --title \\\"$TITLE\\\" --body \\\"\\\" --draft\"\nfi\n"
  },
  {
    "path": "dev/git-upstream-branch.sh",
    "content": "#!/bin/bash\n\n# This script creates a new branch from the upstream main branch and pushes it to origin.\n\nBRANCH=\"$1\"\nif [ -z \"$BRANCH\" ]; then\n    echo \"Usage: $0 <branch-name>\"\n    exit 1\nfi\ngit fetch upstream && git checkout -b \"$BRANCH\" upstream/main && git push -u origin \"$BRANCH\"\necho \"Branch '$BRANCH' created and pushed to origin.\""
  },
  {
    "path": "dev/otel/otel-config.yaml",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nreceivers:\n  otlp:\n    protocols:\n      http:\n\nexporters:\n  debug:\n    verbosity: detailed\n  file:\n    path: /var/log/otel.log\n\nservice:\n  pipelines:\n    logs:\n      receivers: [otlp]\n      exporters: [debug,file]\n"
  },
  {
    "path": "dev/otel/run-ncat.sh",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nncat -tlp 8000\n"
  },
  {
    "path": "dev/otel/run-otel.sh",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ntouch \"${PWD}/otel.log\"\n\nexport OTEL_COLLECTOR_IMAGE=${OTEL_COLLECTOR_IMAGE:-otel/opentelemetry-collector}\nexport OTEL_COLLECTOR_PORT=${OTEL_COLLECTOR_PORT:-4318}\n\nset -x\n\ndocker run \\\n  --rm \\\n  ${OTEL_COLLECTOR_ENV_VARS} \\\n  -p 8000:${OTEL_COLLECTOR_PORT} \\\n  -v \"${PWD}/otel-config.yaml:/etc/otel/config.yaml\" \\\n  -v \"${PWD}/otel.log:/var/log/otel.log\" \\\n  ${OTEL_COLLECTOR_IMAGE}\n"
  },
  {
    "path": "dev/script/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nlint_shell_script_bundle(\n  dev-include-scripts\n  SOURCES\n    bash-error-lib.sh\n    benv-lib.sh\n)\n\n"
  },
  {
    "path": "dev/script/bash-error-lib.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -Eeo pipefail\n\ntrap 'catch $? $LINENO' ERR\ncatch() {\n  echo \"Error $1 occurred at $0 line $2\"\n  exit \"$1\"\n}\n"
  },
  {
    "path": "dev/script/benv-lib.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nget_benv_container_name() {\n  # MacOS has a slightly different syntax for format in stat\n  if [ \"$(uname -s)\" == \"Darwin\" ]; then\n    echo \"benv-$(basename \"${EBPF_NET_SRC}\")-$(stat -f %i \"${EBPF_NET_SRC}\")\"\n  else\n    echo \"benv-$(basename \"${EBPF_NET_SRC}\")-$(stat --format=%i \"${EBPF_NET_SRC}\")\"\n  fi\n}\n\nget_benv_build_dir() {\n  container_name=$(get_benv_container_name)\n  benv_build_dir=\"\"\n  benv_build_dir=$(docker inspect \"${container_name}\" | jq .[].Mounts | grep tmp | awk '{print $2}' | sed 's/,$//' | sed 's/\"//g') || true\n\n  if [ -z \"${benv_build_dir}\" ]\n  then\n    echo \"unable to find benv build directory for container name ${container_name}\" 1>&2\n    # Note: don't return with an error status to allow scripts to check for empty response and continue running\n  fi\n\n  echo \"${benv_build_dir}\"\n}\n\n"
  },
  {
    "path": "dev/script/docker-registry-lib.sh",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nfunction ecr_login {\n  login_command=(aws ecr get-login --no-include-email)\n\n  if [[ \"$1\" == false ]]; then\n    eval \"$(\"${login_command[@]}\")\"\n  else\n    # aws-vault v6 writes the version to stderr now,\n    # so redirect stderr to stdout\n    aws_vault_version=$(aws-vault --version 2>&1)\n    aws_vault_args=()\n    aws_vault_major_version=$(echo ${aws_vault_version} | cut -d. -f1 | cut -c2-)\n    # aws-vault v5 removed the --assume-role-ttl flag and replaced it with --duration\n    if [[ \"${aws_vault_major_version}\" -ge 5 ]]; then\n      aws_vault_args+=(\"--duration=1h\")\n    else\n      aws_vault_args+=(\"--assume-role-ttl=1h\")\n    fi\n\n    echo \"logging in to ecr...\"\n    eval \"$(aws-vault exec \"${aws_vault_args[@]}\" flowmill-prod -- \"${login_command[@]}\")\"\n    echo -e \"done\\\\n\"\n  fi\n}\n\nfunction gcr_login {\n\n  # Make sure docker is set up to use the credentials helpers\n  gcloud auth configure-docker\n\n  echo \"logging in to gcr ...\"\n  gcloud auth login\n  echo -e \"done\\\\n\"\n}\n\nfunction okta_login {\n  echo \"logging in to okta ...\"\n  okta-artifactory-login\n  echo -e \"done\\\\n\"\n}\n\nfunction detect_docker_registry {\n  docker_registry=\"$1\"; shift\n  if [ -z \"${docker_registry}\" ]; then\n    echo 'none'\n  elif echo \"${docker_registry}\" | grep 'localhost:' > /dev/null; then\n    echo 'local'\n  elif echo \"${docker_registry}\" | grep '\\.dkr\\.ecr\\.' > /dev/null; then\n    echo 'ecr'\n  elif echo \"${docker_registry}\" | grep 'gcr\\.io' > /dev/null; then\n    echo 'gcr'\n  else\n    echo 'unknown'\n  fi\n}\n"
  },
  {
    "path": "dev/selinux-bpf.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nif [ \"$EUID\" -ne 0 ]; then\n   echo \"This script must be run as root\"\n   exit 1\nfi\n\nresult=$(which selinuxenabled) || true\nif [[ \"$result\" == \"\" ]]\nthen\n  echo \"selinuxenabled command not found - exiting\"\n  exit 0\nfi\n\nif ! selinuxenabled\nthen\n    echo \"SELinux is not enabled - exiting\"\n    exit 0\nfi\n\ntmp_dir=$(mktemp -d -t EBPF_NET-XXXXX)\n\ncat > \"${tmp_dir}/spc_bpf_allow.te\" <<END\nmodule spc_bpf_allow 1.0;\nrequire {\n    type spc_t;\n    class bpf {map_create map_read map_write prog_load prog_run};\n}\n#============= spc_t ==============\nallow spc_t self:bpf { map_create map_read map_write prog_load prog_run };\nEND\ncheckmodule -M -m -o \"${tmp_dir}/spc_bpf_allow.mod\" \"${tmp_dir}/spc_bpf_allow.te\"\nsemodule_package -o \"${tmp_dir}/spc_bpf_allow.pp\" -m \"${tmp_dir}/spc_bpf_allow.mod\"\nsemodule -i \"${tmp_dir}/spc_bpf_allow.pp\"\n"
  },
  {
    "path": "dev/strip-symbols.sh",
    "content": "#!/bin/bash -e\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nfunction print_help {\n  echo \"usage: $0 binary-file [out-dir]\" >&2\n  echo >&2\n  echo 'outputs stripped binary in ${BINARY_FILE}-stripped' >&2\n  echo 'outputs debug symbols into symbols/${MODULE}/${BINARY_FILE}-stripped.sym' >&2\n}\n\nbinary_file=\"\"\nexport_symbols=false\nupload_bucket=\"\"\nout_dir=\".\"\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n\n  case \"${arg}\" in\n    --export)\n      export_symbols=true\n      ;;\n\n    --upload)\n      if [[ \"$#\" -lt 1 ]]; then\n        print_help\n        echo\n        echo \"ERROR: missing S3 bucket after '--upload'\"\n        exit 1\n      fi\n      upload_bucket=\"$1\"; shift\n      export_symbols=true\n      ;;\n\n    *)\n      if [[ -z \"${binary_file}\" ]]; then\n        binary_file=\"${arg}\"\n      elif [[ -z \"${out_dir}\" ]]; then\n        out_dir=\"${arg}\"\n      else\n        print_help\n        echo\n        echo \"ERROR: unexpected arguments '${arg} $*'\"\n        exit 1\n      fi\n      ;;\n  esac\ndone\n\nif [[ -z \"${binary_file}\" ]] || [[ ! -e \"${binary_file}\" ]]; then\n  print_help\n  echo\n  echo \"ERROR: missing or inexistent binary file '${binary_file}'\"\n  exit 1\nfi\n\nmodule_name=\"$(basename \"${binary_file}\")\"\nstripped_file=\"${binary_file}-stripped\"\nsymbol_file=\"${module_name}.sym\"\n\nmodule_id=\"$(dump_syms -i \"${binary_file}\" | grep MODULE | cut -d ' ' -f 4)\"\nsymbol_dir=\"${module_name}/${module_id}\"\nsymbol_path=\"symbols/${symbol_dir}/${symbol_file}\"\n\ncat > \"debug-info.conf\" <<EOF\nexport EBPF_NET_DEBUG_MODULE_NAME=\"${module_name}\"\nexport EBPF_NET_DEBUG_MODULE_ID=\"${module_id}\"\nEOF\n\necho \"stripping debug symbols from '${binary_file}' into '${stripped_file}'...\" >&2\nstrip -s -o \"${stripped_file}\" \"${binary_file}\" >&2\n\nif [[ \"${export_symbols}\" == true ]]; then\n  mkdir -p \"symbols/${symbol_dir}\" >&2\n  echo \"exporting debug symbols from '${binary_file}' into '${symbol_path}'...\" >&2\n  dump_syms \"${binary_file}\" > \"${symbol_path}\"\n\n  if [[ -n \"${upload_bucket}\" ]]; then\n    if [[ -z \"${AWS_ACCESS_KEY_ID}\" ]] || [[ -z \"${AWS_SECRET_ACCESS_KEY}\" ]]; then\n      echo \"ERROR: AWS credentials missing - environment variables AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY not properly set up\"\n    fi\n\n    echo \"uploading debug symbols '${symbol_dir}' to S3 using AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}'\"\n\n    (set -x; aws s3api put-object \\\n      --bucket \"${upload_bucket}\" \\\n      --key \"${symbol_dir}/${symbol_file}\" \\\n      --body \"${symbol_path}\" \\\n    )\n  fi\nfi\n\necho \"${stripped_file}\"\n"
  },
  {
    "path": "dist/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset(SYSTEMD_UNIT_DIR \"/usr/lib/systemd/system\")\nset(CONFIG_DIR \"/etc/opentelemetry-ebpf\")\n\n################################################################################\n# Reducer\n#\n\nstring(JOIN \";\" CPACK_DEBIAN_REDUCER_PACKAGE_CONTROL_EXTRA\n  \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/deb/conffiles\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/deb/postinst\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/deb/prerm\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/deb/postrm\"\n)\n\nset(CPACK_RPM_REDUCER_USER_FILELIST\n  \"%config(noreplace) ${CONFIG_DIR}/reducer.args\"\n  \"%config(noreplace) ${CONFIG_DIR}/reducer.yaml\"\n)\n\nset(CPACK_RPM_REDUCER_POST_INSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/rpm/post.sh\")\nset(CPACK_RPM_REDUCER_PRE_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/rpm/preun.sh\")\nset(CPACK_RPM_REDUCER_POST_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/reducer/rpm/postun.sh\")\n\ninstall(\n  FILES\n    reducer/reducer.service\n  DESTINATION ${SYSTEMD_UNIT_DIR}\n  COMPONENT reducer\n)\n\ninstall(\n  FILES\n    reducer/reducer.args\n    reducer/reducer.yaml\n  DESTINATION ${CONFIG_DIR}\n  COMPONENT reducer\n)\n\nget_target_property(REDUCER_BIN reducer-stripped OUTPUT)\ninstall(\n  PROGRAMS ${REDUCER_BIN}\n  DESTINATION ${CMAKE_INSTALL_BINDIR}\n  RENAME reducer\n  COMPONENT reducer\n)\n\n\n################################################################################\n# Kernel Collector\n#\n\nset(CPACK_RPM_KERNEL-COLLECTOR_PACKAGE_REQUIRES \"bash, kernel-devel\")\nset(CPACK_DEBIAN_KERNEL-COLLECTOR_PACKAGE_DEPENDS \"bash, linux-headers\")\n\nstring(JOIN \";\" CPACK_DEBIAN_KERNEL-COLLECTOR_PACKAGE_CONTROL_EXTRA\n  \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/deb/conffiles\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/deb/postinst\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/deb/prerm\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/deb/postrm\"\n)\n\nset(CPACK_RPM_KERNEL-COLLECTOR_USER_FILELIST\n  \"%config(noreplace) ${CONFIG_DIR}/kernel-collector.yaml\"\n  \"%config(noreplace) ${CONFIG_DIR}/kernel-collector.args\"\n)\n\nset(CPACK_RPM_KERNEL-COLLECTOR_POST_INSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/rpm/post.sh\")\nset(CPACK_RPM_KERNEL-COLLECTOR_PRE_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/rpm/preun.sh\")\nset(CPACK_RPM_KERNEL-COLLECTOR_POST_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/kernel-collector/rpm/postun.sh\")\n\ninstall(\n  FILES\n    kernel-collector/kernel-collector.service\n  DESTINATION ${SYSTEMD_UNIT_DIR}\n  COMPONENT kernel-collector\n)\n\ninstall(\n  FILES\n    kernel-collector/kernel-collector.args\n    kernel-collector/kernel-collector.yaml\n  DESTINATION ${CONFIG_DIR}\n  COMPONENT kernel-collector\n)\n\nget_target_property(KERNEL_COLLECTOR_BIN kernel-collector-stripped OUTPUT)\ninstall(\n  PROGRAMS ${KERNEL_COLLECTOR_BIN}\n  DESTINATION ${CMAKE_INSTALL_BINDIR}\n  RENAME kernel-collector\n  COMPONENT kernel-collector\n)\n\n\n################################################################################\n# Cloud Collector\n#\n\nstring(JOIN \";\" CPACK_DEBIAN_CLOUD-COLLECTOR_PACKAGE_CONTROL_EXTRA\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/deb/conffiles\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/deb/postinst\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/deb/prerm\"\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/deb/postrm\"\n)\n\nset(CPACK_RPM_CLOUD-COLLECTOR_USER_FILELIST\n  \"%config(noreplace) ${CONFIG_DIR}/cloud-collector.yaml\"\n  \"%config(noreplace) ${CONFIG_DIR}/cloud-collector.args\"\n)\n\nset(CPACK_RPM_CLOUD-COLLECTOR_POST_INSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/rpm/post.sh\")\nset(CPACK_RPM_CLOUD-COLLECTOR_PRE_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/rpm/preun.sh\")\nset(CPACK_RPM_CLOUD-COLLECTOR_POST_UNINSTALL_SCRIPT_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/cloud-collector/rpm/postun.sh\")\n\ninstall(\n  FILES\n    cloud-collector/cloud-collector.service\n  DESTINATION ${SYSTEMD_UNIT_DIR}\n  COMPONENT cloud-collector\n)\n\ninstall(\n  FILES\n    cloud-collector/cloud-collector.args\n    cloud-collector/cloud-collector.yaml\n  DESTINATION ${CONFIG_DIR}\n  COMPONENT cloud-collector\n)\n\nget_target_property(CLOUD_COLLECTOR_BIN cloud-collector-stripped OUTPUT)\ninstall(\n  PROGRAMS ${CLOUD_COLLECTOR_BIN}\n  DESTINATION ${CMAKE_INSTALL_BINDIR}\n  RENAME cloud-collector\n  COMPONENT cloud-collector\n)\n\n\n################################################################################\n# Packaging\n#\n\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"OpenTelemetry-eBPF\")\nset(CPACK_PACKAGE_DESCRIPTION\n\"The OpenTelemetry eBPF project develops components that collect and analyze\ntelemetry from the operating system, cloud, and container orchestrators. Its initial focus\nis on collecting network data to enable users to gain insight into their distributed\napplications.\")\nset(CPACK_PACKAGE_VENDOR \"OpenTelemetry\")\nset(CPACK_PACKAGE_CONTACT \"https://github.com/orgs/open-telemetry/teams/ebpf-maintainers\")\nset(CPACK_PACKAGE_HOMEPAGE_URL \"https://github.com/open-telemetry/opentelemetry-ebpf\")\n\nset(CPACK_RESOURCE_FILE_README \"${PROJECT_SOURCE_DIR}/README.md\")\nset(CPACK_RESOURCE_FILE_LICENSE \"${PROJECT_SOURCE_DIR}/LICENSE.txt\")\n\nset(CPACK_GENERATOR \"TGZ;RPM;DEB\")\nset(CPACK_COMPONENTS_GROUPING IGNORE) # one package per component\nset(CPACK_RPM_COMPONENT_INSTALL ON)\nset(CPACK_DEB_COMPONENT_INSTALL ON)\n\nset(CPACK_RPM_FILE_NAME \"RPM-DEFAULT\")\nset(CPACK_DEBIAN_FILE_NAME \"DEB-DEFAULT\")\n\nset(CPACK_RPM_PACKAGRE_REQUIRES \"bash\")\nset(CPACK_DEBIAN_PACKAGE_DEPENDS \"bash\")\n\ninclude(CPack)\n\ncpack_add_component(\n  reducer\n  DESCRIPTION\n    \"OpenTelemetry-eBPF Reducer.\\n${CPACK_PACKAGE_DESCRIPTION}\"\n)\n\ncpack_add_component(\n  kernel-collector\n  DESCRIPTION\n    \"OpenTelemetry-eBPF Kernel Collector.\\n${CPACK_PACKAGE_DESCRIPTION}\"\n)\n\ncpack_add_component(\n  cloud-collector\n  DESCRIPTION\n    \"OpenTelemetry-eBPF Cloud Collector.\\n${CPACK_PACKAGE_DESCRIPTION}\"\n)\n\ncpack_add_component(tools DESCRIPTION \"OpenTelemetry-eBPF tools\")\n"
  },
  {
    "path": "dist/cloud-collector/cloud-collector.args",
    "content": "--config-file=/etc/opentelemetry-ebpf/cloud-collector.yaml\n--log-console\n"
  },
  {
    "path": "dist/cloud-collector/cloud-collector.service",
    "content": "[Unit]\nDescription=OpenTelemetry-eBPF Cloud Collector\nAfter=network.target\n\n[Service]\nType=exec\nExecStart=/bin/bash -c 'exec /usr/bin/cloud-collector $(< /etc/opentelemetry-ebpf/cloud-collector.args)'\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "dist/cloud-collector/cloud-collector.yaml",
    "content": "intake:\n  host: 127.0.0.1\n  port: 8000\n"
  },
  {
    "path": "dist/cloud-collector/deb/conffiles",
    "content": "/etc/opentelemetry-ebpf/cloud-collector.args\n/etc/opentelemetry-ebpf/cloud-collector.yaml\n"
  },
  {
    "path": "dist/cloud-collector/deb/postinst",
    "content": "#!/bin/sh\nset -e\n\nservice='cloud-collector.service'\n\nif [ \"$1\" = \"configure\" ] || [ \"$1\" = \"abort-upgrade\" ] || [ \"$1\" = \"abort-deconfigure\" ] || [ \"$1\" = \"abort-remove\" ] ; then\n  deb-systemd-helper unmask $service >/dev/null || true\n\n  if deb-systemd-helper --quiet was-enabled $service; then\n    deb-systemd-helper enable $service >/dev/null || true\n  else\n    deb-systemd-helper update-state $service >/dev/null || true\n  fi\n\n  if [ -d /run/systemd/system ]; then\n    systemctl --system daemon-reload >/dev/null || true\n\n    if [ -n \"$2\" ]; then\n      action=restart\n    else\n      action=start\n    fi\n    deb-systemd-invoke $action $service >/dev/null || true\n  fi\nfi\n"
  },
  {
    "path": "dist/cloud-collector/deb/postrm",
    "content": "#!/bin/sh\nset -e\n\nservice='cloud-collector.service'\n\nif [ -d /run/systemd/system ]; then\n  systemctl --system daemon-reload >/dev/null || true\nfi\n\nif [ \"$1\" = \"remove\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper mask $service >/dev/null\n  fi\nfi\n\nif [ \"$1\" = \"purge\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper purge $service >/dev/null\n    deb-systemd-helper unmask $service >/dev/null\n  fi\nfi\n"
  },
  {
    "path": "dist/cloud-collector/deb/prerm",
    "content": "#!/bin/sh\nset -e\n\nservice='cloud-collector.service'\n\nif [ -d /run/systemd/system ] && [ \"$1\" = remove ]; then\n  deb-systemd-invoke stop $service >/dev/null || true\nfi\n"
  },
  {
    "path": "dist/cloud-collector/rpm/post.sh",
    "content": "service=cloud-collector.service\nif [ $1 -eq 1 ]; then\n  # install\n  systemctl preset $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/cloud-collector/rpm/postun.sh",
    "content": "service=cloud-collector.service\nsystemctl daemon-reload >/dev/null 2>&1 || :\nif [ $1 -ge 1 ]; then\n  # upgrade\n  systemctl try-restart $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/cloud-collector/rpm/preun.sh",
    "content": "service=cloud-collector.service\nif [ $1 -eq 0 ]; then\n  # uninstall\n  systemctl --no-reload disable $service > /dev/null 2>&1 || :\n  systemctl stop $service > /dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/kernel-collector/deb/conffiles",
    "content": "/etc/opentelemetry-ebpf/kernel-collector.yaml\n/etc/opentelemetry-ebpf/kernel-collector.args\n"
  },
  {
    "path": "dist/kernel-collector/deb/postinst",
    "content": "#!/bin/sh\nset -e\n\nservice='kernel-collector.service'\n\nif [ \"$1\" = \"configure\" ] || [ \"$1\" = \"abort-upgrade\" ] || [ \"$1\" = \"abort-deconfigure\" ] || [ \"$1\" = \"abort-remove\" ] ; then\n  deb-systemd-helper unmask $service >/dev/null || true\n\n  if deb-systemd-helper --quiet was-enabled $service; then\n    deb-systemd-helper enable $service >/dev/null || true\n  else\n    deb-systemd-helper update-state $service >/dev/null || true\n  fi\n\n  if [ -d /run/systemd/system ]; then\n    systemctl --system daemon-reload >/dev/null || true\n\n    if [ -n \"$2\" ]; then\n      action=restart\n    else\n      action=start\n    fi\n    deb-systemd-invoke $action $service >/dev/null || true\n  fi\nfi\n"
  },
  {
    "path": "dist/kernel-collector/deb/postrm",
    "content": "#!/bin/sh\nset -e\n\nservice='kernel-collector.service'\n\nif [ -d /run/systemd/system ]; then\n  systemctl --system daemon-reload >/dev/null || true\nfi\n\nif [ \"$1\" = \"remove\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper mask $service >/dev/null\n  fi\nfi\n\nif [ \"$1\" = \"purge\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper purge $service >/dev/null\n    deb-systemd-helper unmask $service >/dev/null\n  fi\nfi\n"
  },
  {
    "path": "dist/kernel-collector/deb/prerm",
    "content": "#!/bin/sh\nset -e\n\nservice='kernel-collector.service'\n\nif [ -d /run/systemd/system ] && [ \"$1\" = remove ]; then\n  deb-systemd-invoke stop $service >/dev/null || true\nfi\n"
  },
  {
    "path": "dist/kernel-collector/kernel-collector.args",
    "content": "--config-file=/etc/opentelemetry-ebpf/kernel-collector.yaml\n--log-console\n"
  },
  {
    "path": "dist/kernel-collector/kernel-collector.service",
    "content": "[Unit]\nDescription=OpenTelemetry-eBPF Kernel Collector\nAfter=network.target\n\n[Service]\nType=exec\nExecStart=/bin/bash -c 'exec /usr/bin/kernel-collector $(< /etc/opentelemetry-ebpf/kernel-collector.args)'\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "dist/kernel-collector/kernel-collector.yaml",
    "content": "intake:\n  host: 127.0.0.1\n  port: 8000\n\n#labels:\n#  environment: myenv\n#  zone: myzone\n#  service: myservice\n#  host: myhost\n"
  },
  {
    "path": "dist/kernel-collector/rpm/post.sh",
    "content": "service=kernel-collector.service\nif [ $1 -eq 1 ]; then\n  # install\n  systemctl preset $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/kernel-collector/rpm/postun.sh",
    "content": "service=kernel-collector.service\nsystemctl daemon-reload >/dev/null 2>&1 || :\nif [ $1 -ge 1 ]; then\n  # upgrade\n  systemctl try-restart $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/kernel-collector/rpm/preun.sh",
    "content": "service=kernel-collector.service\nif [ $1 -eq 0 ]; then\n  # uninstall\n  systemctl --no-reload disable $service > /dev/null 2>&1 || :\n  systemctl stop $service > /dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/reducer/deb/conffiles",
    "content": "/etc/opentelemetry-ebpf/reducer.args\n/etc/opentelemetry-ebpf/reducer.yaml\n"
  },
  {
    "path": "dist/reducer/deb/postinst",
    "content": "#!/bin/sh\nset -e\n\nservice='reducer.service'\n\nif [ \"$1\" = \"configure\" ] || [ \"$1\" = \"abort-upgrade\" ] || [ \"$1\" = \"abort-deconfigure\" ] || [ \"$1\" = \"abort-remove\" ] ; then\n  deb-systemd-helper unmask $service >/dev/null || true\n\n  if deb-systemd-helper --quiet was-enabled $service; then\n    deb-systemd-helper enable $service >/dev/null || true\n  else\n    deb-systemd-helper update-state $service >/dev/null || true\n  fi\n\n  if [ -d /run/systemd/system ]; then\n    systemctl --system daemon-reload >/dev/null || true\n\n    if [ -n \"$2\" ]; then\n      action=restart\n    else\n      action=start\n    fi\n    deb-systemd-invoke $action $service >/dev/null || true\n  fi\nfi\n"
  },
  {
    "path": "dist/reducer/deb/postrm",
    "content": "#!/bin/sh\nset -e\n\nservice='reducer.service'\n\nif [ -d /run/systemd/system ]; then\n  systemctl --system daemon-reload >/dev/null || true\nfi\n\nif [ \"$1\" = \"remove\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper mask $service >/dev/null\n  fi\nfi\n\nif [ \"$1\" = \"purge\" ]; then\n  if [ -x \"/usr/bin/deb-systemd-helper\" ]; then\n    deb-systemd-helper purge $service >/dev/null\n    deb-systemd-helper unmask $service >/dev/null\n  fi\nfi\n"
  },
  {
    "path": "dist/reducer/deb/prerm",
    "content": "#!/bin/sh\nset -e\n\nservice='reducer.service'\n\nif [ -d /run/systemd/system ] && [ \"$1\" = remove ]; then\n  deb-systemd-invoke stop $service >/dev/null || true\nfi\n"
  },
  {
    "path": "dist/reducer/reducer.args",
    "content": "--config-file=/etc/opentelemetry-ebpf/reducer.yaml\n--log-console\n"
  },
  {
    "path": "dist/reducer/reducer.service",
    "content": "[Unit]\nDescription=OpenTelemetry-eBPF Reducer\nAfter=network.target\n\n[Service]\nType=exec\nExecStart=/bin/bash -c 'exec /usr/bin/reducer $(< /etc/opentelemetry-ebpf/reducer.args)'\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "dist/reducer/reducer.yaml",
    "content": "# TCP port to listen on for incoming connections from collectors.\ntelemetry_port: 8000\n\n# How many ingest shards to run.\nnum_ingest_shards: 1\n\n# How many matching shards to run.\nnum_matching_shards: 1\n\n# How many aggregation shards to run.\nnum_aggregation_shards: 1\n\n# How many partitions per aggregation shard to write metrics into.\npartitions_per_shard: 1\n\n# Enables id-id timeseries generation.\nenable_id_id: false\n\n# Enables az-id timeseries generation.\nenable_az_id: false\n\n# Enables exporting metric flow logs.\nenable_flow_logs: false\n\n# Enables OTLP gRPC metrics output.\nenable_otlp_grpc_metrics: false\n\n# Network address to send OTLP gRPC metrics.\notlp_grpc_metrics_address: \"localhost\"\n\n# TCP port to send OTLP gRPC metrics.\notlp_grpc_metrics_port: 4317\n\n# Size, in bytes, of batches in which OTLP metrics are sent over gRPC.\notlp_grpc_batch_size: 1000\n\n# Enables sending metric descriptions in OTLP gRPC metrics output.\nenable_otlp_grpc_metric_descriptions: false\n\n# Disables prometheus metrics output.\ndisable_prometheus_metrics: false\n\n# Partitions prometheus metrics.\nshard_prometheus_metrics: false\n\n# Bind address for Prometheus.\nprom_bind: \"0.0.0.0:7010\"\n\n# Maximum size of a scrape response, in bytes.\n# Unlimited if not specified.\n#scrape_size_limit_bytes: 0\n\n# Bind address for Internal Prometheus.\ninternal_prom_bind: \"0.0.0.0:7001\"\n\n# Maximum size of internal stats scrape response, in bytes.\n# Unlimited if not specified.\n#stats_scrape_size_limit_bytes: 0\n\n# Format of TSDB data for scraped metrics.\nscrape_metrics_tsdb_format: \"prometheus\"\n\n# Disables the IP addresses field in node spans.\ndisable_node_ip_field: false\n\n# Enables using IP addresses for autonomous systems.\nenable_autonomous_system_ip: false\n\n# Path to the GeoLite2 ASN database file.\n# Disabled if not specified.\n#geoip_path: \"\"\n\n# Enables enrichment using AWS metadata received from the Cloud Collector.\nenable_aws_enrichment: false\n\n# Enables computation and output of pXX latency timeseries.\nenable_percentile_latencies: false\n\n# Comma-separated list of metrics to disable.\n# A metric group can also be disabled. To do so, specify '<group>.all', where <group> is one of: tcp,udp,dns,http.\n# A value of 'none' can be given to enable all metrics.\n# If this argument is not specified, the recommended collection of metrics will be used.\n# Example: 'http.all,dns.all,udp.drops'. This will disable all http metrics, all dns metrics, and the udp.drops metric.\n#disable_metrics: \"\"\n\n# Comma-separated list of metrics to enable.\n# This list is processed AFTER disable-metrics.\n# A metric group can also be enabled. To do so, specify '<group>.all', where <group> is one of: tcp,udp,dns,http.\n# Example: 'http.all,dns.all,udp.drops'. This will enable all http metrics, all dns metrics, and the udp.drops metric.\n#enable_metrics: \"\"\n\n# Interval (in seconds) to generate a JSON dump of the span indexes for each core.\n# A value of 0 disables index dumping.\nindex_dump_interval: 0\n"
  },
  {
    "path": "dist/reducer/rpm/post.sh",
    "content": "service=reducer.service\nif [ $1 -eq 1 ]; then\n  # install\n  systemctl preset $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/reducer/rpm/postun.sh",
    "content": "service=reducer.service\nsystemctl daemon-reload >/dev/null 2>&1 || :\nif [ $1 -ge 1 ]; then\n  # upgrade\n  systemctl try-restart $service >/dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "dist/reducer/rpm/preun.sh",
    "content": "service=reducer.service\nif [ $1 -eq 0 ]; then\n  # uninstall\n  systemctl --no-reload disable $service > /dev/null 2>&1 || :\n  systemctl stop $service > /dev/null 2>&1 || :\nfi\n"
  },
  {
    "path": "docs/cloud-collector.md",
    "content": "# Cloud Collector #\n\nThe _cloud collector_ gathers metadata from a cloud provider. Currently supported cloud providers are AWS and GCP.\n\n\n## Running ##\n\nTo list all the available command-line options:\n\n```\n$ cloud-collector --help\n```\n\nThe `EBPF_NET_INTAKE_HOST` and `EBPF_NET_INTAKE_PORT` environment variables are required and must be set before running:\n\n```\n$ export EBPF_NET_INTAKE_HOST=192.168.0.101\n$ export EBPF_NET_INTAKE_PORT=8000\n$ cloud-collector –-no-log-file --log-console\n```\n\nThe `EBPF_NET_INTAKE_HOST` environment variable should point to the IP address or hostname of a running reducer instance\n\n\n## Environment variables ##\n\n- `EBPF_NET_INTAKE_HOST`: IP address or host name of the reducer to which telemetry is to be sent.\n- `EBPF_NET_INTAKE_PORT`: TCP port number on which the reducer is listening for collector connections. Usually 8000.\n- `EBPF_NET_DATA_DIR`: Directory in which the program will read and potentially write data files.\n  If not specified the current working directory will be used.\n- `EBPF_NET_LOG_FILE_PATH`: Location of the file in which logging messages are written. Default value is /var/log/ebpf_net.log.\n"
  },
  {
    "path": "docs/data-model.md",
    "content": "# Data model\n\n## Observability: data model\n\n**Three types: Tracing, Metrics, Logging.** One [analysis](https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html) divides application observability into three types:\n\n![](./assets/tracing-metrics-logs.png)\n\n* Logging collects raw event data. This is usually collected in a textual form.\n* Metrics give aggregate counts of quantities \\(number of events per second\\)\n* Tracing involves `spans`: operations with a duration, and their causality relationships \\(i.e., which span caused which other span\\).\n\n**Unifying theory: Events.** As Zipkin developer Adrian Cole [noted](https://speakerdeck.com/adriancole/observability-3-ways-logging-metrics-and-tracing?slide=2) in a [talk](https://www.dotconferences.com/2017/04/adrian-cole-observability-3-ways-logging-metrics-tracing), all of these are derived from events:\n\n![](./assets/unifying-theory.png)\n\nLooking at the different observability types, logging captures raw events; Metrics capture event projections \\(e.g., sum, average, p95\\), and Tracing captures events and causality.\n\n**Context.** Each event has a context in which it happened. This can be \\(1\\) causal context, for example the original user transaction that caused it or the RPC it is part of, or \\(2\\) environmental context: the software version of the process serving the request, which thread was used, the VM instance, etc.\n\n**Tracing already includes Logging.** The OpenTracing specification allows for [Span Logs](https://github.com/opentracing/specification/blob/master/specification.md): each span can contain zero or more key-value maps \\(each with a timestamp\\). So tracing actually allows Logs -- they just have a `span` context. We adopt this construction: events are always related to a span. Events can start a span, end a span, or notify the span of point events.\n\n**Metrics summarize events: time based aggregation.** By dividing time into windows \\(e.g., 1-second intervals\\), it is possible to aggregate events with common context into a metric such as `sum`, `average` or `p95`. For example, when capturing individual packet receptions, it is possible to aggregate the total number of received bytes per second for each application to get an `rx-bytes-per-second` metric per application.\n\n**Metrics are also events: time-based roll-ups.** Metrics aggregated over a time interval are, in essence, an event generated at the end of each time interval that carries the information from the aggregated metric. Thus, metrics can summarize a high frequency event stream to reduce its volume to a lower frequency event stream. Applying time-based aggregation with increasing intervals, for example 1 second, 10 second, 1 minute, 5 minutes, creates _roll-ups_ with lower and lower data volumes.\n\n**Slicing and Dicing: GROUP BY and filters.** As mentioned above, metrics have associated context. For example, when measuring communication performance, it is possible to associate the metric to the IPs, ports, and the container, process, and thread. Given this detailed context, one can aggregate metrics according to fewer attributes: for example, given processes and IP addresses, it is possible to group all records by IP address and get a per-IP address metric.\n\nThis is also true for Logging and Tracing: it is possible to have high fidelity logs, and then forget some context information for events.\n\n**Enriching data.** One event stream can enrich another stream, providing more context for events. For example, an agent running on a host can produce information about the containers and sockets on that host \\(the first event stream\\). Kubernetes can produce a stream of Pod events \\(the second stream\\), which contain containers information: the name of the deployment, the container image and its tag, etc. Enriching the socket stream with Pod information will create a socket event stream with better context, where each socket event also has the deployment/image/tags context \\(and available for slicing and dicing\\).\n\n**Matching data streams.** A special case of enrichment, is where two sides of communication are matched. In regular enrichment, there is a main stream and a metadata stream \\(socket events and pod information\\). With matching, there are two main streams. Each one is useful on its own, but matching the two together provides a better enriched stream, for example each socket stream knows the process/container on the host that is providing the stream, but not on the other end. Together, process/container information from both ends is available.\n\n## Available data\n\nData is collected on:\n\n* Containers, from the cgroup subsystem\n* Processes and threads, with container and parent process context\n* TCP sockets and socket activity \\(throughput, rtt, drops\\), with process context\n* UDP sockets and socket activity \\(throughput, packets\\), with process context\n* NAT records with socket context.\n* Kubernetes Pods and ReplicaSets\n* Docker information on containers\n* AWS metadata, e.g.,`describe-network-interfaces`\n\n## Query functionality\n\n**Filters and aggregations.** Users should be able to query the communication in the system using filters and group bys from:\n\n![](./assets/aggregations.png)\n\n**Macro to micro scope.** Queries can span one minute or one month.\n\n**Distribution queries.** Users should be able to query the p95, p99, p99.9 of communications, and examine distributions.\n\n**Raw data export.** Users should be able to export raw enriched data to other systems, e.g., for intrusion detection.\n\n**Examples and outliers \\(future\\).** Users should be able to get an example of a raw measurement \\(e.g., a specific socket between two services\\) and specifically of outliers \\(which socket had high round-trip time\\).\n\n**Historical analysis \\(future\\).** Compare the time-series returned from a query to historical their values 1 hour, 1 week, 1 year ago, etc.\n\n**Comparative analysis \\(future\\).** For all time series returned from a query, compute a metric of their relative behavior, for example load skew between different replicas of the same service.\n\n"
  },
  {
    "path": "docs/developing.md",
    "content": "# Developer Guide #\n\nOpenTelemetry-eBPF code is written mostly in the C++ language, with one\ncomponent written in Go and the [Render](render.md) code-generation tool is\nwritten in [Xtend](https://www.eclipse.org/xtend/), a Java dialect.\n\nWe use CMake for build automation.\n\n\n## Prerequisites ##\n\nA relatively newer version of GNU bash is required; the bash that is comes with\nmacOS will not do. You can install one from [Homebrew](https://brew.sh/) or\n[MacPorts](https://www.macports.org/).\n\nFor building the project [CMake](https://cmake.org/) and [Docker](https://www.docker.com/)\nare required.\n\n\n## Build Environment ##\n\nThe _build environment_ container image (A.K.A. _build-env_ or _benv_) packs all\nthe necessary tools and dependencies that are required to build the project and\nrun the unit tests. This provides us with repeatable builds and should generaly\nmake building the project easier.\n\nYou can build the build environment image yourself or use a prebuilt image that\nthe OpenTelemetry-eBPF team publishes.\n\nThe only prerequisites for building the image are CMake, Docker and a fair\namount of disk space and patience.\n\n```\ngit clone https://github.com/open-telemetry/opentelemetry-ebpf-build-tools.git\ncd opentelemetry-ebpf-build-tools\ngit submodule update --init --recursive\n./build.sh\n```\n\nThe resulting image will be tagged as `build-env:latest`.\n\nTo save time and space, the latest version can be pulled from from\n`quay.io/splunko11ytest/network-explorer-debug/build-env`:\n\n```\ndocker pull quay.io/splunko11ytest/network-explorer-debug/build-env\ndocker tag quay.io/splunko11ytest/network-explorer-debug/build-env build-env:latest\n```\n\n\n## Building The Project ##\n\nBefore building, make sure that all submodules are checked-out:\n\n```\ncd opentelemetry-ebpf\ngit submodule update --init --recursive\n```\n\nBuilding the project is done from within the build environment docker container.\n\nThe `EBPF_NET_SRC_ROOT` environment variable must be set so that it points to\nthe source repository's top-level directory. When starting a build environment\ncontainer, the opentelemetry-ebpf repository is usually mounted to container's\n`/root/src`, and the `EBPF_NET_SRC_ROOT` environment variable is set\naccordingly.\n\nThe container's default build output directory is `/root/out`. It can be changed\nby setting the `EBPF_NET_OUT_DIR` environment variable, if necessary.\nA host directory can be mounted as the output directory to preserve intermediate\nbuild files and to have access to raw build artifacts.\n\nSome build targets produce docker images. To utilize the host's docker daemon\nfor building docker images from inside the build-env container, we need to mount\nthe host's docker socket. The docker socket's path is usually located at\n`/var/run/docker.sock`.\n\nExample starting the build-env:\n\n```\ncd opentelemetry-ebpf\nmkdir -p ../build\ndocker run -it --rm \\\n  --env EBPF_NET_SRC_ROOT=/root/src \\\n  --mount type=bind,source=$PWD,destination=/root/src,readonly \\\n  --mount type=bind,source=$PWD/../build,destination=/root/out \\\n  --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \\\n  --name benv \\\n  build-env\n```\n\nThe `build.sh` script located inside the container (itself a link to\n`$EBPF_NET_SRC_ROOT/dev/benv-build.sh`) is used to initiate a build or to\npreconfigure it (the `--cmake` option). To list all the available command-line\noptions:\n\n```\n./build.sh --help\n```\n\nSpecifying the `--clean` command-line parameter will cause the previous build to\nbe thoroughly cleaned-out before starting a new one.\n\nThe `--debug` option causes the project to be build in the _Debug_ mode, with\ncompiler optimizations are turned of. If not specified, the default build mode\nis _RelWithDebInfo_, which turns on compiler optimizations but leaves debug\ninformation inside binaries.\n\nThe `--cmake` option stops the script after configuring the build. You can then\n`cd` to the output directory and issue `make` commands manually.\nFor example (inside the build-env container):\n\n```\n./build.sh --cmake\ncd out\nmake render_compiler\n```\n\nThe `build.sh` script accepts a list of targets to be built. The following\ntargets build their respective artifacts:\n\n- `reducer`: builds the [reducer](reducer.md) executable\n- `kernel-collector`: builds the [kernel-collector](kernel-collector.md) executable\n- `cloud-collector`: builds the [cloud-collector](cloud-collector.md) executable\n- `k8s-watcher`: builds the k8s-watcher executable, part of the [k8s-collector](k8s-collector.md)\n- `k8s-relay`: builds the k8s-relay executable, part of the [k8s-collector](k8s-collector.md)\n\nThe `pipeline` target builds all the aforementioned artifacts. The\n`pipeline-docker` target builds the docker images of all components using\nhost's docker daemon (only works if host's docker socket is mounted).\nThe resulting docker images will be placed in the host's docker daemon under the\nfollowing tags: `reducer`, `kernel-collector`, `cloud-collector`, `k8s-wather`\nand `k8s-relay`.\n\nThe `pipeline-docker-registry` target also causes built container images to be\npushed to the docker registry configured through the `EBPF_NET_DOCKER_REGISTRY`\nenvironment variable. The default is `localhost:5000`. For that to work a\n[local registry](https://docs.docker.com/registry/deploying/#run-a-local-registry)\nmust be running.\n\nFor example:\n\n```\n# start local registry\ndocker run -d -p 5000:5000 --name registry registry:2\n\n# build the project, pushing docker images to localhost:5000\ncd opentelemetry-ebpf\ndocker run -it --rm \\\n  --env EBPF_NET_SRC_ROOT=/root/src \\\n  --mount type=bind,source=$PWD,destination=/root/src,readonly \\\n  --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \\\n  --name benv \\\n  build-env ./build.sh pipeline-docker-registry\n```\n\n\n## Running The Unit Tests ##\n\nThe `unit_tests` target builds the unit tests; the `test` target runs them.\n\nExample (inside the build-env container):\n\n```\ncd $HOME/out\nmake unit_tests\nmake test\n```\n\n### Running Kernel Collector tests ###\n\nBy default, unit tests do not run all kernel collector tests.\n\nTo run these tests, you will need to run the Docker container with the `--privileged` flag, additional mount points and the environment variable `RUN_EBPF_TESTS` set to `true`.\n\nTaken together, the docker run command will now look like the following:\n```\ndocker run -it --rm \\\n  --env EBPF_NET_SRC_ROOT=/root/src \\\n  --mount type=bind,source=$PWD,destination=/root/src,readonly \\\n  --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \\\n  --mount \"type=bind,source=/lib/modules,destination=/lib/modules,readonly\" \\\n  --mount \"type=bind,source=/usr/src,destination=/usr/src,readonly\" \\\n  --mount \"type=bind,source=/sys/kernel,destination=/sys/kernel,readonly\" \\\n  --mount \"type=bind,source=/sys/fs/cgroup,destination=/hostfs/sys/fs/cgroup\" \\\n  --privileged \\\n  -e RUN_EBPF_TESTS=true \\\n  --name benv \\\n  build-env bash\n```\n\n## Running ##\n\nSee the documentation for individual components:\n\n- [reducer](reducer.md)\n- [kernel-collector](kernel-collector.md)\n- [cloud-collector](cloud-collector.md)\n- [k8s-collector](k8s-collector.md)\n\n\n## Development VMs ##\n\nOpenTelemetry-eBPF needs to support multiple Linux kernels and flavors\n(distributions). This is particulary true for the kernel collector component,\nwhich is heavily dependent on the intricacies of particular Linux kernel\nversions. To support this, we use a number of devboxes, which are VMs of a\nparticular Linux distribution.\n\nCheck out the [devbox](../dev/devbox/README.md) documentation on how to build\nand use devboxes.\n\n## OpenTelemetry eBPF Linux kernel tests ##\n\nThere are some tests that are run as part of GitHub Actions for each PR and\nmerge to main, and that can be run manually, to validate OpenTelemetry eBPF\ncomponents against various Linux distributions and kernel versions.\nSee [test/kernel](../test/kernel/README.md)\n\n## Further Reading ##\n\n- [Render Framework](./render.md)\n- [Reducer Architecture Overview](./reducer/architecture.md)\n\n## Where to Get Help? ##\n\nYou can connect with us in our [slack channel](https://cloud-native.slack.com/archives/C02AB15583A).\n\nThe OpenTelemetry eBPF special interest group (SIG) meets regularly, and the meetting is held every \nweek on Tuesday at 09:00 Pacific time.\nSee the [eBPF Workgroup Meeting Notes](https://docs.google.com/document/d/13GK915hdDQ9sUYzUIWi4pOfJK68EE935ugutUgL3yOw) for a summary description of past meetings.\n"
  },
  {
    "path": "docs/dns-and-http.md",
    "content": "# DNS and HTTP collection\n\n## DNS collection\n\n### DNS Collection and Enrichment\n\nThis document describes how DNS collection and enrichment works.\n\n### Agent\n\n#### Kernel space\n\nCollector's eBPF code gathers DNS data by hooking into kernel UDP send functions \\(for reference, see where `perf_check_and_submit_dns` function is called from\\). Captured UDP send buffer data \\(`skb->data`\\) are relayed to the user space through perf ring messages. The maximum captured UDP data size is limited to 512 bytes \\(maximum DNS UDP packet message size specified in the RFC\\). We currently do not support DNS over HTTP.\n\n#### User space\n\nOnce data is received by the user space, it is parsed using third party code in the `collector/kernel/dns` directory. If the DNS request is detected, it is stored in the `DnsRequests` cache object \\(see `BufferedPoller::dns_requests_`\\). If data represents a DNS response, the corresponding request is located in the cache. The DNS response time is calculated using previously stored request time stamp, and the `dns_response` message is sent to the pipeline server ingest. This message includes host name and a set of IPv4 and IPv6 associated addresses. All requests are timed out and removed from the collector cache object after 10 seconds \\(see `BufferedPoller::process_dns_timeouts`\\).\n\n## HTTP collection\n\n### HTTP Detection\n\nThere are currently two implementations of HTTP code detection:\n\n* in kernel BPF HTTP detection\n* user land detection \\(userland-tcp\\)\n\nThe main difference is that for user land TCP the raw data is passed to the user space and then processed. For in-kernel based version the detection is performed in kernel space by BPF code. Userland code can be enabled by passing `--enable-userland-tcp` flag to the kernel collector.\n\nThe agent collects data by attaching to `tcp_sendmsg` and `tcp_recvmsg` system calls. Data is gathered directly from packets in the `skb` kernel structure. For user land TCP the data is passed to the user space for further processing. If supported http protocol is detected, the http status code is collected. For requests the timestamp is recorded to compute request latency on response.\n\nThe collected http response code is sent to the pipeline server. It is the actual status number. Pipeline server performs subsequent aggregation into `2xx`, `4xx`, `other`, and `5xx` groups.\n\n### Collector internal messages\n\nThe following message is used to communicate between kernel space and user space in the Kernel Collector:\n\n```text\n`http_response` {\n     sk - socket id\n     pid - process id (TGID) of the process\n     code - the actual http code\n     latency_ns - response latency\n     client_server - whether the side is client or a server\n}\n```\n\n### Server ingest message\n\nThe following message is used to send data to the pipeline server ingest:\n\n```text\n`http_response` {\n     sk - socket id\n     pid - process id (TGID) of the process\n     code - the actual http code\n     latency_ns - response latency\n     client_server - whether the side is client or a server\n}\n```\n\n"
  },
  {
    "path": "docs/k8s-collector.md",
    "content": "# Kubernetes Collector #\n\nThe _kubernetes collector_ gathers information from a Kubernetes API server on events like pod creation and deletion.\n\nUsual deployments will have one kubernetes collector per Kubernetes cluster.\n\n\n## Running ##\n\nThe kubernetes collector is composed of two binaries: _k8s-watcher_ and _k8s-relay_.\n\nK8s-watcher connects to the Kubernetes cluster and listens for notifications on certain events, e.g. pod creation and deletion.\nIt passes this information to k8s-relay, which is in turn connected to the reducer.\n\nTo connect to the Kubernetes API server, k8s-watcher needs to have the right permissions.\nFor local development, if you have access to the Kubernetes cluster through the _kubectl_ command,\nthen k8s-watcher should be able to access the same cluster that is configured in the kubectl's current context.\n\nUsually, kubernetes collector will be deployed through a Kubernetes deployment object,\nwhere k8s-watcher and k8s-relay are two containers running in one pod.\n\n\n## Environment variables ##\n\n- `EBPF_NET_INTAKE_HOST`: IP address or host name of the reducer to which telemetry is to be sent.\n- `EBPF_NET_INTAKE_PORT`: TCP port number on which the reducer is listening for collector connections. Usually 8000.\n- `EBPF_NET_DATA_DIR`: Directory in which the program will read and potentially write data files.\n  If not specified the current working directory will be used.\n- `EBPF_NET_LOG_FILE_PATH`: Location of the file in which logging messages are written. Default value is /var/log/ebpf_net.log.\n"
  },
  {
    "path": "docs/kernel-collector.md",
    "content": "# Kernel Collector #\n\nThe _kernel collector_ gathers low level telemetry straight from the Linux kernel using eBPF.\nIt does so with negligible compute and network overheads.\nThis telemetry is then sent to the reducer, which enriches and aggregates it.\n\nUsual deployments have a kernel collector running on each node of an observed cluster.\n\n\n## Running ##\n\nTo list all the available command-line options:\n\n```\n$ kernel-collector --help\n```\n\nThe kernel collector requires permissions to perform privileged operations, and so needs to be run with root privileges.\n\nThe `EBPF_NET_INTAKE_HOST` and `EBPF_NET_INTAKE_PORT` environment variables are required and must be set before running:\n\n```\n$ export EBPF_NET_INTAKE_HOST=192.168.0.101\n$ export EBPF_NET_INTAKE_PORT=8000\n$ sudo kernel-collector --log-console\n```\n\nThe `EBPF_NET_INTAKE_HOST` environment variable should point to the IP address or hostname of a running reducer instance.\n\nTo compile the eBPF code, the kernel collector requires kernel headers to be installed on the system.\n\n\n## Running with Docker ##\n\nRunning the kernel collector should be as easy as running a docker image:\n\n```\ndocker run -it --rm \\\n  --env EBPF_NET_INTAKE_PORT=\"${EBPF_NET_INTAKE_PORT}\" \\\n  --env EBPF_NET_INTAKE_HOST=\"${EBPF_NET_INTAKE_HOST}\" \\\n  --privileged \\\n  --pid host \\\n  --network host \\\n  --volume /var/run/docker.sock:/var/run/docker.sock \\\n  --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup \\\n  --volume /etc:/hostfs/etc \\\n  --volume /var/cache:/hostfs/cache \\\n  --volume /usr/src:/hostfs/usr/src \\\n  --volume /lib/modules:/hostfs/lib/modules \\\n  kernel-collector \\\n    --log-console --no-log-file\n```\n\nThe kernel collector needs privileged access since it uses the eBPF mechanism from the Linux kernel,\ntherefore these settings need to be passed to docker: `--privileged`, `--pid host` and `--network host`.\n\nWhen running as a docker container, the kernel collector will attempt to obtain the kernel headers package\nthrough the host's package manager. This requires /etc, /usr/src, /lib/modules and /var/cache to be mounted\ninside the container's /hostfs directory.\n\n\n## SELinux ##\n\nIf SELinux is enabled, the _spc_ SELinux policy (see spc_selinux man page) needs to be modified to allow\nadditional access to _spc_t_ domain processes (Super Privileged Containers).\nHere is an example SELinux module that achieves that:\n\n```\nmodule spc_bpf_allow 1.0;\nrequire {\n    type spc_t;\n    class bpf {map_create map_read map_write prog_load prog_run};\n}\n#============= spc_t ==============\nallow spc_t self:bpf { map_create map_read map_write prog_load prog_run };\n```\n\nTo apply, save it to a file (e.g. spc_bpf_allow.te) and execute the following commands:\n\n```\n$ checkmodule -M -m -o spc_bpf_allow.mod spc_bpf_allow.te\n$ semodule_package -o spc_bpf_allow.pp -m spc_bpf_allow.mod\n$ semodule -i spc_bpf_allow.pp\n```\n\n\n## Environment variables ##\n\n- `EBPF_NET_INTAKE_HOST`: IP address or host name of the reducer to which telemetry is to be sent.\n- `EBPF_NET_INTAKE_PORT`: TCP port number on which the reducer is listening for collector connections. Usually 8000.\n- `EBPF_NET_HOST_DIR`: Location where host directories will be mounted to. Default is /hostfs.\n- `EBPF_NET_DATA_DIR`: Directory in which the program will read and potentially write data files.\n  If not specified the current working directory will be used.\n- `EBPF_NET_LOG_FILE_PATH`: Location of the file in which logging messages are written. Default value is /var/log/ebpf_net.log.\n"
  },
  {
    "path": "docs/metrics/dimensions.yaml",
    "content": "availability_zone:\n  brief: Availability zone name in AWS (or similar) cloud environments.\n  description: If the associated metric comes from a monitored AWS (or similar) cloud instance running the cloud-collector, the metric may note the source and destination availability zones that the traffic being measured moved between.  Availability zone naming is documented and provided by AWS, or the cloud service in use.\n  example: us-east-1d\n\nsource.availability_zone:\n  brief: See availability_zone\n  description: See availability_zone\n\ndest.availability_zone:\n  brief: See availability_zone\n  description: See availability_zone\n\naz_equal:\n  brief: Boolean set true if the source and destination zones are the same.\n  description: If the associated metric has both source and destination availability_zone dimensions, the metric will also have this dimension available.  If the values of the source and destination are equal, the value of this dimension will be 'true', otherwise, 'false'.  This may be used to quickly group or filter for cross-zone metrics.\n  example: true, false\n\nworkload.name:\n  brief: Kubernetes workload or application name.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the source and destination workload that the traffic being measured moved between.  The workload name is optionally set by the administrator configuring Kubernetes.  Sometimes referred to as the 'service name.'\n  example: loadgenerator, (unknown), ip-172-12-19-222.ec2.internal\n\nsource.workload.name:\n  brief: See workload.name\n  description: See workload.name\n\ndest.workload.name:\n  brief: See workload.name\n  description: See workload.name\n\nworkload.uid:\n  brief: Kubernetes workload or application UUID.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the UUID of the source and destination workload that the traffic being measured moved between. This UUID represents the unique identifier of the associated Deployment or DaemonSet. \n  example: 0485c36b-283e-496e-af47-1a69545cd887\n\nsource.workload.uid:\n  brief: See workload.uid\n  description: See workload.uid\n\ndest.workload.uid:\n  brief: See workload.uid\n  description: See workload.uid\n\nimage_version:\n  brief: Kubernetes pod version label of the workload.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the source and destination versions that the traffic being measured moved between.  The version label is optionally set by the administrator configuring Kubernetes.  Sometimes referred to as the 'service version.'  May be found in the docker (pod) image or Kubernetes metadata.\n  example: latest, -, v0.3.6\n\nsource.image_version:\n  brief: See image_version\n  description: See image_version\n\ndest.image_version:\n  brief: See image_version\n  description: See image_version\n\nenvironment:\n  brief: Kubernetes cluster name the workload resides in.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the source and destination clusters that the traffic being measured moved between.  The cluster label is optionally set by the administrator configuring Kubernetes.  This will often be the same as the k8s.cluster.name dimension if it is available.\n  example: kubernetes, staging, production, (no agent)\n\nsource.environment:\n  brief: See environment\n  description: See environment\n\ndest.environment:\n  brief: See environment\n  description: See environment\n\nnamespace.name:\n  brief: Kubernetes namespace name the workload resides in.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the source and destination namespaces that the traffic being measured moved between.  The namespace name is optionally set by the administrator configuring Kubernetes.\n  example: staging, -, kube-system, cert-manager\n\nsource.namespace.name:\n  brief: See namespace.name\n  description: See namespace.name\n\ndest.namespace.name:\n  brief: See namespace.name\n  description: See namespace.name\n\nresolution_type:\n  brief: Level of detail available for the metric.\n  description: The source and the destination will be tagged with the level of detail that the system was able to extract and associate with the measurement.  For instance, a resolution of 'IP' for a destination means that we know that some information was 'sent' to a specific IP, but likely have no other information about that system, potentially because it is outside of our monitored cloud environment.  A 'CONTAINER' means we have metadata from the pod or workload, whereas 'K8S_CONTAINER' means we also have additional Kubernetes metadata associated with the workload.  Certain resolutions are special, such as 'DNS', which indicates communication from or to a DNS service.\n  example: AWS, CONTAINER, DNS, INSTANCE_METADATA, IP, K8S_CONTAINER, LOCALHOST, NOMAD, PROCESS\n\nsource.resolution_type:\n  brief: See resolution_type\n  description: See resolution_type\n\ndest.resolution_type:\n  brief: See resolution_type\n  description: See resolution_type\n\nprocess.name:\n  brief: A specific UNIX process name.\n  description: A specific process running on a monitored machine, or within a monitored pod, which is generating or receiving traffic.  This would be similar to the string a user might see locally if using the 'ps' command.\n  example: bind, -, etcd, checkoutservice\n\nsource.process.name:\n  brief: See process.name\n  description: See process.name\n\ndest.process.name:\n  brief: See process.name\n  description: See process.name\n\ncontainer.name:\n  brief: Docker container name.\n  description: If the associated metric comes from a monitored Kubernetes cluster or Dockerized service, the Docker image names associated with the source and destination may be recorded.  These names are set by the docker image creators.\n  example: kubedns, -, kube-proxy, nginx-ingress\n\nsource.container.name:\n  brief: See container.name\n  description: See container.name\n\ndest.container.name:\n  brief: See container.name\n  description: See container.name\n\nstatus_code:\n  brief: HTTP response code.\n  description: For HTTP metrics, the captured response code being reported on in aggregate.  Codes are bucketed, so any 2XX code is counted as a '200'.  Buckets are 200, 400, 500 and any other code outside of these ranges.\n  example: 200, 400, 500, other\n"
  },
  {
    "path": "docs/metrics/internal_metrics/dimensions.yaml",
    "content": "az:\n  brief: availability zone\n  description: availability zone\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns, ebpf_net.codetiming_avg_ns, ebpf_net.collector_health\n  example: us-east-1a\n\nc_host:\n  brief: Client host machine name.\n  description: Collector host machine name. This is a span or state that is reported by collector to reducer.\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns, ebpf_net.collector_health\n  example: ip-192-168-110-244.ec2.internal\n\nc_type:\n  brief: Client type\n  description: Client types are numbers designating different client types. Different types are kernel(1), cloud(2), k8s(3), ingest(4), matching(5), aggregation(6), liveness_probe (7), readiness_probe(8).\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns, ebpf_net.collector_health\n  example: 1\n\ncloud:\n  brief: Cloud type\n  description: Cloud provider type where network explorer is installed. Different types are unknown(1), aws(1), gcp(2).\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.collector_log_count, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.collector_health\n  example: 1\n\ndetail:\n  brief: Detail delineates collector health status\n  description: Detail delineates collector health status. It can have these values healthy(0), unknown(1), aws_describe_region_error(2), aws_describe_network_interfaces_error(3)\n  associated_metrics: ebpf_net.collector_health\n  example: 0\n\nenv:\n  brief: environment\n  description: environment where network explorer was installed.\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.bpf_log, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.collector_health\n  example: network-explorer-staging.\n\nerror:\n  brief: error type\n  description: error type\n  associated_metrics: ebpf_net.pipeline_message_error, ebpf_net.entrypoint_info\n  example: fetched/unknown\n\nfield:\n  brief: Field name\n  description: Field name.\n  associated_metrics: ebpf_net.bpf_log\n  example: pod_name, id, ip, az etc\n\nfilename:\n  brief: Filename of the source code file.\n  description: File name of the source code file.\n  associated_metrics: ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns, ebpf_net.codetiming_count\n  example: agg_core.cc\n\nindex:\n  brief: Index key\n  description:  Index key make sure CodeTimings in templated functions have a unique key for the CodeTimingRegistry.\n  associated_metrics: ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns, ebpf_net.codetiming_count\n  example: 5\n\nid:\n  brief: Id\n  description: Id is the node identifier.\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns, ebpf_net.collector_health\n  example: network-explorer-splunk-otel-network-explorer-k8s-collectos4wnt\n\nkernel:\n  brief: Linux kernel version\n  description: Linux kernel version\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.bpf_log, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns, ebpf_net.collector_health\n  example: 5.4.219-126.411.amzn2.x86_64\n\nkernel_header_source:\n  brief: Linux Kernel header source\n  description: Linux headers is a package. These are part of the kernel, although they are shipped separately. The headers act as an interface between internal kernel components and also between userspace and the kernel. Packages like sys-libs/glibc depend on the kernel headers.\n  associated_metrics: ebpf_net.entrypoint_info\n  example: 5.4.219-126.411.amzn2.x86_64\n\nk8s.cluster.name:\n  brief: Kubernetes Cluster name.\n  description: Kubernetes Cluster name.\n  associated_metrics: ebpf_net.up, ebpf_net.time_since_last_message_ns, ebpf_net.span_utilization, ebpf_net.span_utilization_fraction, ebpf_net.span_utilization_max, ebpf_net.rpc_queue_elem_utilization_fraction, ebpf_net.rpc_queue_buf_utilization, ebpf_net.rpc_queue_buf_utilization_fraction, ebpf_net.rpc_latency_ns, ebpf_net.pipeline_metric_bytes_discarded, ebpf_net.pipeline_metric_bytes_written, ebpf_net.pipeline_message_error, ebpf_net.otlp_grpc.unknown_response_tags, ebpf_net.otlp_grpc.requests_sent, ebpf_net.otlp_grpc.bytes_sent, ebpf_net.otlp_grpc.bytes_failed, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.connections, ebpf_net.disconnects, ebpf_net.collector_log_count, ebpf_net.collector_health, ebpf_net.bpf_log, ebpf_net.agg_root_truncation, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns, ebpf_net.codetiming_count\n  example: staging.\n\nline:\n  brief: Line number of the code block instrumented.\n  description: Line number of the code block instrumented.\n  associated_metrics: ebpf_net.bpf_log, ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns, ebpf_net.codetiming_count\n  example: 170\n\nmessage:\n  brief: Message type\n  description: Message type extracted by collector at the linux kernel level.\n  associated_metrics: ebpf_net.message, ebpf_net.pipeline_message_error\n  example: task_info, set_cgroup, pid_close_info\n\nmodule:\n  brief: Name of the Reducer core\n  description: Name of the Reducer core.\n  associated_metrics: ebpf_net.up, ebpf_net.span_utilization, ebpf_net.span_utilization_fraction, ebpf_net.span_utilization_max, ebpf_net.rpc_queue_elem_utilization_fraction, ebpf_net.rpc_queue_buf_utilization, ebpf_net.rpc_queue_buf_utilization_fraction, ebpf_net.rpc_latency_ns, ebpf_net.pipeline_metric_bytes_discarded, ebpf_net.pipeline_metric_bytes_written, ebpf_net.pipeline_message_error, ebpf_net.otlp_grpc.unknown_response_tags, ebpf_net.otlp_grpc.requests_sent, ebpf_net.otlp_grpc.bytes_sent, ebpf_net.otlp_grpc.bytes_failed, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.connections, ebpf_net.disconnects, ebpf_net.collector_log_count, ebpf_net.bpf_log, ebpf_net.agg_root_truncation, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns\n  example: ingest\n\nname:\n  brief: Name of the instrumented codeblock.\n  description: Name of the instrumented codeblock.\n  associated_metrics: ebpf_net.bpf_log, ebpf_net.codetiming_min_ns, ebpf_net.codetiming_max_ns, ebpf_net.codetiming_sum_ns, ebpf_net.codetiming_avg_ns, ebpf_net.codetiming_count\n  example: OtlpGrpcFormatterFormatLabelsChanged\n\nnamespace.name:\n  brief: Kubernetes namespace name the workload resides in.\n  description: If the associated metric comes from a monitored Kubernetes cluster running the k8-collector, the metric may note the source and destination namespaces that the traffic being measured moved between.  The namespace name is optionally set by the administrator configuring Kubernetes.\n  example: staging, -, kube-system, cert-manager\n\nos:\n  brief: Operating Systems\n  description: Name of the  Operating System\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.collector_health, ebpf_net.bpf_log, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction, ebpf_net.clock_offset_ns\n  example: Linux\n\nos_version:\n  brief: Operating Systems Version\n  description: Version of the  Operating System\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.collector_health, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction\n  example: 5.2.14, unknown\n\npeer:\n  brief: peer module\n  description: See module\n  associated_metrics: ebpf_net.rpc_queue_elem_utilization_fraction, ebpf_net.rpc_latency_ns, ebpf_net.rpc_queue_buf_utilization, ebpf_net.rpc_queue_buf_utilization_fraction\n  example: ingest\n\nprogram:\n  brief: Program\n  description: Program\n  associated_metrics: ebpf_net.up\n  example: reducer\n\nrole:\n  brief: role\n  description: Name of the cloud Identity access managment (IAM) role.\n  associated_metrics: ebpf_net.time_since_last_message_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.collector_log_count, ebpf_net.collector_health, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction\n  example: network-explorer-staging-node-group-more\n\nseverity:\n  brief: Log severity level\n  description: The severity level of an event is in the network-explorer logs and is used by indicate how severe or important an event is. The levels are critical, error, warning, debug, info, ignored.\n  associated_metrics: ebpf_net.collector_log_count, ebpf_net.message\n  example: critical\n\nshard:\n  brief: Shard number\n  description: Shard number; Reducer cores for e.g. Ingest and others can be configured to run with multiple shards. Please note shard number starts from 0.\n  associated_metrics: ebpf_net.up, ebpf_net.span_utilization, ebpf_net.span_utilization_fraction, ebpf_net.span_utilization_max, ebpf_net.rpc_queue_buf_utilization, ebpf_net.rpc_queue_buf_utilization_fraction, ebpf_net.rpc_queue_elem_utilization_fraction, ebpf_net.pipeline_metric_bytes_discarded, ebpf_net.pipeline_metric_bytes_written, ebpf_net.pipeline_message_error, ebpf_net.otlp_grpc.unknown_response_tags, ebpf_net.otlp_grpc.requests_sent, ebpf_net.otlp_grpc.bytes_sent, ebpf_net.otlp_grpc.bytes_failed, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.bpf_log, ebpf_net.agg_root_truncation, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction\n  example: 0\n\nspan:\n  brief: Span name\n  description: Name of the Span. Ingest shards do not just ingest rather their main task is to keep track of all the entities that collector is reporting on. Entities such as TCP and UDP sockets, processes, cgroups etc. These  entities are spans.\n  associated_metrics: ebpf_net.span_utilization, ebpf_net.span_utilization_fraction, ebpf_net.span_utilization_max, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction\n  example: tracked_process, process, cgroup\n\nversion:\n  brief: Network Explorer release version\n  description: Network Explorer release version. This allows to pinpoint which of the code is running in the installation.\n  associated_metrics: ebpf_net.up, ebpf_net.time_since_last_message_ns, ebpf_net.rpc_latency_ns, ebpf_net.pipeline_message_error, ebpf_net.message, ebpf_net.entrypoint_info, ebpf_net.collector_log_count, ebpf_net.bpf_log, ebpf_net.client_handle_pool, ebpf_net.client_handle_pool_fraction\n  example: 0.9.4217\n"
  },
  {
    "path": "docs/metrics/internal_metrics/metrics.yaml",
    "content": "ebpf_net.agg_root_truncation:\n  brief:  Agg root truncation.\n  description: |\n    Total number of Aggregation root truncations in Aggregation core of the reducer component.\n  title:  ebpf_net.agg_root_truncation\n\nebpf_net.bpf_log:\n  brief: eBPF log count.\n  description: |\n     Total eBPF log count in the prior 30 seconds. bpf_logs are error messages from kernel-collector eBPF code that indicate unexpected or unhandled behavior. This is enabled by default.\n  metric_type: counter\n  title: ebpf_net.bpf_log\n\nebpf_net.client_handle_pool:\n  brief: Client handle pool in the prior 30 seconds.\n  description: |\n    Client handle pool for the prior thirty seconds.\n  metric_type: counter\n  title: ebpf_net.client_handle_pool\n\nebpf_net.client_handle_pool_fraction:\n  brief:  Client handle pool fraction.\n  description: |\n    Client handle pool fraction.\n  metric_type: counter\n  title:  ebpf_net.client_handle_pool_fraction\n\nebpf_net.clock_offset_ns:\n  brief:  Clock offset in ns.\n  description: |\n    Clock offset in nanoseconds.\n  metric_type: counter\n  title:  ebpf_net.clock_offset_ns\n\nebpf_net.codetiming_avg_ns:\n  brief: The average code timing time in ns.\n  description: |\n    The average time for a particular instrumented code block in nanoseconds.\n  metric_type: counter\n  title: ebpf_net.codetiming_avg_ns\n\nebpf_net.codetiming_count:\n  brief: The number of codetiming blocks.\n  description: |\n     The number of codetiming blocks.\n  metric_type: counter\n  title: ebpf_net.codetiming_count\n\nebpf_net.codetiming_max_ns:\n  brief: Maximum code timing in nanoseconds.\n  description: |\n    Maximum code timing in nanoseconds.\n  metric_type: counter\n  title: ebpf_net.codetiming_max_ns\n\nebpf_net.codetiming_min_ns:\n  brief: The minimum code timing in the prior 30 seconds.\n  description: |\n    The minimum code timing for an instrumented block of code for the prior 30 seconds.\n  metric_type: counter\n  title: ebpf_net.codetiming_min_ns\n\nebpf_net.codetiming_sum_ns:\n  brief: Code timing sum in nanoseconds.\n  description: |\n    Code timing sum in nanoseconds.\n  metric_type: counter\n  title: ebpf_net.codetiming_sum_ns\n\nebpf_net.collector_health:\n  brief: Collector health status\n  description: |\n    Collector health status. This metrics shows if Network Explorer collectors are up and running to properly extract information  at the kernel level. The healthy value is 0. This is enabled by default.\n  metric_type: counter\n  title: ebpf_net.collector_health\n\nebpf_net.collector_log_count:\n  brief: eBPF collector log count\n  description: |\n     eBPF collector log count\n  metric_type: counter\n  title: ebpf_net.collector_log_count\n\nebpf_net.connections:\n  brief: Total number of connections in the prior 30 seconds.\n  description: |\n    The total number of connections for the prior 30 seconds.\n  metric_type: counter\n  title: ebpf_net.connections\n\nebpf_net.disconnects:\n  brief: The total number of disconnects made during the last 30 seconds.\n  description: |\n    The total number of disconnects made during the last 30 seconds.\n  metric_type: counter\n  title: ebpf_net.disconnects\n\nebpf_net.entrypoint_info:\n  brief: Entry point information.\n  description: |\n    Entry point information.\n  metric_type: counter\n  title:  ebpf_net.entrypoint_info\n\nebpf_net.message:\n  brief: Message count.\n  description: |\n     Message count for the prior 30 seconds. These are events extracted by eBPF hook at the linux kernel level.\n  metric_type: counter\n  title: ebpf_net.message\n\nebpf_net.otlp_grpc.bytes_failed:\n  brief: Total number of OTLP bytes failed.\n  description: |\n    Total number of OTLP bytes failed. This is enabled by default.\n  metric_type: counter\n  title: ebpf_net.otlp_grpc.bytes_failed\n\nebpf_net.otlp_grpc.bytes_sent:\n  brief: Total number of OTLP bytes sent.\n  description: |\n    Total number of OTLP bytes sent.  This is enabled by default.\n  metric_type: counter\n  title: ebpf_net.otlp_grpc.bytes_sent\n\nebpf_net.otlp_grpc.failed_requests:\n  brief: Total number of failed OTLP requests.\n  description: |\n    Total number of failed OTLP requests.\n  metric_type: counter\n  title: ebpf_net.otlp_grpc.failed_requests\n\nebpf_net.otlp_grpc.metrics_failed:\n  brief:  Total number of OTLP grpc metrics failed.\n  description: |\n    Total number of unknown OTLP grpc metrics failed. This is enabled by default.\n  metric_type: counter\n  title:  ebpf_net.otlp_grpc.metrics_failed\n\nebpf_net.otlp_grpc.metrics_sent:\n  brief:  Total number of OTLP grpc metrics sent.\n  description: |\n    Total number of OTLP grpc metrics sent. This is enabled by default.\n  metric_type: counter\n  title:  ebpf_net.otlp_grpc.metrics_sent\n\nebpf_net.otlp_grpc.requests_sent:\n  brief: Total number otlp requests sent.\n  description: |\n    The total number of OTLP (Open Telemetry Protocol) requests sent. This is enabled by default.\n  title: ebpf_net.otlp_grpc.requests_sent\n\nebpf_net.otlp_grpc.unknown_response_tags:\n  brief:  Total number of unknown OTLP grpc response tags.\n  description: |\n    Total number of unknown OTLP grpc response tags. A non-zero value would indicate a logic error in the code or a configuration error. This is enabled by default.\n  metric_type: counter\n  title:  ebpf_net.otlp_grpc.unknown_response_tags\n\nebpf_net.pipeline_message_error:\n  brief:  Pipeline message error.\n  description: |\n    Total number of pipeline message errors. These are the errors seen while trying to send messages downstreams within Reducer cores.\n  metric_type: counter\n  title:  ebpf_net.pipeline_message_error\n\nebpf_net.pipeline_metric_bytes_discarded:\n  brief: The total number of discarded metrics bytes in the pipeline.\n  description: |\n    The number of bytes discarded by the pipeline for the prior 30 seconds.\n  metric_type: counter\n  title: ebpf_net.pipeline_metric_bytes_discarded\n\nebpf_net.pipeline_metric_bytes_written:\n  brief:  Total number of pipeline metric bytes written.\n  description: |\n    Total number of pipeline metric bytes written.\n  metric_type: counter\n  title:  ebpf_net.pipeline_metric_bytes_written\n\nebpf_net.rpc_latency_ns:\n  brief:  RPC latency in ns.\n  description: |\n    Remote Procedure Calls (RPC) latencies in ns\n  metric_type: counter\n  title:  ebpf_net.rpc_latency_ns\n\nebpf_net.rpc_queue_buf_utilization:\n  brief: RCP queue buffer utilization fraction.\n  description: |\n    Total utilization of RPC buffer.\n  metric_type: counter\n  title: ebpf_net.rpc_queue_buf_utilization\n\nebpf_net.rpc_queue_buf_utilization_fraction:\n  brief: RPC queue buffer utilization fraction for the last 30 seconds.\n  description: |\n    RPC queue buffer utilization fraction for the last 30 seconds.\n  metric_type: counter\n  title: ebpf_net.rpc_queue_buf_utilization_fraction\n\nebpf_net.rpc_queue_elem_utilization_fraction:\n  brief: RPC queue utilization fraction in the prior 30 seconds.\n  description: |\n    RPC queue utiliation fraction for the prior thirty seconds.\n  metric_type: counter\n  title: ebpf_net.rpc_queue_elem_utilization_fraction\n\nebpf_net.span_utilization:\n  brief: The span utilization in the last 30 seconds.\n  description: |\n    The span utilization in the last 30 seconds.\n  metric_type: counter\n  title: ebpf_net.span_utilization\n\nebpf_net.span_utilization_fraction:\n  brief: Span Utilization fraction in the prior 30 seconds.\n  description: |\n     The span utilization fraction in the  prior thirty seconds.\n  metric_type: counter\n  title:  ebpf_net.span_utilization_fraction\n\nebpf_net.span_utilization_max:\n  brief:  Maximum span utlization\n  description: |\n    Maximum span utlization.\n  metric_type: counter\n  title:  ebpf_net.span_utilization_max\n\nebpf_net.time_since_last_message_ns:\n  brief: Time since last message in ns.\n  description: |\n    Time since last message in ns.\n  metric_type: counter\n  title: ebpf_net.time_since_last_message_ns\n\nebpf_net.up:\n  brief: Total number of apps in the reducer running in the prior 30 seconds.\n  description: |\n    Total number of apps in the reducer running in the prior 30 seconds. This is enabled by default.\n  metric_type: counter\n  title: ebpf_net.up\n\n"
  },
  {
    "path": "docs/metrics/metrics.yaml",
    "content": "## Notes:\n##  * In most cases, any metric measurement should have all dimensions available from the dimensions document available in both source and destination form.\n##  * Often, dimensions that are not available will have a value of '(unknown)', '-' or similar.\n##  * All metrics from Network Explorer are currently provided with 30 second resolution.\n\ntcp.bytes:\n  brief: Total TCP bytes in the prior 30 seconds.\n  description: |\n    The total number of TCP bytes between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.bytes\n\ntcp.active:\n  brief: The number of active TCP connections.\n  description: |\n    The number of TCP connections considered to be open and alive between the source and destination at the point the measurement was taken.\n  metric_type: gauge\n  title: tcp.active\n\ntcp.packets:\n  brief: Total TCP packets in the prior 30 seconds.\n  description: |\n    The total number of TCP packets between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.packets\n\ntcp.retrans:\n  brief: Total TCP retransmissions in the prior 30 seconds.\n  description: |\n    The total number of TCP retransmission requests between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.retrans\n\ntcp.syn.timeouts:\n  brief: Total TCP SYN timeouts in the prior 30 seconds.\n  description: |\n    The total number of TCP SYN timeouts between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.syn.timeouts\n\ntcp.new_sockets:\n  brief: Total new TCP sockets in the prior 30 seconds.\n  description: |\n    The total number of new TCP sockets opened between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.new_sockets\n\ntcp.resets:\n  brief: Total TCP resets in the prior 30 seconds.\n  description: |\n    The total number of TCP resets sent between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: tcp.resets\n\ntcp.rtt.num_measurements:\n  brief: The number of RTT measurements made.\n  description: |\n    The number of measurements made in calculating the current RTT average value.\n  metric_type: gauge\n  title: tcp.rtt.num_measurements\n\ntcp.rtt.average:\n  brief: The average round trip time in ms.\n  description: |\n    The computed average round trip time between the source and destination as measured in microseconds.\n  metric_type: gauge\n  title: tcp.rtt.average\n\nudp.bytes:\n  brief: Total UDP bytes in the prior 30 seconds.\n  description: |\n    The total number of UDP bytes between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: udp.bytes\n\nudp.packets:\n  brief: Total UDP packets in the prior 30 seconds.\n  description: |\n    The total number of UDP packets between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: udp.packets\n\nudp.active:\n  brief: The number of active UDP connections.\n  description: |\n    The number of UDP connections considered to be open and alive between the source and destination at the point the measurement was taken.\n  metric_type: gauge\n  title: udp.active\n\nudp.drops:\n  brief: Total UDP dropped connections in the prior 30 seconds.\n  description: |\n    The total number of UDP connections dropped between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: udp.drops\n\nhttp.status_code:\n  brief: Count of HTTP server response code in the prior 30 seconds.\n  description: |\n    For a given class of response code (see 'response_code' dimension), the number of times an unencrypted server sent an HTTPv1 status code between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: http.status_code\n\nhttp.active_sockets:\n  brief: The number of HTTP sockets in the prior interval for which metrics have been reported.\n  description: |\n    The number of unencrypted HTTPv1 connections for which measurements were taken in the prior thirty seconds.\n  metric_type: count\n  title: http.active_sockets\n\nhttp.client.duration_average:\n  brief: Average client HTTP response time in ms.\n  description: |\n    This metric is the average duration in microseconds from when the client sends an HTTP request, until the response is received back from the server.  As such, it includes the communication round-trip times, plus the server processing latency.  Computed by summation of all times, divided by http.active_sockets.\n  metric_type: counter\n  title: http.client.duration_average\n\nhttp.server.duration_average:\n  brief: Average server HTTP response time in ms.\n  description: |\n     This metric is the average duration in microseconds for the server to respond to a request received locally.  Thus, it does not include the network latency from or to the client.  Computed by summation of all times, divided by http.active_sockets.\n  metric_type: counter\n  title: http.server.duration_average\n\ndns.active_sockets:\n  brief: The number of DNS sockets in the prior interval for which metrics have been reported.\n  description: |\n    The number of DNS connections for which measurements were taken in the prior thirty seconds.\n  metric_type: gauge\n  title: dns.active_sockets\n\ndns.responses:\n  brief: DNS responses sent in the prior 30 seconds.\n  description: |\n    The total number of DNS responses sent between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: dns.responses\n\ndns.timeouts:\n  brief: Total DNS timeouts in the prior 30 seconds.\n  description: |\n    The total number of\tDNS timeouts between the source and destination measured for the prior thirty seconds.\n  metric_type: counter\n  title: dns.timeouts\n\ndns.client.duration_average:\n  brief:  Average client DNS response time in ms.\n  description: |\n    This metric is the average duration in microseconds from when the client sends a DNS request, until the response is received back from the server.  As such, it includes the communication round-trip times, plus the server processing latency.  Computed by the summation of all times, divided by dns.responses.\n  metric_type: counter\n  title: dns.client.duration_average\n\ndns.server.duration_average:\n  brief: Average server DNS response time in ms.\n  description: |\n    This metric is the average duration in microseconds for the server to respond to a request received locally.  Thus, it does not include the network latency from or to the client.  Computed by the summation of all times, divided by dns.responses.\n  metric_type: counter\n  title: dns.server.duration_average\n"
  },
  {
    "path": "docs/processes-and-cgroups.md",
    "content": "---\ndescription: >-\n  How process and cgroup information is sent from eBPF to the kernel collector\n  user-space\n---\n\n# Processes and cgroups\n\n## Processes\n\n### Messages\n\n* pid\\_info\n* pid\\_close\n* pid\\_set\\_comm\n\nThe _pid\\_info_ message is sent whenever a new process is detected. That can be during startup, when processes are enumerated by visiting the _proc_ filesystem, or during the steady state when a new process is first scheduled to run.\n\nThe _pid\\_close_ message is sent when a process exits.\n\nThe _pid\\_set\\_comm_ message is sent when the command-line is set for the process, e.g. during the _exec_ system call.\n\nAll messages identify the process through the `u32 pid` field \\(process ID\\).\n\n## Cgroups\n\nCgroups are a kernel feature that is, among other uses, utilized for implementing containerization \\(e.g. Docker\\). Cgroups are also used by systemd.\n\n### Messages\n\n* css\\_populate\\_dir\n* cgroup\\_clone\\_children\\_read\n* cgroup\\_attach\\_task\n* kill\\_css\n\nThe _css\\_populate\\_dir_ message is sent when a new cgroup directory is created in the cgroup filesystem hierarchy \\(usually mounted on /sys/fs/cgroup\\).\n\nThe _cgroup\\_clone\\_children\\_read_ message is sent during startup, when existing cgroups are enumerated.\n\nThe _cgroup\\_attach\\_task_ message is sent when a process enters a cgroup.\n\nThe _kill\\_css_ message is sent when a cgroup is destroyed.\n\nAll messages identify the cgroup through the `u64 cgroup` field. A cgroup's parent is identified through the `u64 cgroup_parent` field \\(cgroups are hierarchical\\).\n\n"
  },
  {
    "path": "docs/reducer/architecture.md",
    "content": "# Reducer Architecture Overview #\n\n## Logs to Metrics ##\n\nThe job of the Reducer is to reduce logs coming in from multiple collectors into\none stream of metrics.\n\n![Logs to Metrics](./images/01_logs_to_metrics.png)\n\n\n## Shards and Pipes ##\n\nThe Reducer is split into three stages of data processing – ingest, matching and\naggregation -- with each stage running one or more shards. Shards are connected\none-way, and communicate using message passing, using the same message encoding\nas is used between collectors and the reducer.\n\nEach shard is running one thread of execution, and there is no data-sharing\nbetween shards (in theory -- in practice this is not strictly true, as ingest\nshards share one load balancer and one private-to-public IP address map).\nThis architecture provides scalability without the usual headaches that come\nwith multithreading.\n\n![Shards and Pipes](./images/02_shards_and_pipes.png)\n\n\n## Example ##\n\nIn this example one collector each is observing on part of a HTTP connection.\nLog messages are sent from collectors and are received into ingest shards,\nsame one or different ones – it does not matter. Both the metadata (enrichment\ninformation) and metrics (numbers) from both sides are joined at one place in\none of the matching shards. From there it is sent to one of the aggregation\nshards where it is aggregated and written out in the form of metric samples.\n\n![Example](./images/03_example.png)\n\n\n## State Tracking ##\n\nIngest shards do not just ingest; their main task is to keep track of all the\nentities that collector is reporting on. Entities such as TCP and UDP sockets,\nprocesses, cgroups. We can call this activity _state mirroring_ or\n_state tracking_, and we call these entities _spans_. As log messages are\ningested, spans are created and deleted as those resources on the system being\nobserved are created and destroyed. Some log messages signal a change in state\nof a particular resource (e.g. \"a socket is bound to an IP address\").\n\n![State Tracking](./images/04_state_tracking.png)\n\n\n## Example (continued) ##\n\nThe most interesting resources that we track are TCP and UDP sockets. Sockets\ncontain the lowest level of metadata that we use, and also provide metrics that\nare ultimately the information around which everything else revolves\n(bytes sent/received, TCP resets, drops, etc.) By the time we get a log message\non socket's metrics, we will also know the additional relationships between this\nsocket and other spans – process, cgroup – and from those we can extract\nadditional enrichment metadata.\n\nA _flow_ is something like a network connection – but we associate flows even\nwith connectionless protocols. A flow will contain information about both sides\nof the connection.\n\nEnrichment metadata and metrics from both sockets are joined in one flow span.\nFor this to happen, both sockets need to come up with the same identifier that\nidentifies a flow. We call this the _flow key_.\n\n![Example continued](./images/05_example_cntd.png)\n\n\n## Flow Key ##\n\n```\nflow_key :=\n\tif ((local_host, local_port) < (remote_host, remote_port))\n\tthen (local_host, local_port, remote_host, remote_port)\n\telse (remote_host, remote_port, local_host, local_port)\n\nlocal_host, remote_host : IPv6Address\n\nlocal_port, remote_port : u16\n```\n\nWhat both sockets have in common is the local/remote host/port four-tuple. This\nis how the flow key is derived. There might be address translations between what\none side of the connection sees and what the other side sees. It is important\nthat we track this and that we come up with the same set of addresses and ports\non both ends. Otherwise, instead of one fully-enriched flow we end up with two\npartially-enriched flows.\n\n![Flow Key](./images/06_flow_key.png)\n\n\n## Enrichment ##\n\nInside the flow span, enrichment information from both sides is then used to\nderive the _node keys_ for both sides of the flow. What is meant by this is that\nthe (id, az, role, type, env, ...) n-tuple uniquely identifies a _node_, i.e. it\nconstructs a _node key_. There will be two node keys for two nodes, and this is\nwhere the aggregation tree starts.\n\nIn this example two additional span types are used in deriving the node keys --\none for AWS and one for Kubernetes. These spans came to existence here through\nthe _broadcast_ mechanism (explained later).\n\n![Enrichment](./images/07_enrichment.png)\n\n\n## Aggregation ##\n\nKeys for _az_ and _role_ spans are subsets of the key for node spans. Each\nelement of an aggregation tree derives the key for its sub-elements from its own\nkey. Individual aggregation trees can share elements (spans). Each element\n(span) contains a metric store (or two) for each supported protocol. We output\nmetrics by visiting every element of all aggregation trees, and writing out\nmetrics values from the metric store of each span. We use _(role,role)_ keys as\nsharding keys for sharding across aggregation shards. If two aggregation trees\nhave the same _(role,role)_ values, then _role_role_ elements will be shared\nbetween them and possible other elements as well. If they don't have the same\n_(role,role)_ values, then those two aggregation trees will not touch, and they\ncan be contained in separate aggregation shards.\n\n![Aggregation](./images/08_aggregation.png)\n\n\n## Broadcast ##\n\nEarlier we saw two additional span types that were used in deriving full node\nkeys – _aws_enrichment_ and _k8s_pod_. These two are instantiated by Cloud and\nKubernetes collectors, respectively.\n\nBut that happens in the ingest stage – how do they materialize in the matching\nstage? Answer: proxy spans (covered later). When a message from the\nk8s-collector instantiates a k8s_pod span in the ingest stage, it is a proxy for\nk8s_pod in the matching stage. Targets of proxies can be either sharded across\ntarget shards, or can be broadcast to all target shards, as is the case here.\n\nWe use this kind of broadcasting to relay information that needs to be made\navailable to all downstream shards. Since we don't share information by\nreference, we need to share it by copy.\n\n![Broadcasting](./images/09_broadcast.png)\n\n\n## Connections and Index ##\n\nA _connection_ object is instantiated for every client connection.\n\nA client will normally be a collector, but a connection is instantiated when\nanything connects to the reducer.\n\nA connection object will keep a mapping from a client-specified reference to a\nhandle that points to a span instance inside the corresponding span pool\n(see the [Referenced Instances](../render.md#referenced-instances) section of\nthe _Render Framework_ documentation).\n\nFor singleton span types, it keeps a single handle to a span in the pool.\nSo \"singleton\" means it is a singleton per connection, it does not mean a\nsingleton per shard or per application.\n\nThe index object will contain a container for each span type, not only for those\nspans which are accessed externally – from clients, either as singleton or via\nthe _ref_ message field. Those containers will be hashmaps for indexed span\ntypes or pools for non-indexed span types. This is unrelated to the _ref_ field\nin a message\n\n![](./images/10_connections_and_index.png)\n"
  },
  {
    "path": "docs/reducer.md",
    "content": "# Reducer #\n\nThe role of the reducer is to combine information received from eBPF collectors into metrics.\nMetrics are aggregated across time and across dimensions.\nThe output is in the form of timestamped data points, which can be stored in a time-series database.\n\n\n## Running ##\n\nTo list all the available command-line options:\n\n```\n$ reducer --help\n```\n\nBy default, reducer will write its logging output to /var/log/ebpf_net.log.\nIf that file is not writable, reducer will fail with an error message.\n\nThe `EBPF_NET_LOG_FILE_PATH` environment variable can be used to control the log file location:\n\n```\n$ EBPF_NET_LOG_FILE_PATH=/tmp/reducer.log reducer\n```\n\nTo disable writing a log file and instead output logging messages to the console:\n\n```\n$ reducer --no-log-file --log-console\n```\n\nReducer will normally listen for incoming connections from collectors on port 8000.\nTo select a different port, use the `--port` command-line parameter:\n\n```\n$ reducer --port=9000\n```\n\nMake sure to use the same port number when running the collectors (the `EBPF_NET_INTAKE_PORT` environment variable).\n\n## Prometheus output ##\n\nBy default, reducer will make metrics available for scraping in the Prometheus format.\nUnless specified with the `--prom` command-line parameter, reducer will run a HTTP server on 127.0.0.1:7010.\nTo use a different port, or to make scraping externally accessible:\n\n```\n$ reducer --prom=0.0.0.0:7010\n```\n\nNow an external Prometheus instance will be able to access it. Example Prometheus scraping configuration:\n\n```\nscrape_configs:\n- job_name: 'opentelemetry-ebpf-reducer'\n  static_configs:\n  - targets:\n    - '192.168.0.101:7010'\n```\n\n\n## OpenTelemetry Protocol (OTLP) over gRPC ##\n\nTo send metrics using OTLP over gRPC, e.g. to an OpenTelemetry collector:\n\n```\n$ reducer --disable-prometheus-metrics --enable-otlp-grpc-metrics --otlp-grpc-metrics-host=192.168.0.212\n```\n\nThe default OTLP-over-gRPC port is 4317. To send to a different port the `--otlp-grpc-metrics-port`\ncommand-line parameter is used.\n\nTo conserve bandwidth, metric descriptions are not sent in messages.\nTo enable sending metric descriptions use the `--enable-otlp-grpc-metric-descriptions` command-line parameter.\n\n\n## Choosing metrics ##\n\nIt is possible to select which metrics are generated by using the `--disable-metrics` and `--enable-metrics`\ncommand-line parameters. Both parameters accept a comma-separated list of metrics to enable or disable.\nThe `--enable-metrics` parameter has precedence.\n\nMetrics are grouped into four groups: tcp, udp, dns and http.\nTo refer to all metrics within a group, `<group>.all` can be used.\n\nFor example, to disable all TCP metrics except the number of bytes transferred:\n\n```\n$ reducer --disable-metrics=tcp.all --enable-metrics=tcp.bytes\n```\n\n\n## Feature flags ##\n\nCertain features are disabled by default, but can be enabled using a command-line flag:\n\n- `--enable-aws-enrichment`: Enables enrichment using AWS metadata received from the Cloud Collector.\n  A Cloud Collector instance needs to be running and connected to the reducer for this feature to work.\n- `--enable-autonomous-system-ip`: Enables using IP addresses for autonomous systems (AS).\n  This feature requires loading a GeoIP database by specifying its path using the `GEOIP_PATH` environment variable.\n- `--enable-id-id`: Enables id-id time-series generation. The id-id time-series carry the lowest-level information\n  but are of the greatest volume and cardinality, so are disabled by default.\n- `--enable-az-id`: Enables az-id time-series generation.\n\nIf id-id time-series generation is enabled, the `--disable-node-ip-field` command-line parameter can be used to\ndisable the IP address dimension. In some cases this can greatly reduce the cardinality of the id-id time-series.\n\n\n## Scaling ##\n\nAt present, scaling the reducer is a manual try-and-see task.\nThe reducer runs a data processing pipeline separated into three stages – ingest, matching and aggregation.\nEach stage can be scaled individually using the `--num-ingest-shards`, `--num-matching-shards` and `--num-aggregation-shards`\ncommand-line parameters.\n\nUsually, the best approach is to scale all the stages by the same factor. Keep in mind that each shard consumes a certain\namount of memory, whether it is heavily loaded or not.\n\n\n## Internal metrics ##\n\nInternal metrics (also known as stats) are time-series that show information on reducer and collectors performance.\n\nIf OTLP over gRPC is enabled (with the `--enable-otlp-grpc-metrics` flag), then internal metrics are sent to the\nsame OTLP receiver as are normal metrics, using ebpf_net as the group name.\n\nIf OTLP over gRPC is not enabled, internal metrics will be published in Prometheus format.\nBy default, reducer will run a HTTP server on port 0.0.0.0:7010 where internal metrics can be scraped.\nThe `--internal-prom` command-line parameter can be used to change the bind address and port number.\n\nSelecting which internal metrics are generated is also done using the `--disable-metrics` and `--enable-metrics`\ncommand-line parameters. Internal metrics are contained in the ebpf_net group.\nFor example, to enable just the \"up\" internal metric:\n\n```\n$ reducer --disable-metrics=ebpf_net.all --enable-metrics=ebpf_net.up\n```\n\n\n## Logging and debugging ##\n\nThere are six logging levels: trace, debug, info, warning, error and critical.\nMinimum logging level can be set using the `--<level>` command-line parameter, e.g. `--debug`.\nThe default logging level is info.\n\nMost of the debug- and trace-level logging is not generated if not explicitly enabled.\nEnabling it is done on a subsystem and component basis:\n\n- `--log-whitelist-client-type`: Enables logging based on collector type, e.g. kernel, cloud, k8s.\n- `--log-whitelist-node-resolution-type`: Enables logging based on resolution type.\n- `--log-whitelist-channel`: Enables logging for various communication channel types, e.g. tcp, upstream, reconnecting_channel.\n- `--log-whitelist-ingest`: Enables logging for the ingest stage of the data-processing pipeline.\n- `--log-whitelist-matching`: Enables logging for the matching stage of the data-processing pipeline.\n\nThe `--log-whitelist-all` command-line flag will enable logging for all components and subsystems.\n\n\n## Environment variables ##\n\n- `EBPF_NET_DATA_DIR`: Directory in which the program will read and potentially write data files.\n  If not specified the current working directory will be used.\n- `EBPF_NET_LOG_FILE_PATH`: Location of the file in which logging messages are written.\n  Default value is /var/log/ebpf_net.log.\n- `GEOIP_PATH`: Location of the geolocation database file.\n"
  },
  {
    "path": "docs/render.md",
    "content": "# Render Framework #\n\nThe Render Framework consists of a code-generator and a supporting library.\nThe intention is to reduce the boilerplate code that is required when writing\ndistributed applications based on the message-passing model.\n\n\n## At a Glance ##\n\nA distributed application will be defined in one _.render_ file.\n\n```\n# file ebpf_net.render\n\npackage ebpf_net\n\nnamespace {\n  ingest: 301-310,321-330,341,350-360,390-420,491-520,531-550\n  ...\n}\n\napp ingest {\n\n  span agent {\n    pool_size 512\n    singleton\n\n    110: log connect {\n      description \"called by the agent to connect to intake\"\n      1: u8 collector_type\n      2: string hostname\n    }\n    ...\n  }\n\n  ...\n}\n\n...\n```\n\nThe `package` keyword gives name both to the namespace in which the C++ code is generated\nand the output directory.\n\nInside a package, a number of _apps_ are defined with the `app` statement.\nAn app can be thought of as a separate process or an isolated thread within a process that\ncontains its own (not shared with other threads) data structures.\n\nInside an app, a number of _span_types_ are declared using the `span` statement.\nInstances of a span type are called _spans_. Spans can be thought of as objects that\nencapsulate state.\n\nThe `pool_size` keyword specifies the maximum number of spans of this type that can be\ninstantiated in its span pool.\n\nSpans can specify a number of messages that they can receive. Messages are declared using\nthe `msg`, `log`, `start` and `end` keywords.\n\nApps communicate by sending one-way _messages_. Spans receive messages, act upon them, and in\nmany cases send messages themselves.\n\nMessages are one-directional, with no delivery notifications.\n\nThe `namespace` block defines the ranges in which RPC IDs of messages will be mapped to.\nIn the example given above, the `connect` message has an index of 110 inside the `ingest` app,\nwhich falls into the 531-to-550 range. The resulting global RPC ID of the `connect` message is\nthen 548.\n\n\n## Spans ##\n\nWhen a message intended for a certain span type is received, a live instance of\nthat type must be present to receive the message. There are three ways that span\nlife-times are managed when it pertains to messaging: singletons, reference fields\nand proxies.\n\n\n### Singletons ###\n\n```\napp ingest {\n\n  span agent {\n    pool_size 512\n    singleton\n\n    110: log connect {\n      description \"called by the agent to connect to intake\"\n      1: u8 collector_type\n      2: string hostname\n    }\n    ...\n  }\n\n}\n```\n\nIn this example the `agent` span type declaration includes the `singleton` keyword,\nmaking it a singleton instance *per client*. This means that an instance will be\nallocated for each upstream client that is connected, and will be deallocated when\nthis client disconnects. All messages sent by a particular client will be received\nby this instance.\n\n\n### Referenced Instances ###\n\n```\napp ingest {\n\n  span process {\n    pool_size 10000000\n\n    0: start pid_info ref pid {\n      description \"new process info\"\n      1: u32 pid\n      2: u8 comm[16]\n    }\n\n    5: end pid_close_info ref pid {\n      1: u32 pid\n      ...\n    }\n\n    39: log pid_cgroup_move ref pid {\n      1: u32 pid\n      ...\n    }\n\n    ...\n  }\n\n}\n```\n\nIn this example the `process` span will be allocated when a _start_ message is\nreceived -- `pid_info` in this example -- and deallocated when an _end_ message\nis received -- `pid_close_info`. During its lifetime, this instance will receive\n_log_ messages.\n\nThe `ref` keyword specifies which field of the message should be used as an unique\nreference that identifies a particular span instance.\n_Start_, _end_ and _log_ messages must contain this refence field.\n\n\n### Proxy Spans ###\n\nA span in an upstream app can be a proxy for a target span in a downstream application.\n\n```\napp ingest {\n  span flow {\n    index (addr1, port1, addr2, port2)\n    proxy matching.flow shard_by (addr1, port1, addr2, port2)\n\n    u128 addr1\n    u16 port1\n    u128 addr2\n    u16 port2\n  }\n}\n\napp matching {\n  span flow {\n    index (addr1, port1, addr2, port2)\n\n    u128 addr1\n    u16 port1\n    u128 addr2\n    u16 port2\n\n    3: msg task_info {\n      1: u8 side\n      2: string comm\n      3: string cgroup_name\n    }\n  }\n}\n```\n\nIn this example the `ingest::flow` is a proxy for `matching::flow` span. When we instantiate\na flow span in the `ingest` app, a flow span will automatically be instantiated in the\n`matching` app.\n\nCalling message functions on the proxy span sends messages to its target span. For example:\n\n```\n// reducer/ingest/flow_updater.cc\n\nebpf_net::ingest::keys::flow flow_key = {...};\nauto flow = local_index()->flow.by_key(flow_key);\n\nauto process = process_handle_.access(*local_index());\n\nflow.task_info(\n    (u8)side_,\n    jb_blob(process.comm()),\n    jb_blob(process.cgroup().name()));\n```\n\n\n### Span Implementations ###\n\nMessages received by spans usually cause some state mutation in the span instances.\n\nThis state mutation is not done by the framework, it is up to us to write code that does that.\n\nThe way to do it is to write a _span implementation_ in C++ and attach it to the span type.\nIn the span implementation we can handle all messages that the span receives,\neven start and end messages, and we can access the span instance through the first parameter\nthat all handler methods receive.\n\nIn this example we're accessing the local index and are modifying the span's `cgroup` field:\n\n```\n// ebpf_net.render\n\napp ingest {\n\n  span process\n    impl \"reducer::ingest::ProcessSpan\"\n    include \"<reducer/ingest/process_span.h>\"\n  {\n    reference<cgroup> cgroup\n\n    39: log pid_cgroup_move ref pid {\n      1: u32 pid\n      2: u64 cgroup\n    }\n    ...\n  }\n\n}\n```\n\n```\n// reducer/ingest/process_span.h\n\n#include <generated/ebpf_net/ingest/span_base.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\nnamespace reducer::ingest {\n\nclass ProcessSpan : public ebpf_net::ingest::ProcessSpanBase {\npublic:\n  void pid_cgroup_move(\n      ebpf_net::ingest::weak_refs::process span_ref,\n      u64 timestamp,\n      jsrv_ingest__pid_cgroup_move *msg);\n  // ...\n};\n```\n\n```\n// reducer/ingest/process_span.cc\n\nvoid ProcessSpan::pid_cgroup_move(\n    ebpf_net::ingest::weak_refs::process span_ref,\n    u64 timestamp, jsrv_ingest__pid_cgroup_move *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n\n  auto cgroup_span = conn->get_cgroup(msg->cgroup);\n  if (cgroup_span.valid()) {\n    // log a \"cgroup not found\" error\n    return;\n  }\n  span_ref.modify().cgroup(cgroup_span.get());\n}\n\n```\n\n\n## Compiler ##\n\nThe [Render Compiler](https://github.com/open-telemetry/opentelemetry-ebpf/tree/main/renderc)\ngenerates C and C++ code for each app in the input file.\n\n```\n$ java \\\n  -jar io.opentelemetry.render.standalone-1.0.0-SNAPSHOT-all.jar \\\n  -i opentelemetry-ebpf/render \\\n  -o build/generated\n```\n\nThe `-i` command-line parameter specifies the directory that contains one or more\ninput files with the .render extension. Usually only one input file is used.\n\nThe `-o` command-line parameter specifies the output directory where the generated\ncode will is written to.\n\nFor each app, a number of C and C++ source files will be written into the\n`<outdir>/<package>/<app>` directory.\n\n\n### CMake ###\n\nTo make it easier to integrate the Render Framework, the `render_compile` CMake function\nis provided (see cmake/render.cmake).\n\nExample usage:\n\n```\nrender_compile(\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  PACKAGE\n    ebpf_net\n  APPS\n    agent_internal\n    kernel_collector\n    cloud_collector\n    ingest\n    matching\n    aggregation\n    logging\n  COMPILER\n    ${RENDER_COMPILER}\n  OUTPUT_DIR\n    \"${CMAKE_BINARY_DIR}/generated\"\n)\n```\n\nThe `RENDER_COMPILER` variable should point to the _jar_ file containing the\nstandalone render compiler (io.opentelemetry.render.standalone-1.0.0-SNAPSHOT-all.jar).\n\nThis builds a number of libraries:\n- `render_<package>_<app>`: to be link with the code that is implementing an app\n- `render_<package>_<app>_writer`: to be linked with the code calling (messaging) an app\n- `render_<package>_<app>_hash`: linked by the receiver app if it manually dispatches messages\n\n\n## C++ Classes ##\n\nThe render compiler will generate a number of classes for each app in the package.\nNotable classes are:\n\n`Writer` class instances are used for encoding and sending messages.\n\nAlthough belonging to the target app namespace, writers are used by clients of that app, e.g.\nebpf_net::ingest::Writer is used by collectors to send messages to reducer's ingest.\n\n`Index` object is the root of all render-managed state.\n- accessed both by the generated message-handling code and by the custom C++ code;\n- contains spans that are created by messages and spans that are created programmatically.\n\n`Connection` class implements handlers for all messages.\n- creates and deletes span objects (start and end messages);\n- tracks mappings from client-supplied references to handles into the Index (the ref keyword);\n- calls custom message handers on span implementations (the `impl` keyword).\n\n`Protocol` class decodes received messages, invokes appropriate message handlers.\n"
  },
  {
    "path": "docs/roadmap.md",
    "content": "# Roadmap #\n\n\n## Short-term ##\n\n### Packaging ###\n\nBesides providing docker images, the project will build and provide RPM and DEB\npackages for every release.\n\n### Test coverage ###\n\nThe project will increase test coverage for the codebase by at least 15%.\n\n\n## Medium-term ##\n\n### Refactoring the k8s-collector ###\n\nThe Kubernetes Collector consists of two components. One component is written in\nthe Go language to take advantage of a stable Kubernetes client library. The\nother component is written in C++ to make use of the custom messaging code. This\ndesign is forced and is sub-optimal. We will rewrite the entire Kubernetes\nCollector in one language, be it Go or C++ (or Rust).\n\n### Replacing Xtend with plain Java ###\n\nThe Render compiler component is written in the Xtend language, a Java\ndialect. Modern features available in recent Java versions make the use of Xtend\nobsolete. We can replace all Xtend source code with plain (modern) Java.\n\n### ARM support ###\n\nWe will add support for AArch64/Arm64 Linux distributions.\n\n### CO-RE support ###\n\nKernels with support for Compile Once Run Everywhere offer eBPF deployments with\nfewer failure modes and cost: there is no header fetching and on-node\ncompilation. Docker images can be significantly smaller, as deployment no longer\nrequires LLVM.\n\n### Layer 7 collection and parsing ###\n\nThe project could support collecting the data streams that pass between observed\nservices, and to implement modular/pluggable parsers to extract\nprotocol-specific data. The source could be sockets (for unencrypted traffic)\nand TLS user-space libraries (to collect cleartext versions of encrypted\ntraffic).\n\n\n## Long-term ##\n\n### Load-rebalanced reducer components ###\n\nThe reducer should comprise of components that can be distributed in the user's\ncluster for scaling up and down and for zero-downtime fault tolerance. When a\ncompute node holding a component fails, the other components will re-balance\ntheir workload to the live replicas and synchronize the required state to the\nnew replicas owning that state. When a replacement replica is scheduled, load is\nagain rebalanced.\n\n### Rust: developer productivity and compiler-assisted code stability ###\n\nA lot of the robustness in the current system came at a high development\ncost. Many of the vulnerable/fragile areas in the code deal with memory\nmanagement. This has caused less innovation on the more fragile components, and\nis slowing down developments on parts that interact with fragile code. Rust\noffers dramatic developer productivity improvements over C++ workflows with its\nborrow checker, generics, dependency management, and IDE support. Rust can be a\ncritical competitive advantage in attracting contributors to the project, making\ncontributing easier and more delightful. Rust also compiles binaries statically,\nenabling minimal/empty distros and thus eliminating distribution of all\nunnecessary code (hence smaller docker images).\n\n### Modular collection ###\n\nSupport for eBPF collection modules that can be developed and enabled\nindependently. The kernel collector will offer the framework for module\n\"libraries\" to add instrumentation, with well documented interfaces. The code of\nmultiple libraries should not be barred from interacting if desired (e.g., by\ncalling functions across modules). However, ideally modules would be\nindependently developed, with only data dependencies. For example, the process\nand container modules both use the same container ID, so the process module can\nreport which container each process belongs to. To facilitate processing,\nmodules can further guarantee cross-module invariants. For example, the process\nmodule can guarantee new process events arrive after container metadata\nevents. Testing \"mini-reducers\" can test the modules and their invariants.\n\n### Module convergence ###\n\nWe should develop the technical solutions that enable the open-source community\nto benefit from eBPF instrumentation and infrastructure developed across\ndifferent projects. One contribution could be standardizing eBPF instrumentation\nmodules: specifying kernel compatibility, code loading, configuration and\ntelemetry egress formats. Another one could be message formatting (the \"eBPF\nprotobuf\").\n\n### Collection breadth ###\n\nThe project will host a variety of collection modules. Modules can instrument\nkernel scheduling (fine grained CPU metrics) and memory subsystem (page faults\nand memory bandwidth), and the virtual filesystem (which process accesses which\nfiles on NFS, I/O latencies and bandwidths). Performance counters can provide\nCPU and memory profiling data.\n\n### Userspace instrumentation ###\n\nWhere application-layer data is desired and there is value in not obtaining such\ndata through application-layer instrumentation, eBPF can provide this user\ninstrumentation.\n\n### Linking application trace IDs to kernel events ###\n\nApplication and infrastructure observability should not form disjoint\ndatasets. OS utilization occurs due to applications serving user\nworkloads. Ideally, an observability system could attribute OS events to a user\nrequest (a trace). Conversely, applications experience OS performance\ndegradation. Ideally observability systems could attribute application\nperformance to OS events. The project will implement mechanisms for application\ninstrumentation libraries to communicate trace IDs to eBPF instrumentation, so\nOS events can be attributed to user flows.\n\n### High cardinality storage ###\n\nFiner granularity data can be valuable in many use-cases: tracking a malicious\nactor through a network, diagnosing replication problems in a distributed system\nsuch as etcd, investigating specific traces etc. However, while systems generate\nhuge amounts of fine-grained data, the use-cases require access to only\nrelatively miniscule amounts at a time. The cost of encoding, transmitting and\nthen ingesting the data into traditional time-series databases is\noverwhelming. Instead, the reducer will efficiently encode its state into data\nblocks that are stored in block storage on the user account (e.g., S3,\nGCS). When the data is required, those blocks could be converted to an open\nformat (like Parquet) for further processing, or transferred to a SaaS provider\nfor rehydration into a commercial solution.\n\n### Serving queries from within user deployments ###\n\nTo facilitate use of high cardinality data during incidents (as opposed to\npost-incident reviews), the project will provide a query interface for the\ndata. The data resides alongside instrumented clusters, and the UI sends queries\n\"to the data\", to user deployments. The project will choose among an available\nquery standard or specify such a standard. Candidates could include Prometheus\nremote read API, the Pixie Labs protocol, and promql.\n"
  },
  {
    "path": "docs/running-multiple-agents.md",
    "content": "# Running multiple agents\n\nTo avoid kprobe leaks, kernel collectors by default set each kprobe event name to the same value prepending `ebpf_net_` to it \\(see `/sys/kernel/debug/tracing/kprobe_events`\\). While letting agents reuse the name after the crash, such choice also prevents running multiple agents on the same host.\n\nTo be able to run multiple agents on the same machine, one can set the `BCC_PROBE_SUFFIX` environment variable to an arbitrary string. This will append the string to the kprobe event name making it unique among the agents. For example, one can set `BCC_PROBE_SUFFIX=agent1` and `BCC_PROBE_SUFFIX=agent2` environment variables for the two agents to prevent the conflict. In case of an agent crash we may still leak the probes. However, the impact is limited only to this small set \\(as opposed to appending process PID, where the number of leaked probes can be arbitrarily large\\).\n\n"
  },
  {
    "path": "docs/tcp-and-udp.md",
    "content": "# TCP and UDP\n\n## TCP and UDP\n\n### TCP\n\n#### Messages\n\n* new\\_sock\\_created\n* reset\\_tcp\\_counters\n* close\\_sock\\_info\n* set\\_state\\_ipv4\n* set\\_state\\_ipv6\n* rtt\\_estimator\n* tcp\\_syn\\_timeout\n* tcp\\_reset\n* http\\_response\n* tcp\\_data\n\nMessages _new\\_sock\\_created_ and _reset\\_tcp\\_counters_ are sent when a new TCP socket is created. The _close\\_sock\\_info_ message is sent when a TCP socket is closed.\n\nMessages _set\\_state\\_ipv4_ and _set\\_state\\_ipv6_ are sent when a socket is assigned a IP address and port number.\n\nThe _rtti\\_estimator_ message conveys socket stats, as reported by the kernel.\n\nThe _tcp\\_syn\\_timeout_ message is sent when a socket experiences a timeout in SYN\\_RECV or SYN\\_SENT state. The _tcp\\_reset_ message is sent when a TCP RST was sent or received.\n\nMessages _http\\_response_ and _tcp\\_data_ are sent by the TCP-processor system, and are not strictly part of the TCP socket contract.\n\n#### TCP resets\n\n[https://www.pico.net/kb/what-is-a-tcp-reset-rst](https://www.pico.net/kb/what-is-a-tcp-reset-rst)\n\nWhen an unexpected TCP packet arrives at a host, that host usually responds by sending a reset packet back on the same connection. A reset packet is simply one with no payload and with the RST bit set in the TCP header flags.\n\nThere are a few circumstances in which a TCP packet might not be expected; the two most common are:\n\n* The packet is an initial SYN packet trying to establish a connection to a server port on which no process is listening.\n* The packet arrives on a TCP connection that was previously established, but the local application already closed its socket or exited and the OS closed the socket.\n\nOther circumstances are possible, but are unlikely outside of malicious behavior such as attempts to hijack a TCP connection.\n\n#### Collection\n\nOn the receive side, we attach to the `tcp_reset` kernel function which is called whenever kernel receives a packet with RST flag set. On the send side, there is no such convenient single function in the kernel which is called when sending a reset. We have to directly inspect packet data in the `skb` structure and check if `TCP_FLAG_RST` is set in the TCP segment header. This is done in `on_ip_send_skb` and `on_ip6_send_skb`.\n\nAfter detecting a reset packet, we notify the userland portion of the collector relaying the socket on which the reset was sent/received along with the packet direction \\(i.e., `RX` or `TX`\\).\n\nTCP reset counts are sent to the pipeline server ingest and are written to the timeseries database as a unitless counts like SYN timeouts or TCP retransmits.\n\n### UDP\n\n#### Messages\n\n* udp\\_new\\_socket\n* udp\\_destroy\\_socket\n* udp\\_stats\n* dns\\_packet\n\nThe _udp\\_new\\_socket_ message is sent when a new UDP socket is created. The _udp\\_destroy\\_socket_ message is sent when a UDP socket is closed.\n\nThe _udp\\_stats_ message conveys UDP socket stats, as reported by the kernel.\n\nThe _dns\\_packet_ message is sent when a DNS packet sent or received by an application.\n\n## NAT\n\nThe part of the Linux kernel where NAT is implemented is called the _netfilter connection tracking system_, or _conntrack_.\n\nWe instrument a couple of kernel functions that deal with conntrack to extract NAT mappings.\n\n### Messages\n\nThe following messages are sent from kernel-collector BPF to user-space:\n\n* _existing\\_conntrack\\_tuple_\n* _nf\\_conntrack\\_alter\\_reply_\n* _nf\\_nat\\_cleanup\\_conntrack_\n* _set\\_state\\_ipv4_\n\nThe _existing\\_conntrack\\_tuple_ message is sent during enumeration of existing conntracks -- those that were created before the kernel collector reached a steady state.\n\nThe _nf\\_conntrack\\_alter\\_reply_ message is sent when there is new a translation for a socket's local or remote IP address \\(or both\\).\n\nThe _nf\\_nat\\_cleanup\\_conntrack_ message is sent when a conntrack is deleted.\n\nThe three conntrack messages contain the `u64 ct` field that uniquely identifies a conntrack.\n\nThe _set\\_state\\_ipv4_ message assigns socket's local and remote IP address and port number.\n\n#### Source/destination confusion\n\nIt is important to note that message fields that refer to _source_ or _destination_ IP address and port number don't refer to source and destination of a NAT mapping, but to IP socket's **local** and **remote** IP address and port number.\n\n### Existing conntracks\n\nTwo _existing\\_conntrack\\_tuple_ are needed to establish a NAT mapping: one with the `u8 dir` field set to 0, and the other set to 1. The message with _dir_ set to 0 specifies the mappings source, while the message with _dir_ set to 1 specifies the mapping destination.\n\nIt is required that message with `dir == 0` precedes the corresponding message with `dir == 1`.\n\n### New conntracts\n\nThe _nf\\_conntrack\\_alter\\_reply_ message specifies both the mapping source and destination.\n\nIt is not required that a socket with the matching IP address and port number exist at this time. The _set\\_state\\_ipv4_ message can arrive before or after the _nf\\_conntrack\\_alter\\_reply_ message_._\n\n"
  },
  {
    "path": "geoip/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_library(\n  geoip_wrapper\n  STATIC\n    geoip.cc\n)\ntarget_link_libraries(\n  geoip_wrapper\n    libmaxminddb\n)\ntarget_compile_options(\n  geoip_wrapper\n    PRIVATE\n      -Werror\n      -Wall\n      -Wextra\n)\n"
  },
  {
    "path": "geoip/geoip.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"geoip.h\"\n\n#include <cstring>\n\nnamespace geoip {\n\n///////////////////\n// address_entry //\n///////////////////\n\ndata_unit address_entry::get_impl(char const *const *path)\n{\n  MMDB_entry_data_s data;\n  auto result = ::MMDB_aget_value(&entry_.entry, &data, path);\n\n  if (result != MMDB_SUCCESS) {\n    data.has_data = false;\n  }\n\n  return data_unit(data);\n}\n\n//////////////\n// database //\n//////////////\n\ndatabase::database(std::nothrow_t, std::initializer_list<char const *> db_path, open_mode mode)\n{\n  auto flags = static_cast<std::uint32_t>(mode);\n\n  for (auto const path : db_path) {\n    if (!path) {\n      continue;\n    }\n\n    auto result = ::MMDB_open(path, flags, &db_.emplace());\n\n    if (result == MMDB_SUCCESS) {\n      break;\n    }\n\n    db_.reset();\n  }\n}\n\ndatabase::database(std::initializer_list<char const *> db_path, open_mode mode)\n{\n  auto flags = static_cast<std::uint32_t>(mode);\n\n  if (!db_path.size()) {\n    throw std::runtime_error(\"no geoip database paths given\");\n  }\n\n  int error = MMDB_SUCCESS;\n  for (auto const path : db_path) {\n    if (!path) {\n      continue;\n    }\n\n    auto result = ::MMDB_open(path, flags, &db_.emplace());\n\n    if (result == MMDB_SUCCESS) {\n      break;\n    } else {\n      error = result;\n    }\n\n    db_.reset();\n  }\n\n  if (!db_.has_value()) {\n    assert(error != MMDB_SUCCESS);\n    throw std::runtime_error(::MMDB_strerror(error));\n  }\n}\n\ndatabase::database(char const *db_path, open_mode mode) : database({db_path}, mode) {}\n\ndatabase::database(std::nothrow_t, char const *db_path, open_mode mode) : database(std::nothrow, {db_path}, mode) {}\n\ndatabase::~database()\n{\n  if (db_.has_value()) {\n    ::MMDB_close(&*db_);\n  }\n  db_.reset();\n}\n\naddress_entry database::lookup(sockaddr const *address)\n{\n  int error = MMDB_SUCCESS;\n  auto entry = ::MMDB_lookup_sockaddr(&*db_, address, &error);\n\n  if (error != MMDB_SUCCESS) {\n    entry.found_entry = false;\n  }\n\n  return address_entry(entry);\n}\n\naddress_entry database::lookup(in_addr const *ip_address)\n{\n  sockaddr_in address{};\n  address.sin_family = AF_INET;\n  std::memcpy(&address.sin_addr, ip_address, sizeof(*ip_address));\n  return lookup(&address);\n}\n\naddress_entry database::lookup(in6_addr const *ip_address)\n{\n  sockaddr_in6 address{};\n  address.sin6_family = AF_INET6;\n  std::memcpy(&address.sin6_addr, ip_address, sizeof(*ip_address));\n  return lookup(&address);\n}\n\naddress_entry database::lookup(char const *ip)\n{\n  int gai_error = 0;\n  int mmdb_error = MMDB_SUCCESS;\n  auto entry = ::MMDB_lookup_string(&*db_, ip, &gai_error, &mmdb_error);\n\n  if (gai_error || mmdb_error != MMDB_SUCCESS) {\n    entry.found_entry = false;\n  }\n\n  return address_entry(entry);\n}\n\n} // namespace geoip\n"
  },
  {
    "path": "geoip/geoip.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <maxminddb.h>\n\n#include <initializer_list>\n#include <new>\n#include <optional>\n#include <stdexcept>\n#include <string_view>\n#include <type_traits>\n\n#include <cassert>\n#include <cstdint>\n\n#include \"geoip.inl\"\n\n/**\n * RAII-style user friendly API to query GeoIP databases.\n *\n * Example:\n *\n *  using namespace geoip;\n *\n *  database db(\"path/to/geoip-db.mmdb\");\n *\n *  auto entry = db.lookup(\"1.2.3.4\");\n *  if (!entry) {\n *    std::cout << \"no entry found for ip 1.2.3.4\\n\";\n *    return;\n *  }\n *\n *  std::cout << entry.get_as<std::string_view>(\"autonomous_system_organization\") << '\\n';\n *\n *  std::uint32_t asn;\n *  if (entry.try_get_as(asn, \"autonomous_system_number\") {\n *    std::cout << asn << '\\n';\n *  }\n *\n *  std::cout << well_known_data::autonomous_system_organization(entry) << '\\n';\n */\nnamespace geoip {\n\n/*\n * Encapsulates a unit of data retrieved from the GeoIP database.\n *\n * struct api:\n *  https://github.com/maxmind/libmaxminddb/blob/master/doc/libmaxminddb.md#mmdb_entry_data_s\n *\n * data types:\n *  https://github.com/maxmind/libmaxminddb/blob/master/doc/libmaxminddb.md#data-type-macros\n */\nstruct data_unit {\n  using data_t = MMDB_entry_data_s;\n\n  /* implicit */\n  data_unit(data_t const &data) : data_(data) {}\n\n  /**\n   * Tells whether this data unit represents a string of bytes.\n   */\n  bool is_bytes() const { return data_.type == MMDB_DATA_TYPE_BYTES; }\n\n  /**\n   * Encloses the byte string in this data unit into the `out` string view if it represents a\n   * string of bytes, otherwise returns `false` and leaves `out` untouched.\n   */\n  bool try_bytes(std::string_view &out) const\n  {\n    assert(valid());\n\n    if (!is_bytes()) {\n      return false;\n    }\n\n    out = {reinterpret_cast<char const *>(data_.bytes), data_.data_size};\n    return true;\n  }\n\n  /**\n   * Returns a string view of the byte string in this data unit.\n   * No type checking is performed so if the data unit doesn't represent a byte string,\n   * the contents of the string view are undefined.\n   */\n  std::string_view bytes() const\n  {\n    assert(valid());\n    assert(is_bytes());\n\n    return {reinterpret_cast<char const *>(data_.bytes), data_.data_size};\n  }\n\n  /**\n   * Tells whether this data unit represents a UTF-8 string.\n   */\n  bool is_utf8() const { return data_.type == MMDB_DATA_TYPE_UTF8_STRING; }\n\n  /**\n   * Encloses the UTF-8 string in this data unit into the `out` string view if it represents a\n   * UTF-8 string, otherwise returns `false` and leaves `out` untouched.\n   */\n  bool try_utf8(std::string_view &out) const\n  {\n    assert(valid());\n\n    if (!is_utf8()) {\n      return false;\n    }\n\n    out = {data_.utf8_string, data_.data_size};\n    return true;\n  }\n\n  /**\n   * Returns a string view of the UTF-8 string in this data unit.\n   * No type checking is performed so if the data unit doesn't represent a UTF-8 string,\n   * the contents of the string view are undefined.\n   */\n  std::string_view utf8() const\n  {\n    assert(valid());\n    assert(is_utf8());\n\n    return {data_.utf8_string, data_.data_size};\n  }\n\n  /**\n   * Returns true if `T` can represent this data unit's data type.\n   */\n  template <typename T> bool is() const { return data_type<std::decay_t<T>>::same(data_); }\n\n  /**\n   * Converts this data unit's data to `T` and assigns it to `out` if `T` can represent this\n   * data unit's data type. Otherwise returns `false` and leaves `out` untouched.\n   */\n  template <typename T> bool try_to(T &out) const\n  {\n    assert(valid());\n\n    if (!is<T>()) {\n      return false;\n    }\n\n    out = data_type<std::decay_t<T>>::get(data_);\n    return true;\n  }\n\n  /**\n   * Converts this data unit's data to `T` and returns it.\n   * No type checking is performed so if the data unit can't be represented by `T`, the result is\n   * undefined.\n   */\n  template <typename T> auto to() const\n  {\n    assert(valid());\n    assert(is<T>());\n\n    return data_type<std::decay_t<T>>::get(data_);\n  }\n\n  /**\n   * Tells whether there's any valid data represented by this data unit.\n   */\n  bool valid() const { return data_.has_data; }\n\n  /**\n   * Tells whether there's any valid data represented by this data unit.\n   */\n  explicit operator bool() const { return valid(); }\n\n  /**\n   * Tells whether this data unit doesn't have any valid data.\n   */\n  bool operator!() const { return !valid(); }\n\nprivate:\n  data_t data_;\n};\n\n/**\n * Encapsulates the result of a lookup into the GeoIP database.\n *\n * data lookup api:\n *  https://github.com/maxmind/libmaxminddb/blob/master/doc/libmaxminddb.md#data-lookup-functions\n */\nstruct address_entry {\n  using entry_t = MMDB_lookup_result_s;\n\n  /* implicit */\n  address_entry(entry_t const &entry) : entry_(entry) {}\n\n  /**\n   * Retrieves a data unit for the given path.\n   *\n   * E.g.:\n   *\n   *    auto data = entry.get(\"autonomous_system_organization);\n   */\n  template <typename... Key> data_unit get(Key const *...keys)\n  {\n    char const *const path[] = {keys..., nullptr};\n    return get_impl(path);\n  }\n\n  /**\n   * Retrieves a data unit for the given path, then converts it to `T`, assigns it to `out` and\n   * returns true..\n   *\n   * If the path doesn't contain any data or if the data can't be converted to `T`,\n   * returns `false` and leaves `out` untouched.\n   */\n  template <typename T, typename... Key> bool try_get_as(T &out, Key const *...keys)\n  {\n    auto data = get(keys...);\n\n    return data && data.template try_to<T>(out);\n  }\n\n  /**\n   * Retrieves a data unit for the given path, then converts it to `T` and returns it.\n   * No type checking is performed so if the path doesn't have any data, or if the data can't be\n   * represented by `T`, the result is undefined.\n   */\n  template <typename T, typename... Key> T get_as(Key const *...keys)\n  {\n    auto data = get(keys...);\n\n    assert(data.valid());\n\n    return data.template to<T>();\n  }\n\n  /**\n   * Tells whether this entry is valid and can be used to retrieve data from.\n   */\n  bool valid() const { return entry_.found_entry; }\n\n  /**\n   * Tells whether this entry is valid and can be used to retrieve data from.\n   */\n  explicit operator bool() const { return valid(); }\n\n  /**\n   * Tells whether this entry is invalid and can't be used to retrieve data from.\n   */\n  bool operator!() const { return !valid(); }\n\nprivate:\n  entry_t entry_;\n\n  data_unit get_impl(char const *const *path);\n};\n\n/**\n * Helper for retrieval of well known data entries from a GeoIP database.\n */\nstruct well_known_data {\n  struct keys {\n    static constexpr auto const autonomous_system_number = \"autonomous_system_number\";\n    static constexpr auto const autonomous_system_organization = \"autonomous_system_organization\";\n  };\n\n  /**\n   * Retrieves the autonomous system number from the given entry if it is available. Returns\n   * `true` on success. If the data is not available, returns `false` and leaves `out` untouched.\n   */\n  static bool try_autonomous_system_number(std::uint32_t &out, address_entry &entry)\n  {\n    return entry.try_get_as(out, keys::autonomous_system_number);\n  }\n\n  /**\n   * Retrieves the autonomous system number from the given entry.\n   * No checks are performed so if the data is not available, the result is undefined.\n   */\n  static auto autonomous_system_number(address_entry &entry)\n  {\n    return entry.get_as<std::uint32_t>(keys::autonomous_system_number);\n  }\n\n  /**\n   * Retrieves the autonomous system organization from the given entry if it is available. Returns\n   * `true` on success. If the data is not available, returns `false` and leaves `out` untouched.\n   */\n  template <typename String> static bool try_autonomous_system_organization(String &out, address_entry &entry)\n  {\n    return entry.try_get_as(out, keys::autonomous_system_organization);\n  }\n\n  /**\n   * Retrieves the autonomous system organization from the given entry.\n   * No checks are performed so if the data is not available, the result is undefined.\n   */\n  static auto autonomous_system_organization(address_entry &entry)\n  {\n    return entry.get_as<std::string>(keys::autonomous_system_organization);\n  }\n\nprivate:\n  data_unit data_;\n};\n\n/*\n * Encapsulates a GeoIP database.\n *\n * api:\n *  https://github.com/maxmind/libmaxminddb/blob/master/doc/libmaxminddb.md\n */\nstruct database {\n  enum class open_mode : std::uint32_t {\n    /**\n     * Uses the default mode from `libmaxminddb` to open the database.\n     */\n    standard = 0,\n\n    /**\n     * Uses mmap to open the database.\n     */\n    mmap = MMDB_MODE_MMAP\n  };\n\n  /**\n   * Opens the GeoIP database from the given file.\n   *\n   * The way the database is opened can be specified through the `mode` flag.\n   *\n   * Throws `std::runtime_error` if unable to open.\n   */\n  explicit database(char const *db_path, open_mode mode = open_mode::mmap);\n\n  /**\n   * Opens the GeoIP database from the given file.\n   *\n   * The way the database is opened can be specified through the `mode` flag.\n   *\n   * This is the non throwing version of the constructor. Use the boolean operator\n   * to check if the database has been loaded.\n   */\n  explicit database(std::nothrow_t, char const *db_path, open_mode mode = open_mode::mmap);\n\n  /**\n   * Opens the GeoIP database from the given files. Tries each file in succession until one\n   * of them succeeds.\n   *\n   * The way the database is opened can be specified through the `mode` flag.\n   *\n   * Throws `std::runtime_error` if unable to open.\n   */\n  explicit database(std::initializer_list<char const *> db_path, open_mode mode = open_mode::mmap);\n\n  /**\n   * Opens the GeoIP database from the given files. Tries each file in succession until one\n   * of them succeeds.\n   *\n   * The way the database is opened can be specified through the `mode` flag.\n   *\n   * This is the non throwing version of the constructor. Use the boolean operator\n   * to check if the database has been loaded.\n   */\n  explicit database(std::nothrow_t, std::initializer_list<char const *> db_path, open_mode mode = open_mode::mmap);\n\n  /**\n   * Closes the database file and destroys this object.\n   */\n  ~database();\n\n  database(database &&) = default;\n  database(database const &) = delete;\n\n  database &operator=(database &&) = default;\n  database &operator=(database const &) = delete;\n\n  /**\n   * Uses the given raw address to lookup an entry in the GeoIP database.\n   */\n  address_entry lookup(sockaddr const *address);\n\n  /**\n   * Uses the given raw IPv4 address to lookup an entry in the GeoIP database.\n   */\n  address_entry lookup(in_addr const *ip_address);\n\n  /**\n   * Uses the given raw IPv6 address to lookup an entry in the GeoIP database.\n   */\n  address_entry lookup(in6_addr const *ip_address);\n\n  /**\n   * Uses the given raw IPv4 address to lookup an entry in the GeoIP database.\n   */\n  address_entry lookup(sockaddr_in const *address) { return lookup(reinterpret_cast<sockaddr const *>(address)); }\n\n  /**\n   * Uses the given raw IPv6 address to lookup an entry in the GeoIP database.\n   */\n  address_entry lookup(sockaddr_in6 const *address) { return lookup(reinterpret_cast<sockaddr const *>(address)); }\n\n  /**\n   * Uses the string representation of the address to lookup an entry in the GeoIP database.\n   *\n   * The underlying implementation will resolve the string to a raw address structure before\n   * performing the lookup, so if you have the raw address already, use the alternative overload.\n   */\n  address_entry lookup(char const *ip);\n\n  /**\n   * Tells whether the database was successfully loaded or not.\n   */\n  explicit operator bool() const { return db_.has_value(); }\n\n  /**\n   * Tells whether the database failed or succeeded to load.\n   */\n  bool operator!() const { return !db_.has_value(); }\n\n  /**\n   * Default local path for the Autonomous Systems database.\n   */\n  static constexpr char const *local_asn_path = \"GeoLite2-ASN.mmdb\";\n\n  /**\n   * Default global path for the Autonomous Systems database.\n   */\n  static constexpr char const *global_asn_path = \"/usr/share/GeoIP/GeoLite2-ASN.mmdb\";\n\nprivate:\n  std::optional<::MMDB_s> db_ = {};\n};\n\n} // namespace geoip\n"
  },
  {
    "path": "geoip/geoip.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// this file shouldn't be included by itself\n// it hides implementation details from the API header\n\n#pragma once\n\n#include <util/short_string.h>\n\n#include <algorithm>\n\nnamespace geoip {\n\n/**\n * Helper for proper data type handling in GeoIP data units.\n */\ntemplate <typename> struct data_type;\n\ntemplate <> struct data_type<std::string_view> {\n  using type = std::string_view;\n\n  /**\n   * Returns true if `data_type::type` can represent `data.type`.\n   */\n  static bool same(MMDB_entry_data_s const &data)\n  {\n    return data.type == MMDB_DATA_TYPE_BYTES || data.type == MMDB_DATA_TYPE_UTF8_STRING;\n  }\n\n  /**\n   * Converts the internal data in `data` and returns it as an instance of `data_type::type`.\n   */\n  static std::string_view get(MMDB_entry_data_s const &data)\n  {\n    auto string = data.type == MMDB_DATA_TYPE_BYTES ? reinterpret_cast<char const *>(data.bytes) : data.utf8_string;\n\n    return {string, data.data_size};\n  }\n};\n\ntemplate <> struct data_type<std::string> {\n  using type = std::string;\n\n  /**\n   * Returns true if `data_type::type` can represent `data.type`.\n   */\n  static bool same(MMDB_entry_data_s const &data)\n  {\n    return data.type == MMDB_DATA_TYPE_BYTES || data.type == MMDB_DATA_TYPE_UTF8_STRING;\n  }\n\n  /**\n   * Converts the internal data in `data` and returns it as an instance of `data_type::type`.\n   */\n  static std::string get(MMDB_entry_data_s const &data)\n  {\n    auto string = data.type == MMDB_DATA_TYPE_BYTES ? reinterpret_cast<char const *>(data.bytes) : data.utf8_string;\n\n    return std::string(string, data.data_size);\n  }\n};\n\ntemplate <std::size_t N> struct data_type<short_string<N>> {\n  using type = short_string<N>;\n\n  /**\n   * Returns true if `data_type::type` can represent `data.type`.\n   */\n  static bool same(MMDB_entry_data_s const &data)\n  {\n    return data.type == MMDB_DATA_TYPE_BYTES || data.type == MMDB_DATA_TYPE_UTF8_STRING;\n  }\n\n  /**\n   * Converts the internal data in `data` and returns it as an instance of `data_type::type`.\n   */\n  static short_string<N> get(MMDB_entry_data_s const &data)\n  {\n    auto string = (data.type == MMDB_DATA_TYPE_BYTES) ? reinterpret_cast<char const *>(data.bytes) : data.utf8_string;\n\n    return short_string<N>(string, std::min<std::size_t>(data.data_size, N));\n  }\n};\n\n#define REGISTER_GEOIP_DATA_TYPE(T, Type, Member)                                                                              \\\n  template <> struct data_type<T> {                                                                                            \\\n    using type = T;                                                                                                            \\\n    static bool same(MMDB_entry_data_s const &data)                                                                            \\\n    {                                                                                                                          \\\n      return data.type == Type;                                                                                                \\\n    }                                                                                                                          \\\n    static T const &get(MMDB_entry_data_s const &data)                                                                         \\\n    {                                                                                                                          \\\n      return data.Member;                                                                                                      \\\n    }                                                                                                                          \\\n  }\n\nREGISTER_GEOIP_DATA_TYPE(bool, MMDB_DATA_TYPE_BOOLEAN, boolean);\nREGISTER_GEOIP_DATA_TYPE(std::uint16_t, MMDB_DATA_TYPE_UINT16, uint16);\nREGISTER_GEOIP_DATA_TYPE(std::int32_t, MMDB_DATA_TYPE_INT32, int32);\nREGISTER_GEOIP_DATA_TYPE(std::uint32_t, MMDB_DATA_TYPE_UINT32, uint32);\nREGISTER_GEOIP_DATA_TYPE(std::uint64_t, MMDB_DATA_TYPE_UINT64, uint64);\nREGISTER_GEOIP_DATA_TYPE(float, MMDB_DATA_TYPE_FLOAT, float_value);\nREGISTER_GEOIP_DATA_TYPE(double, MMDB_DATA_TYPE_DOUBLE, double_value);\n\n#undef REGISTER_GEOIP_DATA_TYPE\n\n} // namespace geoip\n"
  },
  {
    "path": "jitbuf/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nadd_library(\n  jitbuf\n  STATIC\n    fixed_handler.c\n)\ntarget_link_libraries(\n  jitbuf\n    element_queue\n)\n"
  },
  {
    "path": "jitbuf/descriptor.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <stdexcept>\n#include <vector>\n\nnamespace jitbuf {\n\nstruct Field {\n  /* field's id */\n  u16 field_id;\n\n  /* ftype */\n  enum class ftype_t { INT8, INT16, INT32, INT64, VAR, INT128 } ftype;\n\n  /* array size. 1 can mean array of size 1 or non-array. */\n  u16 n_elems;\n\n  /* position, valid only after compute_positions() */\n  u16 pos;\n\n  /**\n   * @return the size of the field\n   * @param packed_strings: whether strings are encoded at the end of the\n   *   fixed message\n   */\n  inline u16 size(bool packed_strings) const;\n};\n\nstruct Descriptor {\n  /* the rpc id of the message */\n  u16 rpc_id;\n\n  /* the fields */\n  std::vector<Field> fields;\n\n  /* is this message dynamically sized */\n  bool dynamic_size;\n\n  /* total size of the message's fixed part (without packed strings) */\n  u16 size;\n\n  /* number of VAR fields */\n  u16 n_var_fields;\n};\n\n} /* namespace jitbuf */\n\n/*************\n * impl\n */\nu16 jitbuf::Field::size(bool packed_strings) const\n{\n  switch (ftype) {\n  case Field::ftype_t::INT8:\n    return 1;\n  case Field::ftype_t::INT16:\n    return 2;\n  case Field::ftype_t::INT32:\n    return 4;\n  case Field::ftype_t::INT64:\n    return 8;\n  case Field::ftype_t::VAR:\n    return (packed_strings ? 2 : 4);\n  case Field::ftype_t::INT128:\n    return 16;\n  }\n\n  throw std::runtime_error(\"unknown ftype\");\n}\n"
  },
  {
    "path": "jitbuf/descriptor_reader.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <jitbuf/descriptor_reader.h>\n\n#include <assert.h>\n#include <stdexcept>\n\nnamespace jitbuf {\n\nDescriptor DescriptorReader::read(u8 *buffer, u16 len)\n{\n  if (len < 8)\n    throw std::runtime_error(\"descriptor should be >= 8 bytes\");\n\n  u16 *buf16 = (u16 *)buffer;\n\n  /* unpack header */\n  u16 flags = buf16[0];\n  u16 rpc_id = buf16[1];\n  u16 n_fields = buf16[2];\n  u16 n_arrays = buf16[3];\n\n  /* sanity check header */\n  assert(flags == 0);\n  (void)flags; /* for unused warning */\n\n  if (len < 8 + 2 * n_fields + 2 * n_arrays)\n    throw std::runtime_error(\"descriptor too small for n_fields+n_arrays\");\n\n  if (n_arrays > n_fields)\n    throw std::runtime_error(\"n_arrays must not be larger than n_fields\");\n\n  /* make return Descriptor */\n  Descriptor res;\n  res.rpc_id = rpc_id;\n  res.fields.resize(n_fields);\n\n  /* convenience pointers to start of fields and arrays */\n  u16 *fields = &buf16[4];\n  u16 *arrays = &buf16[4 + n_fields];\n\n  /* array_index is the index of the next array field */\n  u16 array_index = 0;\n\n  /* read fields */\n  for (int i = 0; i < n_fields; i++) {\n    u16 field = fields[i];\n\n    /* unpack the field */\n    bool is_arr = field >> 15;\n    u16 ftype16 = (field >> 12) & 0x7;\n    u16 field_id = field & 0x0FFF;\n\n    /* sanity checks */\n    if (is_arr) {\n      if (array_index >= n_arrays)\n        throw std::runtime_error(\"descriptor has more than n_arrays fields marked as arrays\");\n      if ((arrays[array_index] == 0) || (arrays[array_index] > 4096))\n        throw std::runtime_error(\"array size out of bounds\");\n    }\n\n    /* read the field */\n    res.fields[i].field_id = field_id;\n    switch (ftype16) {\n    case 0:\n      res.fields[i].ftype = Field::ftype_t::INT8;\n      break;\n    case 1:\n      res.fields[i].ftype = Field::ftype_t::INT16;\n      break;\n    case 2:\n      res.fields[i].ftype = Field::ftype_t::INT32;\n      break;\n    case 3:\n      res.fields[i].ftype = Field::ftype_t::INT64;\n      break;\n    case 4:\n      res.fields[i].ftype = Field::ftype_t::VAR;\n      break;\n    case 5:\n      res.fields[i].ftype = Field::ftype_t::INT128;\n      break;\n    default:\n      throw std::runtime_error(\"unknown ftype\");\n    }\n    res.fields[i].n_elems = (is_arr ? arrays[array_index++] : 1);\n  }\n\n  return res;\n}\n\nvoid DescriptorReader::compute_positions(Descriptor &descriptor, bool packed_strings)\n{\n  /* is this message dynamically sized? */\n  u16 n_var_fields = 0;\n  for (auto &field : descriptor.fields)\n    if (field.ftype == Field::ftype_t::VAR)\n      n_var_fields++;\n\n  /* note: if packed_strings == false, then dynamic_size is false. */\n  descriptor.n_var_fields = n_var_fields;\n  descriptor.dynamic_size = packed_strings && (n_var_fields > 0);\n  u16 pos = (descriptor.dynamic_size) ? 4 : 2;\n\n  /* n_var_seen counts the number of VAR fields iterated through, so\n   * we can ignore the last VAR field (in favor of the total length field)\n   */\n  int n_var_seen = 0;\n\n  for (auto &field : descriptor.fields) {\n    u16 size = field.size(packed_strings);\n    u16 align = ((field.ftype == Field::ftype_t::VAR) ? 2 : size);\n\n    if (packed_strings && (field.ftype == Field::ftype_t::VAR)) {\n      n_var_seen++;\n      if (n_var_seen == n_var_fields) {\n        // don't position the last dynamic field\n        field.pos = 0;\n        continue;\n      }\n    }\n\n    /* align for the value according to alignement */\n    pos = (pos + align - 1) & (~(align - 1));\n\n    /* set the position in the Field struct */\n    field.pos = pos;\n\n    /* update the next position after this field. */\n    pos += size * field.n_elems;\n  }\n\n  descriptor.size = pos;\n}\n\n} /* namespace jitbuf */\n"
  },
  {
    "path": "jitbuf/descriptor_reader.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <jitbuf/descriptor.h>\n#include <platform/types.h>\n\nnamespace jitbuf {\n\n/*\n * Jitbuf message descriptor:\n *\n * All fields are 2-bytes aligned to 2 bytes.\n *\n * HEADER:\n * +-----------+------------+------------+------------+\n * |   FLAGS   |   RPC_ID   |  N_FIELDS  |  N_ARRAYS  |\n * +-----------+------------+------------+------------+\n *\n * BODY:\n * +------------+---------+---------------------+\n * |  FIELD[0]  |   ...   | FIELD[N_FIELDS - 1] |\n * +------------+---------+---------------------+\n * +------------+---------+---------------------+\n * |   ARR[0]   |   ...   |   ARR[N_ARRAYS-1]   |\n * +------------+---------+---------------------+\n *\n * Where:\n * FLAGS is 0\n * RPC_ID is the described message's rpc id\n * N_FIELDS counts the number of fields encoded in the message\n * N_ARRAYS is the number of array fields specified\n *\n * FIELD:\n * 16       15             12                                  0\n *  +--------+----+----+----+----------------------------------+\n *  | IS_ARR |    FTYPE     |             FIELD ID             |\n *  +--------+----+----+----+----------------------------------+\n *\n * IS_ARR: 1 if the field is an array, so is specified in the ARR part\n * FTYPE: specifies the size type of field of the following values:\n *   0: 1 bytes (u8 or s8)\n *   1: 2 bytes (u16 or s16)\n *   2: 4 bytes (u32 or s32)\n *   3: 8 bytes (u64 or s64)\n *   4: variable length string.\n *        When strings are packed, length is specified with a u16, except the\n *          last string, whose length is deduced from the message's total length\n *        When strings are unpacked, this is represented by a struct jb_blob\n *          (see jb.h)\n * FIELD_ID: the field ID of the field in the message specification\n *\n * ARR: number of elements in the array. Currently required 4096 > ARR > 0.\n */\n\nclass DescriptorReader {\npublic:\n  /**\n   * Read descriptor buffer, return Descriptor.\n   *\n   * @assumes buffer is 16-bit aligned in memory\n   * @throws on error.\n   */\n  static Descriptor read(u8 *buffer, u16 len);\n\n  /**\n   * Computes the positions of message fields\n   *\n   * @param packed_strings: true if only string lengths are\n   */\n  static void compute_positions(Descriptor &descriptor, bool packed_strings);\n};\n\n} /* namespace jitbuf */\n"
  },
  {
    "path": "jitbuf/fixed_handler.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <jitbuf/fixed_handler.h>\n\n#include <platform/platform.h>\n\ntypedef uint32_t u32;\ntypedef uint16_t u16;\n\n#define EMPTY_RPC_ID ((u32)-1)\n\nint jbf_init(\n    struct jitbuf_fixed *client, struct jitbuf_handler *handler_buf, uint32_t n_handlers, uint32_t (*hash_fn)(uint32_t rpc_id))\n{\n  uint32_t i;\n\n  if ((client == NULL) || (handler_buf == NULL) || (hash_fn == NULL))\n    return -EINVAL;\n\n  /* initialize the buffer */\n  for (i = 0; i < n_handlers; i++)\n    handler_buf[i].rpc_id = EMPTY_RPC_ID;\n\n  /* initialize the client struct */\n  client->handlers = handler_buf;\n  client->n_handlers = n_handlers;\n  client->hash_fn = hash_fn;\n\n  return 0;\n}\n\nint jbf_add(struct jitbuf_fixed *client, const struct jb_rpc *rpc, jbc_handler_func f, void *context)\n{\n  struct jitbuf_handler *h_elem;\n  uint32_t slot;\n\n  if ((client == NULL) || (rpc == NULL))\n    return -EINVAL;\n  if (rpc->rpc_id == EMPTY_RPC_ID)\n    return -EINVAL;\n  if ((rpc->descriptor == NULL) || (rpc->descriptor->size == 0))\n    return -EINVAL;\n  if (f == NULL)\n    return -EINVAL;\n\n  /* find the table element this handler should be in */\n  slot = client->hash_fn(rpc->rpc_id);\n  assert(slot < client->n_handlers);\n\n  h_elem = &client->handlers[slot];\n  if (h_elem->rpc_id != EMPTY_RPC_ID)\n    return -ENOSPC;\n\n  /* initialize fields in the table location */\n  h_elem->rpc_id = rpc->rpc_id;\n  h_elem->f = f;\n  h_elem->context = context;\n  h_elem->descriptor = rpc->descriptor->buf;\n  h_elem->descriptor_len = rpc->descriptor->size;\n  h_elem->buffer_len = rpc->size;\n\n  return 0;\n}\n\nint jbf_make_descriptor(struct jitbuf_fixed *client, char *buf, int len)\n{\n  int i = 0;\n  int used = 0;\n\n  for (i = 0; i < client->n_handlers; i++) {\n    int desc_len = client->handlers[i].descriptor_len;\n\n    if (client->handlers[i].rpc_id == EMPTY_RPC_ID)\n      continue;\n\n    if (client->handlers[i].descriptor_len > len)\n      return -ENOSPC;\n\n    memcpy(buf, client->handlers[i].descriptor, desc_len);\n    buf += desc_len;\n    len -= desc_len;\n    used += desc_len;\n  }\n\n  return used;\n}\n\nint jbf_handle(struct jitbuf_fixed *client, char *buf, int len)\n{\n  u32 rpc_id;\n  uint32_t slot;\n  struct jitbuf_handler *h_elem;\n\n  /* if there's not even enough space for the rpc_id, fail. */\n  if (len < 2)\n    return -EINVAL;\n\n  /* find the table entry for the buffer's rpc_id */\n  rpc_id = *(u16 *)buf;\n  slot = client->hash_fn(rpc_id);\n  assert(slot < client->n_handlers);\n\n  h_elem = &client->handlers[slot];\n\n  /* does the table contain a handler for this rpc_id? */\n  if (h_elem->rpc_id != rpc_id)\n    return -ENOENT;\n\n  /* is the buffer too short to contain the handled struct? */\n  if (h_elem->buffer_len > len)\n    return -EINVAL;\n\n  /* okay, can run the handler */\n  h_elem->f(h_elem->context, buf);\n\n  /* return the number of bytes the struct consumed */\n  return h_elem->buffer_len;\n}\n"
  },
  {
    "path": "jitbuf/fixed_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_FIXED_HANDLER_H_\n#define INCLUDE_JITBUF_FIXED_HANDLER_H_\n\n#include <jitbuf/jb.h>\n#include <platform/platform.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\ntypedef void (*jbc_handler_func)(void *context, void *buf);\n\nstruct jitbuf_handler {\n  uint32_t rpc_id;\n  jbc_handler_func f;\n  void *context;\n  const char *descriptor;\n  int descriptor_len;\n  int buffer_len;\n};\n\nstruct jitbuf_fixed {\n  struct jitbuf_handler *handlers;\n  uint32_t n_handlers;\n  uint32_t (*hash_fn)(uint32_t rpc_id);\n};\n\n/**\n * Initializes the jitbuf client\n *\n * @param client: the client to initialize\n * @param handler_buf: an array of struct jitbuf_handlers for storing handlers\n * @param n_handlers: the number of handlers in handler_buf\n * @param hash_fn: a hash function that maps rpc ID to [0,n_handlers)\n *\n * @returns -EINVAL if pointers are NULL or 0 on success.\n */\nint jbf_init(\n    struct jitbuf_fixed *client, struct jitbuf_handler *handler_buf, uint32_t n_handlers, uint32_t (*hash_fn)(uint32_t rpc_id));\n\n/**\n * Adds an RPC handler to the client\n *\n * @param client: the client to add to\n * @param rpc: the RPC descriptor (from *.jb.h) that describes the RPC.\n *   must remain alive while the jitbuf_client is alive.\n * @param f: the handler function to call\n * @param context: the context to pass to the handler\n *\n * @returns -EINVAL if pointers are NULL or sanity check of handler fields fails\n *   -ENOSPC if hashing the added RPC ID yields an occupied slot, 0 on success.\n *\n * @note rpc_id == (u32)-1 is reserved.\n */\nint jbf_add(struct jitbuf_fixed *client, const struct jb_rpc *rpc, jbc_handler_func f, void *context);\n\n/**\n * Encodes an aggregate descriptor holding all descriptors for handled rpc_ids\n *\n * @param client: the client to create a descriptor for\n * @param buf: where to write the output descriptor\n * @param len: the size of buf\n *\n * @returns -ENOSPC if buf is too short, the length of the descriptor on success\n */\nint jbf_make_descriptor(struct jitbuf_fixed *client, char *buf, int len);\n\n/**\n * Handles an incoming buffer.\n *\n * @param client: the client holding the different handlers\n * @param buf: the buffer containing an entry to be handled\n * @param len: the length of the buffer. It could be larger than the entry.\n *\n * @returns: -EINVAL if len is too small for the rpc_id,\n *   -ENOENT if buffer's rpc_id has not been added to the client\n *   or the number of consumed bytes on success.\n */\nint jbf_handle(struct jitbuf_fixed *client, char *buf, int len);\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_JITBUF_FIXED_HANDLER_H_ */\n"
  },
  {
    "path": "jitbuf/handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <jitbuf/handler.h>\n\n#include <jitbuf/transformer.h>\n#include <sstream>\n#include <string>\n\njitbuf::Handler::Handler(bool fail_unadded, bool fail_unknown)\n    : fail_unadded_(fail_unadded), fail_unknown_(fail_unknown), remote_timestamp_(0)\n{}\n\nvoid jitbuf::Handler::add(\n    std::shared_ptr<jitbuf::TransformRecord> &transform, handler_func_t handler_func, std::shared_ptr<Service> service)\n{\n  struct HandlerRecord record;\n  record.transform = *transform;\n  record.handler_func = handler_func;\n  record.priv = service.get();\n  record.transform_ptr = transform;\n  record.service_ptr = service;\n\n  auto res = handlers_.insert({transform->msg_rpc_id, record});\n  if (res.second != true)\n    throw std::runtime_error(\"rpc_id already exists\");\n}\n\nvoid jitbuf::Handler::add(std::shared_ptr<jitbuf::TransformRecord> &transform, handler_func_t handler_func, void *priv)\n{\n  struct HandlerRecord record;\n  record.transform = *transform;\n  record.handler_func = handler_func;\n  record.priv = priv;\n  record.transform_ptr = transform;\n\n  auto res = handlers_.insert({transform->msg_rpc_id, record});\n  if (res.second != true)\n    throw std::runtime_error(\"rpc_id already exists\");\n}\n\nvoid jitbuf::Handler::add_identity(TransformBuilder &builder, std::shared_ptr<jitbuf::Service> service)\n{\n  const handler_package &package(service->get_package());\n  for (int i = 0; i < package.size; i++) {\n    const handler_descriptor *hdesc = &package.descriptors[i];\n    const jb_descriptor *jbdesc = hdesc->descriptor;\n\n    /* get a string from the descriptor */\n    std::string descriptor_str(jbdesc->buf, jbdesc->size);\n\n    /* add the service */\n    auto xform = builder.get_xform(descriptor_str);\n    add(xform, hdesc->handler_func, service);\n  }\n}\n\nvoid jitbuf::Handler::add_identity(TransformBuilder &builder, jitbuf::Service *service)\n{\n  const handler_package &package(service->get_package());\n  for (int i = 0; i < package.size; i++) {\n    const handler_descriptor *hdesc = &package.descriptors[i];\n    const jb_descriptor *jbdesc = hdesc->descriptor;\n\n    /* get a string from the descriptor */\n    std::string descriptor_str(jbdesc->buf, jbdesc->size);\n\n    /* add the service */\n    auto xform = builder.get_xform(descriptor_str);\n    add(xform, hdesc->handler_func, service);\n  }\n}\n\nint jitbuf::Handler::handle(const char *msg, uint32_t len, u64 flags)\n{\n  int processed = 0;\n\n  /* Handle timestamps */\n  if (flags & HFLAG_TIMESTAMPED) {\n    if (len < sizeof(u64))\n      return -EINVAL;\n\n    remote_timestamp_ = *(u64 *)msg;\n    msg += sizeof(u64);\n    len -= sizeof(u64);\n    processed += sizeof(u64);\n  }\n\n  /* get RPC ID */\n  if (len < 2)\n    return -EINVAL;\n\n  uint16_t rpc_id = *(uint16_t *)msg;\n\n  /* find handler for RPC ID */\n  auto iter = handlers_.find(rpc_id);\n  if (iter == handlers_.end()) {\n    if (fail_unadded_) {\n      std::ostringstream oss;\n      oss << \"message rpc_id=\" << rpc_id << \" not in handler hash\";\n      throw std::runtime_error(oss.str());\n    } else {\n      return -EINVAL;\n    }\n  }\n\n  /* safety check message size */\n  HandlerRecord &record = iter->second;\n  if (len < record.transform.size)\n    return -EINVAL;\n\n  /* invoke handler */\n  uint16_t size = record.handler_func(msg, record.transform.xform, record.priv);\n\n  /* if we didn't get all the dynamic sized part, punt */\n  if (size > len)\n    return -EINVAL;\n\n  return size + processed;\n}\n\nint jitbuf::Handler::handle(const std::string &msg, u64 flags)\n{\n  return handle(msg.data(), msg.length(), flags);\n}\n\njitbuf::Transform jitbuf::Handler::get_transform(int rpc_id)\n{\n  return handlers_.at(rpc_id).transform.xform;\n}\n\nint jitbuf::Handler::handle_multiple(const char *msg, u64 len, u64 flags)\n{\n  u64 processed = 0;\n  u64 remaining = len;\n  int ret = 0;\n\n  while (len > processed) {\n    int ret = handle(msg + processed, (remaining > ((u32)-1) ? ((u32)-1) : remaining), flags);\n    if (ret < 0)\n      break;\n\n    /* sanity check, should not happen */\n    if ((ret + processed > len) || ((u64)(ret + processed) < processed))\n      throw std::runtime_error(\"Possible overflow in handle_multiple\");\n\n    processed += ret;\n    remaining -= ret;\n  }\n\n  if (processed > 0)\n    return processed;\n\n  /* error, return code */\n  return ret;\n}\n\nint jitbuf::Handler::handle_multiple(const std::string &msg, u64 flags)\n{\n  return handle_multiple(msg.data(), msg.length(), flags);\n}\n"
  },
  {
    "path": "jitbuf/handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_HANDLER_H_\n#define INCLUDE_JITBUF_HANDLER_H_\n\n#include <jitbuf/service.h>\n#include <jitbuf/transform_builder.h>\n#include <unordered_map>\n\nnamespace jitbuf {\n\n/**\n * A record for handling of messages\n * @transform: details of transforming the source RPC format to our format\n * @handler_func: a handler for out format messages\n * @priv: parameter to the handler func\n * @transform_ptr: keep the shared_ptr for accurate reference counting\n * @service_ptr: if applicable, keep the shared_ptr for the given service\n */\nstruct HandlerRecord {\n  struct TransformRecord transform;\n  handler_func_t handler_func;\n  void *priv;\n  std::shared_ptr<jitbuf::TransformRecord> transform_ptr;\n  std::shared_ptr<jitbuf::Service> service_ptr;\n};\n\n/* Flags for message handling: */\n/*   There is a timestamp before the message */\n#define HFLAG_TIMESTAMPED 0x1\n\n/* message handler: */\nclass Handler {\npublic:\n  /**\n   * c'tor\n   * @param fail_unadded: should handle() fail on rpc_ids that were not added\n   * @param fail_unknown: should handle() fail on rpc_ids that are not in the\n   * .proto\n   */\n  Handler(bool fail_unadded, bool fail_unknown);\n\n  /**\n   * Add handling of new message\n   * @param transform: the details of JIT transform from source RPC format\n   * @param handler_func: the function to handle messages\n   * @param service: a Service class to implement the service\n   */\n  void add(std::shared_ptr<jitbuf::TransformRecord> &transform, handler_func_t handler_func, std::shared_ptr<Service> service);\n\n  /**\n   * Add handling of new message\n   * @param transform: the details of JIT transform from source RPC format\n   * @param handler_func: the function to handle messages\n   * @param priv: parameter to the function\n   */\n  void add(std::shared_ptr<jitbuf::TransformRecord> &transform, handler_func_t handler_func, void *priv);\n\n  /**\n   * Add identity handling of all messages in package\n   * @param builder: a TransformBuilder to generate transforms\n   * @param service: the service to add identity handlers for\n   */\n  void add_identity(TransformBuilder &builder, std::shared_ptr<Service> service);\n\n  /**\n   * Add identity handling of all messages in package. No smart-pointer variant\n   *\n   * @param builder: a TransformBuilder to generate transforms\n   * @param service: the service to add identity handlers for\n   */\n  void add_identity(TransformBuilder &builder, Service *service);\n\n  /**\n   * handle a message\n   * @returns: the message length on success, -EINVAL on error (or throws)\n   */\n  int handle(const char *msg, uint32_t len, u64 flags = 0);\n  int handle(const std::string &msg, u64 flags = 0);\n\n  /**\n   * Handles multiple consecutive messages\n   * @returns: the length of successfully consumed messages if at least one\n   *           message was processed, otherwise returns -EINVAL (or throws)\n   */\n  int handle_multiple(const char *msg, u64 len, u64 flags = 0);\n  int handle_multiple(const std::string &msg, u64 flags = 0);\n\n  /* get the most recently known remote timestamp */\n  inline u64 remote_timestamp() { return remote_timestamp_; }\n\n  /* accessors to individual transforms */\n  jitbuf::Transform get_transform(int rpc_id);\n\nprivate:\n  bool fail_unadded_;\n  bool fail_unknown_;\n\n  u64 remote_timestamp_;\n\n  std::unordered_map<uint32_t, HandlerRecord> handlers_;\n};\n\n} /* namespace jitbuf */\n\n#endif /* INCLUDE_JITBUF_HANDLER_H_ */\n"
  },
  {
    "path": "jitbuf/jb.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_JB_H_\n#define INCLUDE_JITBUF_JB_H_\n\n#ifdef __cplusplus\n#include <array>\n#include <limits>\n#include <string>\n#include <string_view>\n\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct jb_descriptor {\n  int size;\n  const char *buf;\n};\n\nstruct jb_package {\n  const struct jb_descriptor **descriptors;\n  const struct jb_descriptor **ext_descriptors;\n  int size;\n};\n\n/**\n * RPC description\n * @rpc_id: the RPC ID\n * @size: the length of an encoded message\n * @descriptor: the short descriptor\n * @ext_descriptor: the extended descriptor\n */\nstruct jb_rpc {\n  unsigned int rpc_id;\n  int size;\n  const struct jb_descriptor *descriptor;\n  const struct jb_descriptor *ext_descriptor;\n};\n\n/**\n * Information about a blob (chunk of bytes) adjacent to a jb message\n */\nstruct jb_blob {\n  const char *buf;\n  unsigned short len;\n\n#ifdef __cplusplus\n  constexpr jb_blob() : buf(nullptr), len(0) {}\n\n  constexpr jb_blob(const char *buf, unsigned short len) : buf(buf), len(len) {}\n\n  explicit constexpr jb_blob(std::string_view from) : jb_blob(from.data(), static_cast<unsigned short>(from.size()))\n  {\n    assert(from.size() <= std::numeric_limits<unsigned short>::max());\n  }\n\n  explicit jb_blob(std::string const &from) : jb_blob(from.data(), static_cast<unsigned short>(from.size()))\n  {\n    assert(from.size() <= std::numeric_limits<unsigned short>::max());\n  }\n\n  std::string to_string() const { return std::string(buf, len); }\n\n  constexpr std::string_view string_view() const { return {buf, len}; }\n\n  constexpr operator std::string_view() const { return string_view(); }\n\n  constexpr bool operator==(std::string_view rhs) const { return string_view() == rhs; }\n\n  constexpr bool operator!=(std::string_view rhs) const { return string_view() != rhs; }\n\n  jb_blob &operator=(std::string_view from)\n  {\n    assert(from.size() <= std::numeric_limits<unsigned short>::max());\n    buf = from.data();\n    len = from.size();\n    return *this;\n  }\n\n  constexpr char const *data() const { return buf; }\n  constexpr unsigned short size() const { return len; }\n  constexpr bool empty() const { return !len; }\n\n#endif /* __cplusplus */\n};\n\n#ifdef __cplusplus\n// fmt v10+ customization point: allow formatting jb_blob without a formatter\n// by exposing it as a std::string_view via ADL.\ninline std::string_view format_as(jb_blob const &blob)\n{\n  return std::string_view(blob.buf, blob.len);\n}\n#endif\n\n#ifdef __cplusplus\n}\n\ntemplate <std::size_t Size> constexpr jb_blob to_jb_blob(std::uint8_t const (&data)[Size])\n{\n  static_assert(Size <= std::numeric_limits<unsigned short>::max());\n  auto const length = strnlen(reinterpret_cast<char const *>(data), Size / sizeof(*data));\n  return {reinterpret_cast<char const *>(data), static_cast<unsigned short>(length)};\n}\n\ntemplate <std::size_t Size> constexpr jb_blob to_jb_blob(std::array<std::uint8_t, Size> const &data)\n{\n  static_assert(Size <= std::numeric_limits<unsigned short>::max());\n  auto const length = strnlen(reinterpret_cast<char const *>(data.data()), Size);\n  return {reinterpret_cast<char const *>(data.data()), static_cast<unsigned short>(length)};\n}\n\ntemplate <typename Out> Out &operator<<(Out &&out, jb_blob const &blob)\n{\n  out << std::string_view(blob.buf, blob.len);\n  return out;\n}\n\ninline std::string &assign_jb(std::string &lhs, jb_blob const &rhs)\n{\n  lhs.assign(rhs.buf, rhs.len);\n  return lhs;\n}\n\ntemplate <std::size_t Size> std::string &assign_render_array(std::string &lhs, std::uint8_t const (&rhs)[Size])\n{\n  lhs.assign(reinterpret_cast<char const *>(rhs), strnlen(reinterpret_cast<char const *>(rhs), Size / sizeof(*rhs)));\n  return lhs;\n}\n\ntemplate <std::size_t Size> std::string render_array_to_string(std::uint8_t const (&data)[Size])\n{\n  return {reinterpret_cast<char const *>(data), strnlen(reinterpret_cast<char const *>(data), Size / sizeof(*data))};\n}\n\ntemplate <std::size_t Size> std::string_view render_array_to_string_view(std::uint8_t const (&data)[Size])\n{\n  return {reinterpret_cast<char const *>(data), strnlen(reinterpret_cast<char const *>(data), Size / sizeof(*data))};\n}\n\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_JITBUF_JB_H_ */\n"
  },
  {
    "path": "jitbuf/perfect_hash.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <cstddef>\n#include <platform/platform.h>\n#include <tuple>\n#include <type_traits>\n#include <util/iterable_bitmap.h>\n\ntemplate <class T, std::size_t HASH_SIZE, typename HASH_FN, class Allocator = std::allocator<T>> class PerfectHash {\npublic:\n  using key_type = u32;\n  using index_type = u32;\n  using value_type = std::pair<const key_type, T>;\n\n  PerfectHash() : elem_count_(0) {}\n\n  ~PerfectHash()\n  {\n    for (auto i : allocated_) {\n      destroy(i);\n    }\n  }\n\n  T *find(key_type k)\n  {\n    index_type index = hash_fn(k);\n\n    if (!allocated_.get(index))\n      return nullptr;\n\n    if (((value_type *)&values_[index])->first != k)\n      return nullptr;\n\n    return &((value_type *)&values_[index])->second;\n  }\n\n  template <typename... Args> T *insert(key_type k, Args &&... args)\n  {\n    index_type index = hash_fn(k);\n\n    /* is the entry present? */\n    if (allocated_.get(index))\n      return nullptr;\n\n    /* construct the object, might throw! */\n    traits_::construct(\n        allocator_,\n        (value_type *)&values_[index],\n        std::piecewise_construct,\n        std::forward_as_tuple(std::forward<key_type>(k)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n\n    /* okay, we're good! */\n    elem_count_++;\n    allocated_.set(index);\n\n    return &((value_type *)&values_[index])->second;\n  }\n\n  bool erase(key_type k)\n  {\n    index_type index = hash_fn(k);\n\n    /* is the entry missing? */\n    if (!allocated_.get(index))\n      return false;\n\n    /* does the key match? */\n    if (((value_type *)&values_[index])->first != k)\n      return false;\n\n    /* ok, can free */\n    destroy(index);\n    elem_count_--;\n    allocated_.clear(index);\n    return true;\n  }\n\nprivate:\n  using traits_ = typename std::allocator_traits<Allocator>::template rebind_traits<value_type>;\n  using allocator_type = typename traits_::allocator_type;\n\n  /**\n   * Destroys the element at @index.\n   */\n  void destroy(index_type index) { traits_::destroy(allocator_, &values_[index]); }\n\n  HASH_FN hash_fn;\n\n  allocator_type allocator_;\n\n  std::array<typename std::aligned_storage<sizeof(value_type), alignof(value_type)>::type, HASH_SIZE> values_;\n\n  IterableBitmap<HASH_SIZE> allocated_;\n\n  u32 elem_count_;\n};\n"
  },
  {
    "path": "jitbuf/service.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_SERVICE_H_\n#define INCLUDE_JITBUF_SERVICE_H_\n\n#include <jitbuf/jb.h>\n#include <jitbuf/transformer.h>\n\nnamespace jitbuf {\n\ntypedef uint16_t (*handler_func_t)(const char *msg, Transform &xform, void *priv);\n\n/**\n * Information required to construct a single message handler\n */\nstruct handler_descriptor {\n  const struct jb_descriptor *descriptor;\n  handler_func_t handler_func;\n};\n\n/**\n * Information required to construct all of a package's handlers\n */\nstruct handler_package {\n  const struct handler_descriptor *descriptors;\n  int size;\n};\n\nclass Service {\npublic:\n  virtual ~Service() {}\n  virtual const handler_package get_package() { throw std::runtime_error(\"unimplemented\"); }\n};\n\n} // namespace jitbuf\n\n#endif /* INCLUDE_JITBUF_SERVICE_H_ */\n"
  },
  {
    "path": "jitbuf/transform_builder.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <jitbuf/transform_builder.h>\n\n#include <jitbuf/descriptor_reader.h>\n#include <sstream>\n#include <vector>\n\nnamespace jitbuf {\n\nTransformBuilder::TransformBuilder(llvm::LLVMContext &context) : xformer_(context) {}\n\nvoid TransformBuilder::add_descriptor(std::shared_ptr<Descriptor> descriptor)\n{\n  u32 struct_rpc_id = descriptor->rpc_id;\n\n  auto res = descriptors_.insert({struct_rpc_id, descriptor});\n  if (res.second != true) {\n    std::stringstream msg;\n    msg << \"rpc_id \" << struct_rpc_id << \" already exists\";\n    throw std::runtime_error(msg.str());\n  }\n\n  return;\n}\n\nvoid jitbuf::TransformBuilder::add_descriptor(const std::string &descriptor)\n{\n  std::shared_ptr<Descriptor> desc(new Descriptor(DescriptorReader::read((u8 *)descriptor.data(), descriptor.size())));\n\n  DescriptorReader::compute_positions(*desc, false);\n\n  add_descriptor(desc);\n}\n\nstd::shared_ptr<TransformRecord> TransformBuilder::get_xform(const std::string &from)\n{\n  /* first try the cache */\n  auto iter = cache_.find(from);\n  if (iter != cache_.end()) {\n    /* found! try to get a strong pointer from the weak pointer */\n    auto sptr = iter->second.lock();\n    if (sptr)\n      return sptr;\n\n    /* buffer has been freed, will fall through and make a new one */\n    cache_.erase(iter);\n  }\n\n  /* De-serialize the descriptor */\n  Descriptor jb_desc(DescriptorReader::read((u8 *)from.data(), from.size()));\n\n  DescriptorReader::compute_positions(jb_desc, true);\n\n  u32 struct_rpc_id = jb_desc.rpc_id;\n\n  /* find the struct handler for the rpc_id. might throw std::out_of_bounds */\n  auto to_ptr = descriptors_.at(struct_rpc_id);\n\n  /* make a MessageHandler */\n  std::shared_ptr<TransformRecord> buf(get_xform_to(jb_desc, *to_ptr));\n\n  /* put in the cache */\n  cache_.insert({from, buf});\n\n  return buf;\n}\n\nstd::shared_ptr<TransformRecord> TransformBuilder::get_xform_to(const Descriptor &from, const Descriptor &to)\n{\n  std::vector<u32> src_pos;\n  std::vector<u32> dst_pos;\n  std::vector<u32> sizes;\n  uint32_t min_size = 0;\n  u32 len_pos = 0xffffffff;\n  const u32 src_size = from.size;\n  std::vector<BlobDetails> blobs;\n\n  /** regular fields */\n  /* how many fields does each message type contain? */\n  int from_fields = from.fields.size();\n  int to_fields = to.fields.size();\n  int max_fields = std::min(from_fields, to_fields);\n\n  /* populate a map [id] -> [pos] of the non-string @from fields */\n  std::map<u32, u32> from_map;\n  for (auto &field : from.fields)\n    if (field.ftype != Field::ftype_t::VAR)\n      from_map[field.field_id] = field.pos;\n\n  /* reserve to avoid unnecessary re-allocations */\n  src_pos.reserve(max_fields);\n  dst_pos.reserve(max_fields);\n  sizes.reserve(max_fields);\n\n  /* for each @to field, try to match with a @from field */\n  for (auto &to_field : to.fields) {\n    /* this pass: only non-VAR fields */\n    if (to_field.ftype == Field::ftype_t::VAR)\n      continue;\n\n    u32 field_id = to_field.field_id;\n    auto from_field_it = from_map.find(field_id);\n    if (from_field_it == from_map.end())\n      continue; /* no field in @from */\n\n    src_pos.push_back(from_field_it->second);\n    dst_pos.push_back(to_field.pos);\n    u32 field_size = to_field.size(false) * to_field.n_elems;\n    sizes.push_back(field_size);\n    min_size = std::max(min_size, from_field_it->second + field_size);\n  }\n\n  /* sanity checks */\n  if (min_size > src_size)\n    throw std::runtime_error(\"descriptor fields must be smaller than incoming total size\");\n\n  /** VAR fields */\n  int from_blobs = from.n_var_fields;\n  int to_blobs = to.n_var_fields;\n\n  if (from_blobs > 0)\n    len_pos = 2;\n\n  if ((from_blobs > 0) && (to_blobs > 0)) {\n\n    /* populate a map [id] -> [pos] of the @to blobs */\n    std::map<u32, u32> to_map;\n    for (auto &to_field : to.fields)\n      if (to_field.ftype == Field::ftype_t::VAR)\n        to_map[to_field.field_id] = to_field.pos;\n\n    /* reserve space to avoid re-allocation */\n    blobs.reserve(from_blobs);\n\n    /* we'll remember the last should_write = true */\n    int last_should_write = -1;\n\n    /* for each @from blob, add a BlobDetails */\n    int i = 0;\n    for (auto &from_field : from.fields) {\n      if (from_field.ftype != Field::ftype_t::VAR)\n        continue;\n\n      BlobDetails blob;\n\n      /* fill in the from pos */\n      if (i == from.n_var_fields - 1) {\n        /* last blob does not have src; its length is the remainder */\n        blob.length_is_remainder = true;\n      } else {\n        blob.src_pos = from_field.pos;\n        blob.length_is_remainder = false;\n      }\n\n      /* try to find a matching to blob */\n      u32 field_id = from_field.field_id;\n      auto to_field_it = to_map.find(field_id);\n      if (to_field_it == to_map.end()) {\n        /* no field in @to */\n        blob.should_write = false;\n      } else {\n        blob.dst_pos = to_field_it->second;\n        blob.should_write = true;\n        last_should_write = i;\n\n        /* leverage the field copy code to copy the len if we're\n         * not the from field */\n        if (!blob.length_is_remainder) {\n          src_pos.push_back(blob.src_pos);\n          dst_pos.push_back(blob.dst_pos + sizeof(char *));\n          sizes.push_back(sizeof(uint16_t));\n        }\n      }\n\n      blobs.push_back(blob);\n      i++;\n    }\n\n    /* we've pushed back a blob for each from blob, but if at some point\n     * there are no more @to blocks we need to write to, why bother going\n     * through all those from blobs? In this case we can throw away the last\n     * few blobs.\n     */\n    blobs.resize(last_should_write + 1); /* 0 if no to blobs matched */\n  }\n\n  /* jit-compile the transformation */\n  std::shared_ptr<TransformRecord> buf(new TransformRecord);\n  buf->msg_rpc_id = to.rpc_id;\n  buf->xform = xformer_.get_xform(src_pos.data(), dst_pos.data(), sizes.data(), sizes.size(), len_pos, src_size, blobs);\n  buf->size = src_size;\n  buf->min_buffer_size = min_size;\n\n  return buf;\n}\n\n} /* namespace jitbuf */\n"
  },
  {
    "path": "jitbuf/transform_builder.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_TRANSFORM_BUILDER_H_\n#define INCLUDE_JITBUF_TRANSFORM_BUILDER_H_\n\n#include <jitbuf/descriptor.h>\n#include <jitbuf/jb.h>\n#include <jitbuf/transformer.h>\n#include <memory>\n#include <stdint.h>\n#include <unordered_map>\n\nnamespace llvm {\nclass LLVMContext;\n}\n\nnamespace jitbuf {\n\n/**\n * Handling incoming buffers including their mapping into known structs\n * @param min_buffer_size: the minimum required buffer size to be able to decode\n */\nstruct TransformRecord {\n  uint32_t msg_rpc_id;\n  Transform xform;\n  uint32_t size;\n  uint32_t min_buffer_size;\n};\n\n/**\n * The TransformBuilder creates functions that translate from the remote's\n *   message format to the local message format.\n *\n * The TransformBuilder is:\n *   1. Initialized with the local known message formats in the form of\n *      - descriptors -- via add_descriptor()\n *      - packages -- via add_package()\n *   2. Then queried for the functions via get_xform(), given the serialized\n *     representation of the remote's message format.\n *\n *  The included Transformer (xformer_) handles the LLVM-related parts of\n *    creating the functions.\n */\nclass TransformBuilder {\npublic:\n  TransformBuilder(llvm::LLVMContext &context);\n\n  /**\n   * Adds a message descriptor.\n   */\n  void add_descriptor(std::shared_ptr<Descriptor> descriptor);\n\n  /**\n   * Adds a message descriptor.\n   */\n  void add_descriptor(const std::string &descriptor);\n\n  /**\n   * Gets a buffer handler, given the serialized JitbufDescriptor.\n   */\n  std::shared_ptr<TransformRecord> get_xform(const std::string &from);\n\n  /**\n   * Returns a function that transforms between two jitbuf descriptors, and\n   *   the minimum incoming message for safe operation\n   * @param from: the descriptor of the source message\n   * @param to: the descriptor of the destination message\n   */\n  std::shared_ptr<TransformRecord> get_xform_to(const Descriptor &from, const Descriptor &to);\n\nprivate:\n  Transformer xformer_;\n\n  std::unordered_map<uint32_t, std::shared_ptr<Descriptor>> descriptors_;\n  std::unordered_map<std::string, std::weak_ptr<TransformRecord>> cache_;\n};\n\n} /* namespace jitbuf */\n\n#endif /* INCLUDE_JITBUF_TRANSFORM_BUILDER_H_ */\n"
  },
  {
    "path": "jitbuf/transformer.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <jitbuf/transformer.h>\n\n#include \"llvm/ADT/STLExtras.h\"\n#include \"llvm/Analysis/BasicAliasAnalysis.h\"\n#include \"llvm/Analysis/Passes.h\"\n#include \"llvm/ExecutionEngine/MCJIT.h\"\n#include \"llvm/IR/LegacyPassManager.h\"\n#include \"llvm/IR/Verifier.h\"\n#include \"llvm/Support/TargetSelect.h\"\n#include \"llvm/Transforms/InstCombine/InstCombine.h\"\n#include \"llvm/Transforms/Scalar.h\"\n#include \"llvm/Transforms/Scalar/GVN.h\"\n#include <cctype>\n#include <cstdio>\n#include <map>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#define SHOULD_DUMP_MODULES_AFTER_JIT 0\n\nnamespace jitbuf {\n\nvoid initialize_llvm()\n{\n  bool res;\n  res = llvm::InitializeNativeTarget();\n  if (res)\n    throw std::runtime_error(\"llvm::InitializeNativeTarget() failed\");\n\n  res = llvm::InitializeNativeTargetAsmPrinter();\n  if (res)\n    throw std::runtime_error(\"llvm::InitializeNativeTargetAsmPrinter() failed\");\n\n  res = llvm::InitializeNativeTargetAsmParser();\n  if (res)\n    throw std::runtime_error(\"llvm::InitializeNativeTargetAsmParser() failed\");\n}\n\nTransformer::Transformer(llvm::LLVMContext &context) : unique_counter_(0), context_(context), builder_(context) {}\n\nTransform Transformer::get_xform(\n    u32 *src_pos, u32 *dst_pos, u32 *sizes, u32 n_elem, u32 len_pos, u32 src_size, std::vector<BlobDetails> &blobs)\n{\n  llvm::IntegerType *U16T = llvm::Type::getInt16Ty(context_);\n  llvm::IntegerType *U32T = llvm::Type::getInt32Ty(context_);\n  llvm::PointerType *S8PT = llvm::Type::getInt1PtrTy(context_);\n\n  llvm::Function *memcpy_func = NULL;\n\n  /* arguments are two char[] */\n  std::vector<llvm::Type *> args(2, llvm::Type::getInt8PtrTy(context_, 0));\n\n  /* returns u16 */\n  llvm::FunctionType *func_type = llvm::FunctionType::get(U16T, args, false);\n  llvm::Value *ret_value = NULL;\n\n  /* get a module as a container */\n  llvm::Module *module = get_new_module();\n\n  /* create prototype */\n  std::string func_name = generate_unique_name(\"xform_\");\n  llvm::Function *F = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, func_name, module);\n\n  /* set names and get Value* for function parameters */\n  llvm::Function::arg_iterator AI = F->arg_begin();\n  AI->setName(\"src\");\n  llvm::Value *src_buf_ptr = &(*(AI++));\n  AI->setName(\"dst\");\n  llvm::Value *dst_buf_ptr = &(*(AI++));\n\n  // Create a new basic block to start insertion into.\n  llvm::BasicBlock *BB = llvm::BasicBlock::Create(context_, \"entry\", F);\n  builder_.SetInsertPoint(BB);\n\n  /* Add IR to copy fields from src to dst */\n  for (u32 i = 0; i < n_elem; i++) {\n    /* get constant for source position */\n    llvm::Value *src_pos_val = llvm::ConstantInt::get(U32T, src_pos[i], false);\n    /* get constant for destination position */\n    llvm::Value *dst_pos_val = llvm::ConstantInt::get(U32T, dst_pos[i], false);\n\n    /* Pointer to source variable */\n    llvm::Value *src_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), src_buf_ptr, src_pos_val, \"src_charp\");\n    /* Pointer to dst variable */\n    llvm::Value *dst_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), dst_buf_ptr, dst_pos_val, \"dst_charp\");\n\n    /* decide which type the value is */\n    llvm::Type *copied_type;\n    switch (sizes[i]) {\n    case 1:\n      copied_type = llvm::Type::getInt8Ty(context_);\n      break;\n    case 2:\n      copied_type = llvm::Type::getInt16Ty(context_);\n      break;\n    case 4:\n      copied_type = llvm::Type::getInt32Ty(context_);\n      break;\n    case 8:\n      copied_type = llvm::Type::getInt64Ty(context_);\n      break;\n    case 16:\n      copied_type = llvm::Type::getInt128Ty(context_);\n      break;\n    default:\n      copied_type = NULL;\n      break;\n    }\n\n    u32 is_unaligned = (src_pos[i] | dst_pos[i]) & (sizes[i] - 1);\n\n    if ((copied_type != NULL) && (is_unaligned == 0)) {\n      /* cast pointers to correct type */\n      llvm::Value *src_ptr = builder_.CreatePointerCast(src_char_ptr, copied_type->getPointerTo(), \"src_ptr\");\n      llvm::Value *dst_ptr = builder_.CreatePointerCast(dst_char_ptr, copied_type->getPointerTo(), \"dst_ptr\");\n\n      /* Load the source */\n      llvm::LoadInst *loaded = builder_.CreateLoad(copied_type, src_ptr, \"ld\");\n      /* Save to destination */\n      llvm::StoreInst *stored = builder_.CreateStore(loaded, dst_ptr);\n      (void)stored; /* unused, only need the side effect */\n    } else {\n      /* pointers are unaligned or don't have a suitable register size */\n      /* get memcpy */\n\n      if (memcpy_func == NULL)\n        memcpy_func = get_memcpy(module);\n\n      /* get constant for size */\n      llvm::Value *size_val = llvm::ConstantInt::get(U32T, sizes[i], true);\n\n      std::vector<llvm::Value *> memcpy_args;\n      memcpy_args.push_back(dst_char_ptr);\n      memcpy_args.push_back(src_char_ptr);\n      memcpy_args.push_back(size_val);\n\n      builder_.CreateCall(memcpy_func, memcpy_args, \"memcpy_ret\");\n    }\n  }\n\n  /* Translate blobs */\n  if (blobs.size() > 0) {\n    /* our initial offset is the message size */\n    llvm::Value *offset = llvm::ConstantInt::get(U16T, src_size, false);\n\n    for (auto &blob : blobs) {\n      if (blob.should_write) {\n        /* get constant for destination position */\n        llvm::Value *dst_pos_val = llvm::ConstantInt::get(U32T, blob.dst_pos, false);\n\n        /* Char pointer to dst variable */\n        llvm::Value *dst_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), dst_buf_ptr, dst_pos_val, \"offset_charp\");\n\n        /* cast pointer to correct type */\n        llvm::Value *dst_ptr = builder_.CreatePointerCast(dst_char_ptr, S8PT->getPointerTo(), \"buf_ptr\");\n\n        /* compute &src[offset] */\n        llvm::Value *src_blob_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), src_buf_ptr, offset, \"src_blob_ptr\");\n\n        /* Save to destination */\n        llvm::StoreInst *stored = builder_.CreateStore(src_blob_ptr, dst_ptr);\n        (void)stored; /* unused, only need the side effect */\n      }\n\n      /* are we writing the last @from field? */\n      if (blob.should_write && blob.length_is_remainder) {\n        /* get constant for source position */\n        llvm::Value *src_pos_val = llvm::ConstantInt::get(U32T, len_pos, false);\n\n        /* Pointer to source variable */\n        llvm::Value *src_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), src_buf_ptr, src_pos_val, \"total_len_charp\");\n\n        /* cast pointers to correct type */\n        llvm::Value *src_ptr = builder_.CreatePointerCast(src_char_ptr, U16T->getPointerTo(), \"total_len_ptr\");\n\n        /* load */\n        llvm::LoadInst *loaded = builder_.CreateLoad(U16T, src_ptr, \"loaded_total\");\n\n        /* save this for return from the generated function */\n        ret_value = loaded;\n\n        /* get constant for destination position */\n        llvm::Value *dst_pos_val = llvm::ConstantInt::get(U32T, blob.dst_pos + sizeof(uint16_t), false);\n\n        /* Char pointer to dst variable */\n        llvm::Value *dst_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), dst_buf_ptr, dst_pos_val, \"last_len_charp\");\n\n        /* cast pointer to correct type */\n        llvm::Value *dst_ptr = builder_.CreatePointerCast(dst_char_ptr, U16T->getPointerTo(), \"last_len_ptr\");\n\n        /* compute total - offset */\n        llvm::Value *remaining_val = builder_.CreateSub(loaded, offset, \"remaining_len\");\n\n        /* Save to destination */\n        llvm::StoreInst *stored = builder_.CreateStore(remaining_val, dst_ptr);\n        (void)stored; /* unused, only need the side effect */\n      }\n\n      /* update the offset */\n      if (!blob.length_is_remainder) {\n        /* get constant for source position */\n        llvm::Value *src_pos_val = llvm::ConstantInt::get(U32T, blob.src_pos, false);\n\n        /* Pointer to source variable */\n        llvm::Value *src_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), src_buf_ptr, src_pos_val, \"field_len_charp\");\n\n        /* cast pointers to correct type */\n        llvm::Value *src_ptr = builder_.CreatePointerCast(src_char_ptr, U16T->getPointerTo(), \"field_len_ptr\");\n\n        /* load */\n        llvm::LoadInst *loaded = builder_.CreateLoad(U16T, src_ptr, \"field_len\");\n\n        /* update offset */\n        offset = builder_.CreateAdd(offset, loaded, \"cur_offset\");\n      }\n    }\n  }\n\n  // Decide on return value if we haven't already\n  if (ret_value == NULL) {\n    if (len_pos <= src_size - sizeof(uint16_t)) {\n      /* dynamic length -- duplicated code from blob handling :/ */\n      /* get constant for source position */\n      llvm::Value *src_pos_val = llvm::ConstantInt::get(U32T, len_pos, false);\n\n      /* Pointer to source variable */\n      llvm::Value *src_char_ptr = builder_.CreateGEP(llvm::Type::getInt8Ty(context_), src_buf_ptr, src_pos_val, \"total_len_charp\");\n\n      /* cast pointers to correct type */\n      llvm::Value *src_ptr = builder_.CreatePointerCast(src_char_ptr, U16T->getPointerTo(), \"total_len_ptr\");\n\n      /* load */\n      llvm::LoadInst *loaded = builder_.CreateLoad(U16T, src_ptr, \"loaded_total\");\n\n      /* we'll return that loaded variable */\n      ret_value = loaded;\n    } else {\n      /* our initial offset is the message size */\n      ret_value = llvm::ConstantInt::get(U16T, src_size, false);\n    }\n  }\n\n  // Finish off the function.\n  builder_.CreateRet(ret_value);\n\n  // Validate the generated code, checking for consistency.\n  verifyFunction(*F);\n\n  /* For debugging */\n  if (SHOULD_DUMP_MODULES_AFTER_JIT)\n    module->dump();\n\n  return jit_function(module, F);\n}\n\nstd::string Transformer::generate_unique_name(const char *prefix)\n{\n  std::stringstream st;\n  st << prefix << unique_counter_++;\n  return st.str();\n}\n\nllvm::Module *Transformer::get_new_module()\n{\n  // Otherwise create a new Module.\n  std::string module_name = generate_unique_name(\"mcjit_module_\");\n  llvm::Module *m = new llvm::Module(module_name, context_);\n  return m;\n}\n\nllvm::Function *Transformer::get_memcpy(llvm::Module *module)\n{\n  /* create arguments */\n  std::vector<llvm::Type *> args(2, llvm::Type::getInt8PtrTy(context_, 0));\n  args.push_back(llvm::Type::getInt32Ty(context_));\n\n  /* return type */\n  llvm::Type *return_type = llvm::Type::getInt8PtrTy(context_, 0);\n\n  /* function type */\n  llvm::FunctionType *func_type = llvm::FunctionType::get(return_type, args, false);\n\n  /* function */\n  llvm::Function *memcpy_func = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, \"memcpy\", module);\n\n  if (memcpy_func == NULL)\n    throw std::runtime_error(\"could not create memcpy func\");\n\n  return memcpy_func;\n}\n\nTransform Transformer::jit_function(llvm::Module *module, llvm::Function *function)\n{\n  /* Make a new ExecutionEngine */\n  std::string error_str;\n  std::shared_ptr<llvm::ExecutionEngine> engine(llvm::EngineBuilder(std::unique_ptr<llvm::Module>(module))\n                                                    .setErrorStr(&error_str)\n                                                    .setEngineKind(llvm::EngineKind::JIT)\n                                                    .create());\n  if (!engine) {\n    std::stringstream st;\n    st << \"Could not create ExecutionEngine: \" << error_str;\n    throw std::runtime_error(st.str());\n  }\n\n  // Create a function pass manager for this engine\n  auto *pass_manager = new llvm::legacy::FunctionPassManager(module);\n\n  // Set up the optimizer pipeline.  Start with registering info about how the\n  // target lays out data structures.\n  module->setDataLayout(engine->getDataLayout());\n  // Provide basic AliasAnalysis support for GVN.\n  pass_manager->add(llvm::createBasicAAWrapperPass());\n  // Do simple \"peephole\" optimizations and bit-twiddling optzns.\n  pass_manager->add(llvm::createInstructionCombiningPass());\n  // Reassociate expressions.\n  pass_manager->add(llvm::createReassociatePass());\n  // Eliminate Common SubExpressions.\n  pass_manager->add(llvm::createGVNPass());\n  // Simplify the control flow graph (deleting unreachable blocks, etc).\n  pass_manager->add(llvm::createCFGSimplificationPass());\n  pass_manager->doInitialization();\n\n  // Run the pass manager on this function\n  pass_manager->run(*function);\n\n  // We don't need this anymore\n  delete pass_manager;\n\n  module = NULL;\n  engine->finalizeObject();\n  return Transform((transform)engine->getPointerToFunction(function), engine);\n}\n\n} /* namespace jitbuf */\n"
  },
  {
    "path": "jitbuf/transformer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_JITBUF_TRANSFORMER_H_\n#define INCLUDE_JITBUF_TRANSFORMER_H_\n\n#include \"llvm/ExecutionEngine/ExecutionEngine.h\"\n#include \"llvm/IR/IRBuilder.h\"\n#include \"llvm/IR/LLVMContext.h\"\n#include \"llvm/IR/Module.h\"\n#include <memory>\n#include <stdint.h>\n\nnamespace jitbuf {\n\ntypedef uint32_t u32;\ntypedef uint64_t u64;\ntypedef uint16_t (*transform)(const char *src, char *dst);\n\nclass Transform {\npublic:\n  Transform() : xform_(NULL) {}\n\n  Transform(transform xform, std::shared_ptr<llvm::ExecutionEngine> engine) : xform_(xform), engine_(engine) {}\n\n  inline bool empty() { return (xform_ == NULL); }\n\n  inline uint16_t operator()(const char *src, char *dst) { return xform_(src, dst); }\n\n  inline bool operator==(const Transform &other) { return xform_ == other.xform_; }\n\n  /**\n   * gets the function pointer. note its lifetime can only be trusted as a\n   *   subset of this Transform's lifetime.\n   */\n  inline transform get() { return xform_; }\n\nprivate:\n  transform xform_;\n  std::shared_ptr<llvm::ExecutionEngine> engine_;\n};\n\n/**\n * Initializes global LLVM structures.\n * @throws exception on error\n */\nvoid initialize_llvm();\n\nstruct BlobDetails {\n  /* offset to the length of the blob, in the source message */\n  u32 src_pos;\n\n  /* offset to the struct jb_blob in the destination message */\n  u32 dst_pos;\n\n  /* true if dst_len_pos is valid and should be populated, false otherwise */\n  bool should_write;\n\n  /* the length of this field is all the way from offset to the\n   *   src_msg->_len */\n  bool length_is_remainder;\n};\n\nclass Transformer {\npublic:\n  Transformer(llvm::LLVMContext &context);\n\n  /**\n   * Returns a function that transforms buffers\n   * @param src_pos: the positions of variables in the source array\n   * @param dst_pos: the positions of variables in the destination array\n   * @param sizes: variable sizes, must be in {1,2,4,8}\n   * @param n_elem: the number of elements to transform\n   * @param len_pos: location of the u16 total message length in src\n   * @param src_size: size of the constant part of the source message\n   * @param blobs: collection of BlobDetails for recipt of decoding blobs\n   *\n   * The generated function returns the length of the message. If\n   *   len_pos < src_size or there is a length_is_remainder==true blob, it\n   *   returns *(u16*)&src_msg[len_pos]. Otherwise, it returns src_size.\n   */\n  Transform\n  get_xform(u32 *src_pos, u32 *dst_pos, u32 *sizes, u32 n_elem, u32 len_pos, u32 src_size, std::vector<BlobDetails> &blobs);\n\nprivate:\n  /**\n   * generates a unique name from the prefix\n   */\n  std::string generate_unique_name(const char *prefix);\n\n  /**\n   * Returns a new module for JIT\n   */\n  llvm::Module *get_new_module();\n\n  /**\n   * Returns a memcpy function\n   */\n  llvm::Function *get_memcpy(llvm::Module *module);\n\n  /**\n   * JIT compiles the function in the given module, returns a pointer to it\n   */\n  Transform jit_function(llvm::Module *module, llvm::Function *function);\n\n  u64 unique_counter_; /* counter for generating names */\n  llvm::LLVMContext &context_;\n  llvm::IRBuilder<> builder_;\n};\n\n} /* namespace jitbuf */\n\n#endif /* INCLUDE_JITBUF_TRANSFORMER_H_ */\n"
  },
  {
    "path": "platform/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_unit_test(types)\n"
  },
  {
    "path": "platform/bitops.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_PLATFORM_BITOPS_H_\n#define INCLUDE_FASTPASS_PLATFORM_BITOPS_H_\n\n/**\n * Loops i over all the indices of 1 bits in mask.\n * @note: changes mask, it will be zero at the end of the loop.\n */\n#define mask_foreach(i, mask) for (i = __builtin_ctzll(mask); mask; mask &= (mask - 1), i = __builtin_ctzll(mask))\n\n/**\n * Sets bit 'index' of mask 'mask' to 1 if 'cond' != 0\n * @assumes cond, when cast to signed 64 bit, is non-negative.\n */\n#define set_bit_if_nz(mask, index, cond)                                                                                       \\\n  do {                                                                                                                         \\\n    mask |= (((u64)(-(s64)(cond))) >> 63) << (index);                                                                          \\\n  } while (0)\n\n/**\n * returns 1 if (a < b), 0 otherwise\n */\n#define nz_if_lt(a, b) ((u64)((s64)(a) - (b))) >> 63\n\n#endif /* INCLUDE_FASTPASS_PLATFORM_BITOPS_H_ */\n"
  },
  {
    "path": "platform/debug.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * debug.h\n */\n\n#ifndef FASTPASS_DEBUG_H_\n#define FASTPASS_DEBUG_H_\n\n/* FASTPASS_PR_DEBUG defined in platform.h */\n#if defined __KERNEL__\n#define FASTPASS_PR_DEBUG(enable, fmt, a...)                                                                                   \\\n  do {                                                                                                                         \\\n    if (enable)                                                                                                                \\\n      printk(KERN_DEBUG \"%s: \" fmt, __func__, ##a);                                                                            \\\n  } while (0)\n\n#elif defined RTE_ARCH\n#define FASTPASS_PR_DEBUG(enable, fmt, a...)                                                                                   \\\n  do {                                                                                                                         \\\n    if (enable)                                                                                                                \\\n      COMM_DEBUG(\"%s: \" fmt, __func__, ##a);                                                                                   \\\n  } while (0)\n\n#else\n#define FASTPASS_PR_DEBUG(enable, fmt, a...)                                                                                   \\\n  do {                                                                                                                         \\\n    if (enable)                                                                                                                \\\n      printf(\"%s: \" fmt, __func__, ##a);                                                                                       \\\n  } while (0)\n#endif\n\n/* BUILD_BUG_ON */\n#if defined __KERNEL__\n#include <linux/bug.h>\n#else\n#define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))\n#endif\n\n#ifdef CONFIG_IP_FASTPASS_DEBUG\nextern bool fastpass_debug;\n#define fp_debug(format, a...) FASTPASS_PR_DEBUG(1, format, ##a)\n#else\n#define fp_debug(format, a...)\n#endif\n\n#ifdef __KERNEL__\n/*\n * Warning and debugging macros, (originally taken from DCCP)\n */\n#define FASTPASS_WARN(fmt, a...) net_warn_ratelimited(\"%s: \" fmt, __func__, ##a)\n#define FASTPASS_CRIT(fmt, a...) printk(KERN_CRIT fmt \" at %s:%d/%s()\\n\", ##a, __FILE__, __LINE__, __func__)\n#define FASTPASS_BUG(a...)                                                                                                     \\\n  do {                                                                                                                         \\\n    FASTPASS_CRIT(\"BUG: \" a);                                                                                                  \\\n    dump_stack();                                                                                                              \\\n  } while (0)\n#define FASTPASS_BUG_ON(cond)                                                                                                  \\\n  do {                                                                                                                         \\\n    if (unlikely((cond) != 0))                                                                                                 \\\n      FASTPASS_BUG(\"\\\"%s\\\" holds (exception!)\", __stringify(cond));                                                            \\\n  } while (0)\n\n#define fp_info(format, a...) pr_info(\"%s: \" format, __func__, ##a)\n\n#ifndef NDEBUG\n#define assert(cond) BUG_ON(!(cond))\n#else\n#define assert(cond)                                                                                                           \\\n  do {                                                                                                                         \\\n  } while (0)\n#endif\n\n#else\n\n#include <assert.h>\n#include <execinfo.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n/* based on\n * http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes\n */\nstatic inline void fp_backtrace(void)\n{\n  void *array[50];\n  size_t size;\n\n  // get void*'s for all entries on the stack\n  size = backtrace(array, 50);\n\n  // print out all the frames to stderr\n  backtrace_symbols_fd(array, size, STDERR_FILENO);\n}\n\n#ifdef NO_DPDK\n#define FASTPASS_BUG_SHOULD_PANIC 1\n#else\n#define FASTPASS_BUG_SHOULD_PANIC 0\n#endif\n\nstatic inline void panic(void)\n{\n  exit(-1);\n}\n\n#define FASTPASS_CRIT(fmt, a...) printf(fmt \" at %s:%d/%s()\\n\", ##a, __FILE__, __LINE__, __func__)\n\n/** from linux's include/asm-generic/bug.h */\n#define FASTPASS_BUG()                                                                                                         \\\n  do {                                                                                                                         \\\n    FASTPASS_CRIT(\"BUG\");                                                                                                      \\\n    fp_backtrace();                                                                                                            \\\n    if (FASTPASS_BUG_SHOULD_PANIC)                                                                                             \\\n      panic();                                                                                                                 \\\n  } while (0)\n\n#define FASTPASS_BUG_ON(condition)                                                                                             \\\n  do {                                                                                                                         \\\n    if (unlikely(condition))                                                                                                   \\\n      FASTPASS_BUG();                                                                                                          \\\n  } while (0)\n\n#define fp_info(format, a...) printf(\"%s: \" format, __func__, ##a)\n\n#endif\n\n#endif /* FASTPASS_DEBUG_H_ */\n"
  },
  {
    "path": "platform/fastpass_linux.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_FASTPASS_LINUX_H_\n#define INCLUDE_FASTPASS_FASTPASS_LINUX_H_\n\n#include <linux/ioctl.h>\n\n#define FASTPASS_IOCTL_MAGIC 0xFA\n/* fastpass engine configuration */\n#define FASTPASS_IOW_TX_COST _IOW(FASTPASS_IOCTL_MAGIC, 0, u32)\n#define FASTPASS_IOW_MAX_CREDIT _IOW(FASTPASS_IOCTL_MAGIC, 1, u32)\n#define FASTPASS_IOW_NEW_DATA_TIMEOUT_NS _IOW(FASTPASS_IOCTL_MAGIC, 2, u32)\n#define FASTPASS_IOW_RX_TIMEOUT_NS _IOW(FASTPASS_IOCTL_MAGIC, 3, u32)\n#define FASTPASS_IOW_TX_TIMEOUT_NS _IOW(FASTPASS_IOCTL_MAGIC, 4, u32)\n#define FASTPASS_IOW_RESET_WINDOW_NS _IOW(FASTPASS_IOCTL_MAGIC, 5, u64)\n#define FASTPASS_IOW_IS_CLIENT _IOW(FASTPASS_IOCTL_MAGIC, 6, u8)\n#define FASTPASS_IOW_MAX_PAYLOAD_LEN _IOW(FASTPASS_IOCTL_MAGIC, 7, u32)\n\n#define TUNSERV_DEFAULT_IPPROTO 223\n#define PINGER_DEFAULT_IPPROTO 224\n#define FLOWTUNE_DEFAULT_IPPROTO 225\n#define SWITCH_PACE_DEFAULT_IPPROTO 226\n\n#endif /* INCLUDE_FASTPASS_FASTPASS_LINUX_H_ */\n"
  },
  {
    "path": "platform/generic.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * linux-compat.h\n */\n\n#ifndef FASTPASS_PLATFORM_GENERIC_H_\n#define FASTPASS_PLATFORM_GENERIC_H_\n\n#include <platform/types.h>\n\n#define fp_fprintf_nonz_prefix(f, var, description, prefix)                                                                    \\\n  {                                                                                                                            \\\n    if (var)                                                                                                                   \\\n      fp_fprintf(f, prefix \"%12llu  \" description \"\\n\", (unsigned long long)(var));                                            \\\n  }\n\n#define fp_fprintf_nonz(f, var, description) fp_fprintf_nonz_prefix(f, var, description, \"  \")\n\n#ifdef __KERNEL__\n\n/**\n * Works around the requirement for KBUILD_MODNAME to be defined when using\n *   pr_debug(). Some kernel header files use pr_debug(), and they fail in our\n *   \"infrastructure\" code, i.e., the files that are included from multiple\n *   modules (see scripts/Makefile.lib).\n */\n#if defined(CONFIG_DYNAMIC_DEBUG)\n#ifndef KBUILD_MODNAME\n#define KBUILD_MODNAME \"fastpass_common\"\n#endif /* #ifndef KBUILD_MODNAME */\n#endif /* defined(CONFIG_DYNAMIC_DEBUG) */\n\n#include <linux/errno.h>\n#include <linux/jhash.h>\n#include <linux/jiffies.h>\n#include <linux/types.h>\n#include <linux/vmalloc.h>\n#include <net/ip.h>\n\n#ifndef time_in_range64\n#define time_in_range64(a, b, c) (time_after_eq64(a, b) && time_before_eq64(a, c))\n#endif\n\ntypedef struct seq_file fp_outfile;\n#define fp_fprintf(f, ...) seq_printf(f, __VA_ARGS__)\n\n#define fp_vzalloc(size) vzalloc(size)\n#define fp_vfree(ptr) vfree(ptr)\n\n#else\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\ntypedef FILE fp_outfile;\n#define fp_fprintf(f, ...) fprintf(f, __VA_ARGS__)\n\n#define fp_vzalloc(size) calloc(1, size)\n#define fp_vfree(ptr) free(ptr)\n\n/* compiler-gcc.h */\n#ifdef __GNUC__\n#ifdef __aarch64__\n#define barrier() __asm__ __volatile__(\"\" : : : \"memory\")\n#define mb() asm volatile(\"dmb ish\" : : : \"memory\")\n#define rmb() asm volatile(\"dmb ishld\" : : : \"memory\")\n#define wmb() asm volatile(\"dmb ishst\" : : : \"memory\")\n#else // __aarch64__\n#define barrier() __asm__ __volatile__(\"\" : : : \"memory\")\n#define mb() asm volatile(\"mfence\" : : : \"memory\")\n#define rmb() asm volatile(\"lfence\" : : : \"memory\")\n#define wmb() asm volatile(\"sfence\" : : : \"memory\")\n#endif // ! __aarch64__\n#define read_barrier_depends()                                                                                                 \\\n  do {                                                                                                                         \\\n  } while (0)\n\n#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))\n#endif\n\n#define smp_wmb() mb()\n#define smp_mb() barrier()\n#define smp_rmb() barrier()\n#define smp_read_barrier_depends() read_barrier_depends()\n\n#ifdef RTE_ARCH\n#include <rte_byteorder.h>\n#else\n#include <arpa/inet.h>\n#endif\n\n#ifdef RTE_ARCH\n\n#include <rte_branch_prediction.h>\n\n#else\n#ifndef unlikely\n#define unlikely(x) (x)\n#endif\n\n#ifndef likely\n#define likely(x) (x)\n#endif\n#endif\n\n#ifdef RTE_ARCH\n#include <rte_common.h>\n#else\n#ifndef typeof\n#define typeof __typeof__\n#endif\n#endif\n\n#define CONFIG_64BIT\n\n/* from kernel.h */\n#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))\n\n/* this is a way to compute this without platform-dependent code */\n#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))\n\n/* from bitops.h */\n#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))\n#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)\n#define BITS_PER_BYTE 8\n#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))\n\n/* include/asm-generic/bitops/non-atomic.h */\nstatic inline void __set_bit(int nr, unsigned long *addr)\n{\n  unsigned long mask = BIT_MASK(nr);\n  unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);\n\n  *p |= mask;\n}\n\nstatic inline void __clear_bit(int nr, unsigned long *addr)\n{\n  unsigned long mask = BIT_MASK(nr);\n  unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);\n\n  *p &= ~mask;\n}\n\nstatic inline int test_bit(int nr, const volatile unsigned long *addr)\n{\n  return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG - 1)));\n}\n\n/* kernel.h */\n#define max_t(type, x, y)                                                                                                      \\\n  ({                                                                                                                           \\\n    type __max1 = (x);                                                                                                         \\\n    type __max2 = (y);                                                                                                         \\\n    __max1 > __max2 ? __max1 : __max2;                                                                                         \\\n  })\n\n/* typecheck.h */\n#define typecheck(type, x)                                                                                                     \\\n  ({                                                                                                                           \\\n    type __dummy;                                                                                                              \\\n    typeof(x) __dummy2;                                                                                                        \\\n    (void)(&__dummy == &__dummy2);                                                                                             \\\n    1;                                                                                                                         \\\n  })\n\n/* jiffies.h */\n#define time_after64(a, b) (typecheck(u64, a) && typecheck(u64, b) && ((s64)((b) - (a)) < 0))\n#define time_before64(a, b) time_after64(b, a)\n\n#define time_after_eq64(a, b) (typecheck(u64, a) && typecheck(u64, b) && ((s64)((a) - (b)) >= 0))\n#define time_before_eq64(a, b) time_after_eq64(b, a)\n\n#define time_in_range64(a, b, c) (time_after_eq64(a, b) && time_before_eq64(a, c))\n\n/* need __fls */\n#define __fls(x) (BITS_PER_LONG - 1 - __builtin_clzl(x))\n#define __ffs(x) (__builtin_ffsl(x) - 1)\n\n#define csum_partial fp_csum_partial\n#define csum_tcpudp_magic fp_csum_tcpudp_magic\n\n/* based on rte_hash_crc from DPDK's rte_hash_crc.h, but does checksum */\nstatic inline uint32_t fp_csum_partial(const void *data, uint32_t data_len, uint32_t init_val)\n{\n  unsigned i;\n  u64 sum = init_val;\n  const uint32_t *p32 = (const uint32_t *)data;\n  bool flip = false;\n\n  if (unlikely(data_len < 4))\n    goto do_last;\n\n  flip = (u64)p32 & 0x1;\n  if (unlikely(flip)) {\n    sum += *((const uint8_t *)p32) << 8;\n    p32 = (const uint32_t *)((const uint8_t *)p32 + 1);\n    data_len -= 1;\n  }\n\n  if ((u64)p32 & 0x2) {\n    sum += *((const uint16_t *)p32);\n    p32 = (const uint32_t *)((const uint8_t *)p32 + 2);\n    data_len -= 2;\n  }\n\n  for (i = 0; i < data_len / 4; i++) {\n    sum += *p32++;\n  }\n\ndo_last:\n  switch (3 - (data_len & 0x03)) {\n  case 0:\n    sum += *((const uint8_t *)p32 + 2) << 16;\n    /* Fallthrough */\n  case 1:\n    sum += *((const uint8_t *)p32 + 1) << 8;\n    /* Fallthrough */\n  case 2:\n    sum += *((const uint8_t *)p32);\n  default:\n    break;\n  }\n\n  if (unlikely(flip))\n    sum <<= 8; /* assume we didn't checksum so much data to overflow 56 bits */\n\n  sum = (u32)sum + (sum >> 32);       /* could have overflow on bit 32 */\n  return (u32)sum + (u32)(sum >> 32); /* add the overflow */\n}\n\nstatic inline uint16_t fp_fold(uint64_t sum64)\n{\n  uint32_t sum32;\n\n  /* now fold */\n  sum64 = (u32)sum64 + (sum64 >> 32);        /* could have overflow on bit 32 */\n  sum32 = sum64 + (sum64 >> 32);             /* add the overflow */\n  sum32 = (u16)sum32 + (sum32 >> 16);        /* could have overflow on bit 16 */\n  return ~((u16)sum32 + (u16)(sum32 >> 16)); /* add the overflow */\n}\n\nstatic inline uint16_t fp_csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, uint16_t len, uint16_t proto, uint32_t sum32)\n{\n  uint64_t sum64 = sum32;\n  sum64 += saddr + daddr + ((len + proto) << 8);\n\n  /* now fold */\n  return fp_fold(sum64);\n}\n\n#endif /* __KERNEL__ */\n\nstatic inline void __set_bit64(int nr, u64 *addr)\n{\n  u64 *p = (addr + (nr / 64));\n  *p |= (1ULL << (nr % 64));\n}\n\nstatic inline void __clear_bit64(int nr, u64 *addr)\n{\n  u64 *p = (addr + (nr / 64));\n  *p &= ~(1ULL << (nr % 64));\n}\n\n/* from Jenkin's public domain lookup3.c at\n * http://burtleburtle.net/bob/c/lookup3.c */\n#define jhash_3words fp_jhash_3words\n#define jhash_1word fp_jhash_1word\n\n#define fp_rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))\n#define fp_jhash_final(a, b, c)                                                                                                \\\n  {                                                                                                                            \\\n    c ^= b;                                                                                                                    \\\n    c -= fp_rot(b, 14);                                                                                                        \\\n    a ^= c;                                                                                                                    \\\n    a -= fp_rot(c, 11);                                                                                                        \\\n    b ^= a;                                                                                                                    \\\n    b -= fp_rot(a, 25);                                                                                                        \\\n    c ^= b;                                                                                                                    \\\n    c -= fp_rot(b, 16);                                                                                                        \\\n    a ^= c;                                                                                                                    \\\n    a -= fp_rot(c, 4);                                                                                                         \\\n    b ^= a;                                                                                                                    \\\n    b -= fp_rot(a, 14);                                                                                                        \\\n    c ^= b;                                                                                                                    \\\n    c -= fp_rot(b, 24);                                                                                                        \\\n  }\n\n/* compatibility with the kernel's jhash -- note this is only compatible\n * with _some_ linux versions */\nstatic inline u32 fp_jhash_nwords(u32 a, u32 b, u32 c, u32 initval)\n{\n  a += initval;\n  b += initval;\n  c += initval;\n  fp_jhash_final(a, b, c);\n  return c;\n}\nstatic inline u32 fp_jhash_3words(u32 a, u32 b, u32 c, u32 initval)\n{\n  return fp_jhash_nwords(a, b, c, initval + 0xDEADBEEF + (3 << 2));\n}\nstatic inline u32 fp_jhash_1word(u32 a, u32 initval)\n{\n  return fp_jhash_nwords(a, 0, 0, initval + 0xDEADBEEF + (1 << 2));\n}\n\n#endif /* FASTPASS_PLATFORM_GENERIC_H_ */\n"
  },
  {
    "path": "platform/linux-platform.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * linux-platform.h\n */\n\n#ifndef FASTPASS_LINUX_PLATFORM_H_\n#define FASTPASS_LINUX_PLATFORM_H_\n\nstatic inline u64 fp_get_time_ns(void)\n{\n  return ktime_to_ns(ktime_get_real());\n}\n\nstatic inline u64 fp_monotonic_time_ns(void)\n{\n  return ktime_to_ns(ktime_get());\n}\n\n#endif /* FASTPASS_LINUX_PLATFORM_H_ */\n"
  },
  {
    "path": "platform/memory.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_PLATFORM_MEMORY_H_\n#define INCLUDE_FASTPASS_PLATFORM_MEMORY_H_\n\n#if defined __KERNEL__\n\n#define fp_free(ptr) kfree(ptr)\n#define fp_calloc(typestr, num, size) kcalloc(num, size, GFP_KERNEL)\n#define fp_malloc(typestr, size) kmalloc(size, GFP_KERNEL)\n\n#elif defined RTE_ARCH\n\n#include <rte_malloc.h>\n\n#define fp_free(ptr) rte_free(ptr)\n#define fp_calloc(typestr, num, size) rte_calloc(typestr, num, size, 0)\n#define fp_malloc(typestr, size) rte_malloc(typestr, size, 0)\n\n#else\n\n#define fp_free(ptr) free(ptr)\n#define fp_calloc(typestr, num, size) calloc(num, size)\n#define fp_malloc(typestr, size) malloc(size)\n\n#endif\n\n#endif /* INCLUDE_FASTPASS_PLATFORM_MEMORY_H_ */\n"
  },
  {
    "path": "platform/no-dpdk.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * no-dpdk.h\n */\n\n#ifndef INCLUDE_FASTPASS_PLATFORM_NO_DPDK_H_\n#define INCLUDE_FASTPASS_PLATFORM_NO_DPDK_H_\n\n#define FASTPASS_PR_DEBUG(enable, fmt, a...)                                                                                   \\\n  do {                                                                                                                         \\\n    if (enable)                                                                                                                \\\n      printf(\"%s: \" fmt, __func__, ##a);                                                                                       \\\n  } while (0)\n\n#ifndef likely\n#define likely(x) __builtin_expect((x), 1)\n#endif /* likely */\n\n#ifndef unlikely\n#define unlikely(x) __builtin_expect((x), 0)\n#endif /* unlikely */\n\n#endif /* INCLUDE_FASTPASS_PLATFORM_NO_DPDK_H_ */\n"
  },
  {
    "path": "platform/platform.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * Platform-supplied API\n */\n\n#ifndef PROTOCOL_PLATFORM_H_\n#define PROTOCOL_PLATFORM_H_\n\n#include \"platform/bitops.h\"\n#include \"platform/debug.h\"\n#include \"platform/generic.h\"\n\n#ifdef __KERNEL__\n#include \"platform/linux-platform.h\"\n#else /* __KERNEL__ */\n#include \"platform/userspace-time.h\"\n#ifdef RTE_ARCH\n#include \"../src/arbiter/dpdk-platform.h\"\n#else /* #ifndef NO_DPDK */\n#include \"platform/no-dpdk.h\"\n#endif /* #ifndef NO_DPDK */\n#endif /* __KERNEL__ */\n\n/** FUNCTIONS IN PLATFORM.H **/\n\n/**\n * static inline u64 fp_get_time_ns(void)\n *\n * returns the current real time (the time that is used to determine timeslots)\n */\n\n#endif /* PROTOCOL_PLATFORM_H_ */\n"
  },
  {
    "path": "platform/spin_lock.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_PLATFORM_SPIN_LOCK_H_\n#define INCLUDE_FASTPASS_PLATFORM_SPIN_LOCK_H_\n\n#ifdef __KERNEL__\n\n#include <linux/spinlock.h>\n\n#define fp_spinlock_t spinlock_t\n#define fp_spin_init(lock)                                                                                                     \\\n  ({                                                                                                                           \\\n    spin_lock_init(lock);                                                                                                      \\\n    0;                                                                                                                         \\\n  })\n#define fp_spin_lock(lock)                                                                                                     \\\n  ({                                                                                                                           \\\n    spin_lock_bh(lock);                                                                                                        \\\n    0;                                                                                                                         \\\n  })\n#define fp_spin_unlock(lock) spin_unlock_bh(lock)\n#define fp_spin_destroy(lock)                                                                                                  \\\n  do {                                                                                                                         \\\n  } while (0)\n\n#else\n\n#include <pthread.h>\n\n#define fp_spinlock_t pthread_spinlock_t\n#define fp_spin_init(lock) pthread_spin_init(lock, PTHREAD_PROCESS_SHARED)\n#define fp_spin_lock(lock) pthread_spin_lock(lock)\n#define fp_spin_unlock(lock) pthread_spin_unlock(lock)\n#define fp_spin_destroy(lock) pthread_spin_destroy(lock)\n\n#endif\n\n#endif /* INCLUDE_FASTPASS_PLATFORM_SPIN_LOCK_H_ */\n"
  },
  {
    "path": "platform/types.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_PLATFORM_TYPES_H_\n#define INCLUDE_FASTPASS_PLATFORM_TYPES_H_\n\n#ifndef __KERNEL__\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#ifdef __APPLE__\n#include <sys/types.h>\n\ntypedef unsigned long long __u64;\ntypedef long long __s64;\ntypedef uint32_t __u32;\ntypedef uint32_t __be32;\ntypedef uint32_t __wsum;\ntypedef uint16_t __u16;\ntypedef uint16_t __be16;\ntypedef uint16_t __sum16;\n\n#else /* #ifdef __APPLE__ */\n#include <linux/types.h>\n#endif /* else #ifdef __APPLE__ */\n\ntypedef unsigned __int128 u128;\ntypedef __int128 s128;\ntypedef uint64_t u64;\ntypedef int64_t s64;\ntypedef uint32_t u32;\ntypedef int32_t s32;\ntypedef uint16_t u16;\ntypedef int16_t s16;\ntypedef uint8_t u8;\ntypedef int8_t s8;\n\n#endif /* #ifndef __KERNEL__ */\n\n#ifdef __cplusplus\n\n#include <chrono>\n\nusing namespace std::literals::chrono_literals;\n\n#include <iomanip>\n\nnamespace {\ntemplate <typename Out> void print_large_unsigned(Out &out, u128 value)\n{\n  constexpr u128 mask = 10000000000000ull;\n  auto const piece = static_cast<u64>(value % mask);\n  u128 const head = value / mask;\n  if (head) {\n    print_large_unsigned(out, head);\n    out << std::setfill('0') << std::setw(13) << piece;\n  } else {\n    out << piece;\n  }\n}\n} // namespace\n\ntemplate <typename Out, typename T, typename = std::enable_if_t<std::is_same_v<T, s128> || std::is_same_v<T, u128>>>\nOut &operator<<(Out &&out, T value)\n{\n  auto format_flags(out.flags());\n  if constexpr (std::is_same_v<T, u128>) {\n    print_large_unsigned(out, value);\n  } else {\n    if (value < 0) {\n      out << '-';\n      print_large_unsigned(out, static_cast<u128>(-value));\n    } else {\n      print_large_unsigned(out, static_cast<u128>(value));\n    }\n  }\n  out.flags(format_flags);\n  return out;\n}\n\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_FASTPASS_PLATFORM_TYPES_H_ */\n"
  },
  {
    "path": "platform/types_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/types.h>\n\n#include <gtest/gtest.h>\n\n#include <climits>\n\n#define TEST_PRINT_S128(Expected, Value)                                                                                       \\\n  do {                                                                                                                         \\\n    std::ostringstream ss;                                                                                                     \\\n    ss << static_cast<s128>(Value);                                                                                            \\\n    EXPECT_EQ(Expected, ss.str());                                                                                             \\\n  } while (false)\n\nTEST(types_test, print_s128)\n{\n  TEST_PRINT_S128(\"0\", 0);\n  TEST_PRINT_S128(\"1\", 1);\n  TEST_PRINT_S128(\"-1\", -1);\n  TEST_PRINT_S128(\"10\", 10);\n  TEST_PRINT_S128(\"-10\", -10);\n  TEST_PRINT_S128(\"255\", 255);\n  TEST_PRINT_S128(\"-255\", -255);\n  TEST_PRINT_S128(\n      \"170141183460469231731687303715884105727\",\n      static_cast<s128>(static_cast<u128>(~static_cast<u128>(0)) >> static_cast<u128>(1)));\n  TEST_PRINT_S128(\n      \"-170141183460469231731687303715884105728\", static_cast<s128>(static_cast<u128>(1) << static_cast<u128>(127)));\n}\n\n#define TEST_PRINT_U128(Expected, Value)                                                                                       \\\n  do {                                                                                                                         \\\n    std::ostringstream ss;                                                                                                     \\\n    ss << static_cast<u128>(Value);                                                                                            \\\n    EXPECT_EQ(Expected, ss.str());                                                                                             \\\n  } while (false)\n\nTEST(types_test, print_u128)\n{\n  TEST_PRINT_U128(\"0\", 0);\n  TEST_PRINT_U128(\"1\", 1);\n  TEST_PRINT_U128(\"10\", 10);\n  TEST_PRINT_U128(\"255\", 255);\n  TEST_PRINT_U128(\n      \"170141183460469231731687303715884105727\", (static_cast<u128>(~static_cast<u128>(0)) >> static_cast<u128>(1)));\n  TEST_PRINT_U128(\"340282366920938463463374607431768211455\", static_cast<u128>(~static_cast<u128>(0)));\n}\n"
  },
  {
    "path": "platform/userspace-time.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef FASTPASS_PLATFORM_USERSPACE_TIME_H_\n#define FASTPASS_PLATFORM_USERSPACE_TIME_H_\n\n#include <platform/generic.h>\n#include <platform/types.h>\n\n#include <time.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\nstatic inline __attribute__((always_inline)) u64 fp_get_time_ns(void)\n{\n  struct timespec tp;\n\n  if (unlikely(clock_gettime(CLOCK_REALTIME, &tp) != 0))\n    return -1;\n\n  return (1000 * 1000 * 1000) * (u64)tp.tv_sec + tp.tv_nsec;\n}\n\n/**\n * Get monotonic clock\n */\nstatic inline __attribute__((always_inline)) u64 monotonic()\n{\n  struct timespec ts;\n  int res = clock_gettime(CLOCK_MONOTONIC, &ts);\n  if (unlikely(res != 0))\n    return -1;\n\n  return (u64)ts.tv_sec * (1000 * 1000 * 1000) + ts.tv_nsec;\n}\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* FASTPASS_PLATFORM_USERSPACE_TIME_H_ */\n"
  },
  {
    "path": "reducer/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_subdirectory(util)\n\n# CXX bridge for Rust reducer crate (exposes config + run entrypoint)\nadd_rust_cxxbridge(\n  reducer_entrypoint_cxxbridge\n  ${CMAKE_SOURCE_DIR}/crates/reducer-sys/src/lib.rs\n)\n\n# CXX bridge for Rust AggregationCore (reducer crate side)\nadd_rust_cxxbridge(\n  reducer_aggregation_cxxbridge\n  ${CMAKE_SOURCE_DIR}/crates/reducer/src/ffi.rs\n)\n\n# Reducer library\n#\nadd_library(\n  reducerlib\n    entrypoint.cc\n    reducer.cc\n    reducer_config.cc\n    core.cc\n    worker.cc\n    uid_key.cc\n    rpc_stats.cc\n    ingest/ingest_core.cc\n    ingest/ingest_worker.cc\n    ingest/shared_state.cc\n    ingest/tcp_server.cc\n    ingest/agent_span.cc\n    ingest/socket_span.cc\n    ingest/udp_socket_span.cc\n    ingest/process_span.cc\n    ingest/cgroup_span.cc\n    ingest/k8s_pod_span.cc\n    ingest/flow_updater.cc\n    ingest/npm_connection.cc\n    ingest/aws_network_interface_span.cc\n    matching/matching_core.cc\n    matching/flow_span.cc\n    matching/aws_enrichment_span.cc\n    matching/k8s_pod_span.cc\n    matching/k8s_container_span.cc\n    aggregation/agg_core.cc\n    logging/logging_core.cc\n    logging/logger_span.cc\n    logging/core_stats_span.cc\n    logging/agg_core_stats_span.cc\n    logging/ingest_core_stats_span.cc\n)\ntarget_link_libraries(\n  reducerlib\n    reducer_entrypoint_cxxbridge\n    reducer_aggregation_cxxbridge\n    signal_handler\n    metrics_output\n    render_pipeline\n    tcp_channel\n    buffered_writer\n    blob_collector\n    index_dumper\n    scheduling\n    libuv-interface\n    element_queue_writer\n    fastpass_util\n    error_handling\n    time_tracker\n    json\n    lz4_decompressor\n    geoip_wrapper\n    absl::flat_hash_map\n    absl::flat_hash_set\n    absl::node_hash_map\n    absl::str_format\n    absl::strings\n    absl::synchronization\n    absl::time\n    yaml-cpp\n    tdigest\n    ip_address\n    file_ops\n    args_parser\n    uv_helpers\n    system_ops\n    thread_ops\n    error_handling\n    environment_variables\n    virtual_clock\n    cgroup_parser\n    versions\n    render_rust_ebpf_net\n)\nadd_dependencies(\n  reducerlib\n    render_compile_ebpf_net\n)\n\n# Library containing code responsible for publishing metrics (e.g. to a TSDB).\n#\nadd_library(\n  metrics_output\n    tsdb_formatter.cc\n    prometheus_formatter.cc\n    json_formatter.cc\n    otlp_grpc_formatter.cc\n    otlp_grpc_publisher.cc\n    null_publisher.cc\n    prometheus_handler.cc\n    prometheus_publisher.cc\n    disabled_metrics.cc\n    metric_info.cc\n    stat_info.cc\n    $<TARGET_OBJECTS:civetweb>\n)\ntarget_link_libraries(\n  metrics_output\n    element_queue_writer\n    error_handling\n    civetweb-interface\n    yaml-cpp\n    time\n    otlp_export_cxxbridge\n)\nadd_dependencies(\n  metrics_output\n    render_compile_ebpf_net\n    otlp_export_cxxbridge\n)\n\n# CXX bridge for Rust otlp_export crate\nadd_rust_cxxbridge(\n  otlp_export_cxxbridge\n  ${CMAKE_SOURCE_DIR}/crates/otlp_export/src/lib.rs\n)\n\n# Shell scripts\n#\nlint_shell_script_bundle(\n  reducer-scripts\n  SOURCES\n    entrypoint.sh\n    health_check.sh\n)\n\n# Rust binary integration\ninclude(rust_main)\nset(REDUCER_LINK_LIBRARIES\n    reducerlib\n    metrics_output\n    libuv-static\n    spdlog\n)\nadd_rust_main(\n  TARGET           reducer\n  STRIPPED_TARGET  reducer-stripped\n  PACKAGE          reducer-bin\n  BIN_NAME         reducer\n  LINK_LIBS        ${REDUCER_LINK_LIBRARIES}\n)\n\nadd_dependencies(pipeline reducer-stripped)\n\n# Docker image\n#\nif(\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n  build_custom_docker_image(\n    reducer\n    OUT_DIR srv\n    OUTPUT_OF\n      reducer-scripts\n      reducer-stripped\n    BINARIES\n      debug-info.conf\n    DEPENDENCY_OF\n      pipeline\n    ARGS\n      BUILD_TYPE=\"Debug\"\n  )\nelse()\n  build_custom_docker_image(\n    reducer\n    OUT_DIR srv\n    OUTPUT_OF\n      reducer-scripts\n      reducer-stripped\n    BINARIES\n      debug-info.conf\n    DEPENDENCY_OF\n      pipeline\n  )\nendif()\n\n# Unit Tests\n# otlp_grpc_formatter test removed due to Rust-backed exporter path\nadd_unit_test(rpc_queue_matrix LIBS fastpass_util element_queue_writer)\nadd_unit_test(disabled_metrics LIBS metrics_output)\n\n# Disable the reducer_test. It doesn't link because of Rust dependencies -- fixable\n# but doesn't seem worth the effort. The CI e2e test runs the reducer with a\n# kernel-collector and otelcol, whereas the reducer_test just runs a reducer with\n# a synthetic collector (and no kernel-collector)\n# add_unit_test(reducer LIBS reducerlib libuv-static static-executable spdlog)\n"
  },
  {
    "path": "reducer/Dockerfile",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nFROM docker.io/bitnami/minideb:trixie@sha256:0766a3b76750541ae2c5f3b806e5201e1b2ca1549a0a036a057726dc9e5dfa4d\n\nLABEL org.label-schema.name=\"opentelemetry-ebpf-reducer\"\nLABEL org.label-schema.description=\"OpenTelemetry eBPF reducer\"\nLABEL org.label-schema.vcs-url=\"https://github.com/open-telemetry/opentelemetry-ebpf\"\nLABEL org.label-schema.schema-version=\"1.0\"\n\n# required by entrypoint.sh and health_check.sh scripts\nRUN install_packages bc coreutils netcat-openbsd sed   libcurlpp0 libgrpc++1.51\n\nARG BUILD_TYPE\nRUN if [ \"$BUILD_TYPE\" = \"Debug\" ]; then \\\n      install_packages cgdb curl gawk gdb gzip iputils-ping jq procps ripgrep tar vim valgrind; \\\n    fi\n\n# Inbound metrics on 8000 (currently via stunnel); prometheus scrapes 7000, 7001, 7010\nEXPOSE 7000 7001 7010 8000\n\nENV EBPF_NET_INSTALL_DIR=/srv\nENV EBPF_NET_DATA_DIR=/var/run/ebpf_net\n\nENTRYPOINT [\"/srv/entrypoint.sh\"]\nCMD [\"--port\", \"8000\", \"--prom\", \"0.0.0.0:7010\"]\n\nCOPY srv /srv\nRUN mv /srv/reducer-stripped /srv/opentelemetry-ebpf-reducer\n"
  },
  {
    "path": "reducer/aggregation/agg_core.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include \"agg_core.h\"\n#include <reducer/constants.h>\n#include <reducer/otlp_grpc_formatter.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <common/constants.h>\n\n#include <platform/userspace-time.h>\n#include <util/log.h>\n#include <util/time.h>\n\n#include <stdexcept>\n\n#include <reducer/util/thread_ops.h>\n\nnamespace reducer::aggregation {\n\nbool AggCore::node_ip_field_disabled_ = false;\n\nvoid AggCore::set_node_ip_field_disabled(bool disabled)\n{\n  node_ip_field_disabled_ = disabled;\n}\n\nbool AggCore::id_id_enabled_ = false;\nbool AggCore::az_id_enabled_ = false;\nbool AggCore::flow_logs_enabled_ = false;\n\nvoid AggCore::set_id_id_enabled(bool enabled)\n{\n  id_id_enabled_ = enabled;\n}\n\nvoid AggCore::set_az_id_enabled(bool enabled)\n{\n  az_id_enabled_ = enabled;\n}\n\nvoid AggCore::set_flow_logs_enabled(bool enabled)\n{\n  flow_logs_enabled_ = enabled;\n}\n\nAggCore::AggCore(\n    RpcQueueMatrix &matching_to_aggregation_queues,\n    RpcQueueMatrix &aggregation_to_logging_queues,\n    size_t shard_num,\n    u64 initial_timestamp,\n    std::string otlp_endpoint,\n    bool disable_node_ip_field)\n    : CoreBase(\n          \"aggregation\",\n          shard_num,\n          initial_timestamp,\n          aggregation_to_logging_queues.make_writers<ebpf_net::logging::Writer>(\n              shard_num, std::bind(&Core::current_timestamp, this))),\n      rust_core_([&] {\n        auto readers = matching_to_aggregation_queues.make_readers(shard_num);\n        std::vector<reducer_agg::EqView> eqs;\n        eqs.reserve(readers.size());\n        for (auto &q : readers) {\n          reducer_agg::EqView v;\n          v.data = reinterpret_cast<uint8_t *>(q.shared);\n          v.n_elems = q.elem_mask + 1;\n          v.buf_len = q.buf_mask + 1;\n          eqs.push_back(v);\n        }\n        return reducer_agg::aggregation_core_new(\n            eqs,\n            static_cast<uint32_t>(shard_num),\n            id_id_enabled_,\n            az_id_enabled_,\n            otlp_endpoint,\n            disable_node_ip_field,\n            reducer::OtlpGrpcFormatter::metric_description_field_enabled());\n      }())\n{}\n\n// Note: C++ internal metric emission paths are not used by the Rust core.\n\nvoid AggCore::run()\n{\n  set_self_thread_name(fmt::format(\"{}_{}\", app_name(), shard_num())).on_error([this](auto const &error) {\n    LOG::warn(\"unable to set name for {} core thread {}: {}\", app_name(), shard_num(), static_cast<int>(error));\n  });\n\n  // Delegate to Rust AggregationCore until stop\n  rust_core_->aggregation_core_run();\n}\n\nvoid AggCore::stop_async()\n{\n  // Cooperative stop for the Rust core\n  rust_core_->aggregation_core_stop();\n}\n\n} // namespace reducer::aggregation\n"
  },
  {
    "path": "reducer/aggregation/agg_core.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/aggregation/connection.h>\n#include <generated/ebpf_net/aggregation/index.h>\n#include <generated/ebpf_net/aggregation/protocol.h>\n#include <generated/ebpf_net/aggregation/transform_builder.h>\n#include <reducer/core_base.h>\n\n#include <memory>\n#include <vector>\n\n// cxx::bridge header for Rust AggregationCore\n#include <reducer_aggregation_cxxbridge.h>\n\nnamespace reducer {\nclass RpcQueueMatrix;\n}\n\nnamespace reducer::aggregation {\n\nclass AggCore : public CoreBase<\n                    ebpf_net::aggregation::Index,\n                    ebpf_net::aggregation::Protocol,\n                    ebpf_net::aggregation::Connection,\n                    ebpf_net::aggregation::TransformBuilder> {\npublic:\n  // Disables using IP address in node spans.\n  static void set_node_ip_field_disabled(bool disabled);\n  // Returns whether using IP addresses in node spans is disabled.\n  static bool node_ip_field_disabled() { return node_ip_field_disabled_; }\n\n  // Enables generating node-node (id-id) metrics.\n  static void set_id_id_enabled(bool enabled);\n\n  // Enables generating az-node metrics.\n  static void set_az_id_enabled(bool enabled);\n\n  // Enables generating flow logs from node-node (id-id) metrics.\n  static void set_flow_logs_enabled(bool enabled);\n\n  AggCore(\n      RpcQueueMatrix &matching_to_aggregation_queues,\n      RpcQueueMatrix &aggregation_to_logging_queues,\n      size_t shard_num,\n      u64 initial_timestamp,\n      std::string otlp_endpoint,\n      bool disable_node_ip_field);\n\nprivate:\n  // Flag indicating whether using IP addresses in node spans is disabled.\n  static bool node_ip_field_disabled_;\n\n  // Flag indicating whether id-id timeseries should be outputted.\n  static bool id_id_enabled_;\n\n  // Flag indicating whether az-id timeseries should be outputted.\n  static bool az_id_enabled_;\n\n  // Flag indicating whether flow logs should be outputted.\n  static bool flow_logs_enabled_;\n\npublic:\n  // Run the aggregation core using the Rust implementation.\n  void run();\n  // Stop the Rust core.\n  void stop_async();\n\nprivate:\n  // Opaque Rust AggregationCore owned via cxx rust::Box\n  rust::Box<reducer_agg::AggregationCore> rust_core_;\n};\n\n} // namespace reducer::aggregation\n"
  },
  {
    "path": "reducer/aggregation/agg_root_span.h",
    "content": "/*\n * Temporary stub while AggregationCore runs in Rust.\n * Render-generated aggregation spans include this header to define AggRootSpan.\n */\n#pragma once\n\n#include <generated/ebpf_net/aggregation/span_base.h>\n\nnamespace reducer {\nnamespace aggregation {\n\n// Minimal stub: inherit default no-op handlers so generated code can link.\nclass AggRootSpan : public ::ebpf_net::aggregation::AggRootSpanBase {\npublic:\n  AggRootSpan() = default;\n};\n\n} // namespace aggregation\n} // namespace reducer\n"
  },
  {
    "path": "reducer/aggregation/labels.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/aggregation/weak_refs.h>\n\n#include <functional>\n#include <string>\n#include <string_view>\n\nnamespace reducer::aggregation {\n\n// Set of labels (name/value pairs) that one side of a flow can have.\n//\nstruct NodeLabels {\n  std::string id;\n  std::string ip;\n  std::string az;\n  std::string role;\n  std::string role_uid;\n  std::string version;\n  std::string env;\n  std::string ns;\n  std::string type;\n  std::string process;\n  std::string container;\n  std::string pod;\n\n  // NOTE: if any label is added or removed, the FOREACH_NODE_LABEL macro needs to\n  //       be updated.\n\n  NodeLabels();\n  NodeLabels(::ebpf_net::aggregation::weak_refs::node node_ref);\n  NodeLabels(::ebpf_net::aggregation::weak_refs::az az_ref);\n  NodeLabels(::ebpf_net::aggregation::weak_refs::role role_ref);\n\n  void foreach (std::function<void(std::string_view, std::string_view)> func) const;\n};\n\n// Set of labels that a flow can have.\n//\nstruct FlowLabels {\n  NodeLabels src;\n  NodeLabels dst;\n\n  void foreach (std::function<void(std::string_view, std::string_view)> func) const;\n};\n\n} // namespace reducer::aggregation\n\n#include \"labels.inl\"\n"
  },
  {
    "path": "reducer/aggregation/labels.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include \"labels.h\"\n\n#include <reducer/constants.h>\n\n#define FOREACH_NODE_LABEL(FUNC)                                                                                               \\\n  FUNC(\"workload.name\", role)                                                                                                  \\\n  FUNC(\"workload.uid\", role_uid)                                                                                               \\\n  FUNC(\"availability_zone\", az)                                                                                                \\\n  FUNC(\"id\", id)                                                                                                               \\\n  FUNC(\"ip\", ip)                                                                                                               \\\n  FUNC(\"resolution_type\", type)                                                                                                \\\n  FUNC(\"image_version\", version)                                                                                               \\\n  FUNC(\"environment\", env)                                                                                                     \\\n  FUNC(\"namespace.name\", ns)                                                                                                   \\\n  FUNC(\"process.name\", process)                                                                                                \\\n  FUNC(\"container.name\", container)                                                                                            \\\n  FUNC(\"pod\", pod)\n\nnamespace reducer::aggregation {\n\ninline NodeLabels::NodeLabels() {}\n\ninline NodeLabels::NodeLabels(::ebpf_net::aggregation::weak_refs::node node_ref) : NodeLabels(node_ref.az())\n{\n  id = node_ref.id().to_string();\n  ip = node_ref.ip().to_string();\n  pod = node_ref.pod_name().to_string();\n}\n\ninline NodeLabels::NodeLabels(::ebpf_net::aggregation::weak_refs::az az_ref) : NodeLabels(az_ref.role())\n{\n  az = az_ref.s().to_string();\n}\n\ninline NodeLabels::NodeLabels(::ebpf_net::aggregation::weak_refs::role role_ref)\n{\n  role = role_ref.s().to_string();\n  role_uid = role_ref.uid().to_string();\n  version = role_ref.version().to_string();\n  env = role_ref.env().to_string();\n  ns = role_ref.ns().to_string();\n  type = to_string(static_cast<NodeResolutionType>(role_ref.node_type()), \"\");\n  process = role_ref.process().to_string();\n  container = role_ref.container().to_string();\n}\n\ninline void NodeLabels::foreach (std::function<void(std::string_view, std::string_view)> func) const\n{\n#define CALL_FUNC(NAME, VALUE) func(NAME, VALUE);\n  FOREACH_NODE_LABEL(CALL_FUNC);\n#undef CALL_FUNC\n}\n\ninline void FlowLabels::foreach (std::function<void(std::string_view, std::string_view)> func) const\n{\n#define CALL_FUNC_SRC(NAME, VALUE) func(\"source.\" NAME, src.VALUE);\n#define CALL_FUNC_DST(NAME, VALUE) func(\"dest.\" NAME, dst.VALUE);\n\n  FOREACH_NODE_LABEL(CALL_FUNC_SRC);\n  FOREACH_NODE_LABEL(CALL_FUNC_DST);\n\n#undef CALL_FUNC_SRC\n#undef CALL_FUNC_DST\n\n  // Equality label for the az field.\n  // Needed for queriying cross-zone traffic.\n  //\n  if (!src.az.empty() && !dst.az.empty()) {\n    func(\"az_equal\", (src.az == dst.az) ? \"true\" : \"false\");\n  }\n}\n\ninline bool operator==(NodeLabels const &lhs, NodeLabels const &rhs)\n{\n#define EQL(NAME, VALUE) &&(lhs.VALUE == rhs.VALUE)\n  return true FOREACH_NODE_LABEL(EQL);\n#undef EQL\n}\n\ntemplate <typename Hash> Hash AbslHashValue(Hash hash, NodeLabels const &labels)\n{\n#define LVAL(NAME, VALUE) , labels.VALUE\n  return Hash::combine(std::move(hash) FOREACH_NODE_LABEL(LVAL));\n#undef LVAL\n}\n\ninline bool operator==(FlowLabels const &lhs, FlowLabels const &rhs)\n{\n  return (lhs.src == rhs.src) && (lhs.dst == rhs.dst);\n}\n\ntemplate <typename Hash> Hash AbslHashValue(Hash hash, FlowLabels const &labels)\n{\n  return Hash::combine(std::move(hash), labels.src, labels.dst);\n}\n\n} // namespace reducer::aggregation\n\n#undef FOREACH_NODE_LABEL\n"
  },
  {
    "path": "reducer/aggregation/stat_counters.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <cstddef>\n\n#define AGG_CORE_TRUNCATION_FIELDS(FN)                                                                                         \\\n  FN(az, 0)                                                                                                                    \\\n  FN(container, 1)                                                                                                             \\\n  FN(env, 2)                                                                                                                   \\\n  FN(id, 3)                                                                                                                    \\\n  FN(ip, 4)                                                                                                                    \\\n  FN(ns, 5)                                                                                                                    \\\n  FN(pod_name, 6)                                                                                                              \\\n  FN(process, 7)                                                                                                               \\\n  FN(role, 8)                                                                                                                  \\\n  FN(version, 9)                                                                                                               \\\n  FN(role_uid, 10)\n\nnamespace reducer::aggregation {\n\nclass StatCounters {\npublic:\n  // how many field truncation counters are there\n  static constexpr std::size_t field_truncation_count = 0\n#define COUNT_FIELD_TRUNCATION_COUNTERS(Field, Index) +1\n      AGG_CORE_TRUNCATION_FIELDS(COUNT_FIELD_TRUNCATION_COUNTERS)\n#undef COUNT_FIELD_TRUNCATION_COUNTERS\n      ;\n\n  // calls the callback with signature (index is i-th counter)\n  // `(std::string_view field_name, std::size_t count, std::size_t index)`\n  // for each supported field truncation counter\n  template <typename Fn>\n  void foreach_field_truncation(Fn &&fn) const {\n#define CALL_TRUNCATION_FIELD_CALLBACK(Field, Index) fn(#Field, trunc_##Field, Index);\n      AGG_CORE_TRUNCATION_FIELDS(CALL_TRUNCATION_FIELD_CALLBACK)\n#undef CALL_TRUNCATION_FIELD_CALLBACK\n  }\n\n#define DECLARE_TRUNCATION_COUNTER(Field, Index) std::size_t trunc_##Field = 0;\n  AGG_CORE_TRUNCATION_FIELDS(DECLARE_TRUNCATION_COUNTER)\n#undef DECLARE_TRUNCATION_COUNTER\n};\n\n} // namespace reducer::aggregation\n"
  },
  {
    "path": "reducer/constants.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/constants.h>\n#include <platform/types.h>\n#include <util/enum.h>\n#include <util/version.h>\n\n#include <absl/time/time.h>\n\n#include <cassert>\n#include <cstdint>\n#include <string_view>\n\n#define ENUM_NAME NodeResolutionType\n#define ENUM_TYPE u8\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(NONE, 0, \"\")                                                                                                               \\\n  X(IP, 1, \"\")                                                                                                                 \\\n  X(DNS, 2, \"\")                                                                                                                \\\n  X(AWS, 3, \"\")                                                                                                                \\\n  X(INSTANCE_METADATA, 4, \"\")                                                                                                  \\\n  X(PROCESS, 5, \"\")                                                                                                            \\\n  X(LOCALHOST, 6, \"\")                                                                                                          \\\n  X(K8S_CONTAINER, 7, \"\")                                                                                                      \\\n  X(CONTAINER, 8, \"\")                                                                                                          \\\n  X(NOMAD, 9, \"\")\n#define ENUM_DEFAULT NONE\n#include <util/enum_operators.inl>\n\nconstexpr std::size_t K8S_UID_SUFFIX_LENGTH = 64;\n\nenum class FlowSide : bool {\n  SIDE_A,\n  SIDE_B,\n};\n\nenum class UpdateDirection {\n  NONE,\n  A_TO_B,\n  B_TO_A,\n};\n\nstatic constexpr char kUnknown[] = \"(unknown)\";\n\nstatic constexpr char kCommKubelet[] = \"kubelet\";\n\nstatic constexpr u16 kPortDNS = 53;\n\nstatic constexpr char kProductIdDimName[] = \"sf_product\";\nstatic constexpr char kProductIdDimValue[] = \"network-explorer\";\nstatic constexpr char kServiceName[] = \"reducer\";\n\nenum class ConnectorDirection : uint8_t {\n  UNKNOWN = 0,\n  REQUEST = 1,\n  RESPONSE = 2,\n};\n\n// Environment variable specifying which directory to use for saving data at runtime.\nstatic constexpr auto DATA_DIR_VAR = \"EBPF_NET_DATA_DIR\";\n\n// Environment variable specifying the path to a GeoIP database file.\nstatic constexpr char GEOIP_PATH_VAR[] = \"GEOIP_PATH\";\n\n// Time that RPC handlers wait between checks for messages in message queues.\nstatic constexpr auto RPC_HANDLE_TIME = 20ms;\n\n// Maximum number of messages each RPC handlers handles from each queue in each call\nstatic constexpr auto kMaxRpcBatchPerQueue = 10 * 1000;\n\n// helper functions\n\n// Reverse the connector direction.\n// @returns: UNKNOWN on UNKNOWN, and flips REQUEST and RESPONSE\ninline ConnectorDirection reverse(ConnectorDirection direction);\n\n// unary plus operator (neutral integer operator) to cast the enum class into\n// the underlying type (say, for indexing)\ninline std::underlying_type_t<FlowSide> operator+(FlowSide side);\n\n// complement operator that retrieves the other side\ninline FlowSide operator~(FlowSide side);\n\ninline FlowSide u8_to_side(u8 side);\n\ninline std::string_view to_string(FlowSide side, std::string_view fallback = \"<unknown>\");\n\ntemplate <typename Out> Out &operator<<(Out &&out, FlowSide side);\n\nnamespace versions::client {\n\n// minimum version of a collector for incoming connections to be accepted\nconstexpr VersionInfo MINIMUM_ACCEPTED_VERSION{0, 9, 0};\n\n} // namespace versions::client\n\n#include <reducer/constants.inl>\n"
  },
  {
    "path": "reducer/constants.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\ninline ConnectorDirection reverse(ConnectorDirection direction)\n{\n  switch (direction) {\n  case ConnectorDirection::UNKNOWN:\n    return ConnectorDirection::UNKNOWN;\n  case ConnectorDirection::REQUEST:\n    return ConnectorDirection::RESPONSE;\n  case ConnectorDirection::RESPONSE:\n    return ConnectorDirection::REQUEST;\n  default:\n    assert(false); // invalid direction\n    return ConnectorDirection::UNKNOWN;\n  }\n}\n\ninline std::underlying_type_t<ConnectorDirection> to_int(ConnectorDirection direction)\n{\n  return static_cast<std::underlying_type_t<ConnectorDirection>>(direction);\n}\n\ninline std::underlying_type_t<FlowSide> operator+(FlowSide side)\n{\n  return static_cast<std::underlying_type_t<FlowSide>>(side);\n}\n\ninline FlowSide operator~(FlowSide side)\n{\n  return static_cast<FlowSide>(!static_cast<std::underlying_type_t<FlowSide>>(side));\n}\n\ninline FlowSide u8_to_side(u8 side)\n{\n  assert(side <= 1);\n  return static_cast<FlowSide>(static_cast<std::underlying_type_t<FlowSide>>(side));\n}\n\ninline std::string_view to_string(FlowSide side, std::string_view fallback)\n{\n  switch (side) {\n  case FlowSide::SIDE_A:\n    return \"SIDE_A\";\n  case FlowSide::SIDE_B:\n    return \"SIDE_B\";\n  default:\n    return fallback;\n  }\n}\n\ntemplate <typename Out> Out &operator<<(Out &&out, FlowSide side)\n{\n  out << to_string(side);\n  return out;\n}\n"
  },
  {
    "path": "reducer/copy_metrics.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\nnamespace reducer {\n\ntemplate <typename Dst, typename Src> void copy_tcp_metrics(Dst &dst, Src const &src)\n{\n  dst.active_sockets = src.active_sockets;\n  dst.sum_retrans = src.sum_retrans;\n  dst.sum_bytes = src.sum_bytes;\n  dst.sum_srtt = src.sum_srtt;\n  dst.sum_delivered = src.sum_delivered;\n  dst.active_rtts = src.active_rtts;\n  dst.syn_timeouts = src.syn_timeouts;\n  dst.new_sockets = src.new_sockets;\n  dst.tcp_resets = src.tcp_resets;\n}\n\ntemplate <typename Dst, typename Src> void copy_udp_metrics(Dst &dst, Src const &src)\n{\n  dst.active_sockets = src.active_sockets;\n  dst.addr_changes = src.addr_changes;\n  dst.packets = src.packets;\n  dst.bytes = src.bytes;\n  dst.drops = src.drops;\n}\n\ntemplate <typename Dst, typename Src> void copy_dns_metrics(Dst &dst, Src const &src)\n{\n  dst.active_sockets = src.active_sockets;\n  dst.requests_a = src.requests_a;\n  dst.requests_aaaa = src.requests_aaaa;\n  dst.responses = src.responses;\n  dst.timeouts = src.timeouts;\n  dst.sum_total_time_ns = src.sum_total_time_ns;\n  dst.sum_processing_time_ns = src.sum_processing_time_ns;\n}\n\ntemplate <typename Dst, typename Src> void copy_http_metrics(Dst &dst, Src const &src)\n{\n  dst.active_sockets = src.active_sockets;\n  dst.sum_code_200 = src.sum_code_200;\n  dst.sum_code_400 = src.sum_code_400;\n  dst.sum_code_500 = src.sum_code_500;\n  dst.sum_code_other = src.sum_code_other;\n  dst.sum_total_time_ns = src.sum_total_time_ns;\n  dst.sum_processing_time_ns = src.sum_processing_time_ns;\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/core.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"core.h\"\n\n#include <reducer/constants.h>\n#include <reducer/util/thread_ops.h>\n\n#include <platform/userspace-time.h>\n#include <util/log_formatters.h>\n#include <util/time.h>\n#include <util/uv_helpers.h>\n\n#include <math.h>\n\nnamespace reducer {\n\nthread_local Core *Core::instance_ = nullptr;\n\nCore::Core(std::string_view app_name, size_t shard_num, u64 initial_timestamp)\n    : app_name_(app_name), shard_num_(shard_num), current_timestamp_(initial_timestamp)\n{\n  CHECK_UV(uv_loop_init(&loop_));\n\n  CHECK_UV(uv_async_init(&loop_, &stop_async_, &on_stop_async));\n  stop_async_.data = this;\n\n  CHECK_UV(uv_timer_init(&loop_, &rpc_timer_));\n  rpc_timer_.data = this;\n\n  CHECK_UV(uv_timer_init(&loop_, &stats_timer_));\n  stats_timer_.data = this;\n}\n\nCore::~Core() {}\n\nvoid Core::set_connection_authenticated()\n{\n  for (auto &rpc_client : rpc_clients_) {\n    rpc_client.handler->set_connection_authenticated();\n  }\n}\n\ndouble Core::timeslot_duration() const\n{\n  return virtual_clock_.timeslot_duration();\n}\n\nstd::chrono::nanoseconds Core::metrics_timestamp() const\n{\n  // clock timestamps are in nanoseconds\n  u64 timestamp = current_timestamp();\n\n  // fraction of the timeslot left after the current timestamp\n  double frac = 1.0 - fmod(timestamp / timeslot_duration(), 1);\n\n  // aligned to end of timeslot boundary\n  u64 metrics_timestamp = timestamp + (u64)(frac * timeslot_duration());\n\n  return std::chrono::nanoseconds(metrics_timestamp);\n}\n\nvoid Core::run()\n{\n  // save thread-local instance\n  instance_ = this;\n\n  set_self_thread_name(fmt::format(\"{}_{}\", app_name_, shard_num_)).on_error([this](auto const &error) {\n    LOG::warn(\"unable to set name for {} core thread {}: {}\", app_name_, shard_num_, error);\n  });\n\n  if (!rpc_clients_.empty()) {\n    auto repeat = integer_time<std::chrono::milliseconds>(RPC_HANDLE_TIME);\n    CHECK_UV(uv_timer_start(&rpc_timer_, on_rpc_timer, repeat, repeat));\n  }\n\n  {\n    auto repeat = integer_time<std::chrono::milliseconds>(STATS_PERIOD);\n    CHECK_UV(uv_timer_start(&stats_timer_, on_stats_timer, repeat, repeat));\n  }\n\n  uv_run(&loop_, UV_RUN_DEFAULT);\n  close_uv_loop_cleanly(&loop_);\n\n  done_.Notify();\n}\n\nvoid Core::stop_async()\n{\n  uv_async_send(&stop_async_);\n}\n\nvoid Core::stop_sync()\n{\n  stop_async();\n  wait_for_shutdown();\n}\n\nvoid Core::wait_for_shutdown()\n{\n  done_.WaitForNotification();\n}\n\nvoid Core::on_stop_async(uv_async_t *handle)\n{\n  auto core = reinterpret_cast<Core *>(handle->data);\n\n  uv_stop(&core->loop_);\n}\n\nvoid Core::on_rpc_timer(uv_timer_t *timer)\n{\n  auto core = reinterpret_cast<Core *>(timer->data);\n\n  bool any_handled = core->handle_rpc();\n\n  if (any_handled) {\n    // restart the timer immediately\n    // otherwise the timer will again fire after the normal repeat time\n    uv_timer_start(timer, timer->timer_cb, 0, timer->repeat);\n  }\n}\n\nvoid Core::on_stats_timer(uv_timer_t *timer)\n{\n  auto core = reinterpret_cast<Core *>(timer->data);\n\n  core->write_internal_stats();\n}\n\nbool Core::handle_rpc()\n{\n  // Current monotonic time.\n  u64 time_now = monotonic();\n\n  // Time at which we will stop handling messages in this iteration, to allow\n  // other timers to trigger when due.\n  const u64 timeout_ns = time_now + integer_time<std::chrono::nanoseconds>(RPC_HANDLE_TIME);\n\n  bool any_handled{false};\n  bool timed_out{false};\n\n  // Try going through all RPC clients, starting from the next in line.\n  for (size_t i = 0; (i < rpc_clients_.size()) && !timed_out; ++i) {\n    const auto rpc_client_index = next_rpc_client_;\n\n    // Since handling of RPC messages can be interrupted by a time-out, we\n    // have to make sure that on the next try we start from the right place.\n    next_rpc_client_ = (next_rpc_client_ + 1) % rpc_clients_.size();\n\n    auto &rpc_client = rpc_clients_[rpc_client_index];\n\n    if (!virtual_clock_.can_update(rpc_client_index)) {\n      continue;\n    }\n\n    rpc_client.queue.start_read_batch();\n\n    // process queue elements, but not more than kMaxRpcBatchPerQueue\n    for (size_t msg_i = 0; (msg_i < kMaxRpcBatchPerQueue) && virtual_clock_.can_update(rpc_client_index) &&\n                           (rpc_client.queue.peek() > 0) && !timed_out;\n         msg_i++) {\n      char *msg_buf{nullptr};\n      int msg_len{0};\n\n      // decode the message timestamp\n      u64 msg_timestamp;\n      if (rpc_client.queue.peek_value(msg_timestamp) < 0) {\n        LOG::critical(\"could not read timestamp from a message\");\n        // read the message to remove it from the queue but skip processing it\n        (void)rpc_client.queue.read(msg_buf);\n        continue;\n      }\n\n      // update the virtual clock for this client\n      if (int r = virtual_clock_.update(rpc_client_index, msg_timestamp); r < 0) {\n        if (r == -EINVAL) {\n          LOG::critical(\n              \"{}-{}: out-of-order message from client {} ({}): current_timestamp={}, msg_timestamp={}\",\n              app_name(),\n              shard_num(),\n              rpc_client_index,\n              to_string(rpc_client.client_type),\n              current_timestamp_,\n              msg_timestamp);\n          throw std::runtime_error(fmt::format(\"{}-{}: out-of-order message\", app_name(), shard_num()));\n        } else {\n          throw std::runtime_error(fmt::format(\"{}-{}: unexpected error: {}\", app_name(), shard_num(), r));\n        }\n      }\n\n      if (virtual_clock_.is_current(rpc_client_index)) {\n        // update this core's timestamp\n        current_timestamp_ = std::max(current_timestamp_, msg_timestamp);\n        // read this message\n        msg_len = rpc_client.queue.read(msg_buf);\n        // pass the message to the message handler\n        rpc_client.handler->handle(current_timestamp_, msg_buf, msg_len);\n        // yup\n        any_handled = true;\n      }\n\n      // update the current time\n      time_now = monotonic();\n      // check for timeout\n      timed_out = (time_now >= timeout_ns);\n    }\n\n    rpc_client.queue.finish_read_batch();\n  }\n\n  if (virtual_clock_.advance()) {\n    on_timeslot_complete();\n  }\n\n  return any_handled;\n}\n\nvoid Core::on_timeslot_complete() {}\n\nvoid Core::write_internal_stats() {}\n\n////////////////////////////////////////////////////////////////////////////////\n\nCore::RpcClient::RpcClient(ElementQueue _queue, std::unique_ptr<IRpcHandler> _handler, ClientType _client_type)\n    : queue(std::move(_queue)), handler(std::move(_handler)), client_type(_client_type)\n{}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/core.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/util/virtual_clock.h>\n\n#include <common/client_type.h>\n\n#include <util/element_queue_cpp.h>\n#include <util/fast_div.h>\n\n#include <absl/synchronization/notification.h>\n#include <uv.h>\n\n#include <cassert>\n#include <chrono>\n#include <optional>\n#include <string>\n#include <vector>\n\nnamespace reducer {\n\n// Base class for core implementations.\n//\nclass Core {\npublic:\n  static constexpr auto STATS_PERIOD = 10s;\n\n  virtual ~Core();\n\n  // Runs the core's execution loop.\n  void run();\n\n  // Stops the core's execution loop. Can be run from any thread.\n  void stop_async();\n  void stop_sync();\n  void wait_for_shutdown();\n\n  // This core's application name.\n  std::string_view app_name() const { return app_name_; }\n  // Returns this core's shard number.\n  size_t shard_num() const { return shard_num_; }\n\n  // Flag the connection as authenticated.\n  // Used when this core is receiving messages only from in-process sources.\n  void set_connection_authenticated();\n\n  // This core's current timestamp.\n  u64 current_timestamp() const { return current_timestamp_; }\n  // Duration of time slots, in timestamp units.\n  double timeslot_duration() const;\n\n  // Returns the current metrics timestamp, for output to a TSDB.\n  std::chrono::nanoseconds metrics_timestamp() const;\n\nprotected:\n  // Subclasses implement to use concrete render-generated classes.\n  //\n  struct IRpcHandler {\n    virtual ~IRpcHandler() {};\n\n    // Handle a received RPC message.\n    virtual void handle(u64 current_timestamp, char *buf, size_t len) = 0;\n    // Flag connections as authenticated.\n    virtual void set_connection_authenticated() = 0;\n  };\n\n  // For each RPC client that is sending messages to this core.\n  //\n  struct RpcClient {\n    // Queue to which the client writes and we read from.\n    ElementQueue queue;\n    // Handles incoming messages received from this client.\n    std::unique_ptr<IRpcHandler> handler;\n    // Type of this client.\n    ClientType client_type;\n\n    RpcClient(ElementQueue queue, std::unique_ptr<IRpcHandler> handler, ClientType client_type);\n  };\n\n  // Clients sending RPC messages to this core.\n  std::vector<RpcClient> rpc_clients_;\n\n  // The virtual clock driven by messages from RPC clients.\n  VirtualClock virtual_clock_;\n\n  Core(std::string_view app_name, size_t shard_num, u64 initial_timestamp);\n\nprivate:\n  // Core instance belonging to the current thread.\n  // Assigned in run().\n  static thread_local Core *instance_;\n\n  // This core's application name.\n  std::string app_name_;\n  // This core's shard number.\n  size_t shard_num_;\n\n  // Current timestamp.\n  // Initialized to initial_timestamp on construction.\n  u64 current_timestamp_;\n\n  // Next RPC client to read from.\n  size_t next_rpc_client_{0};\n\n  // Libuv loop object.\n  uv_loop_t loop_;\n  // Async object used for stopping the loop from another thread.\n  uv_async_t stop_async_;\n  // Notification triggered when the loop is done running.\n  absl::Notification done_;\n\n  // Timer that services reading RPC messages from the queue.\n  uv_timer_t rpc_timer_;\n  // Timer that services writing internal stats to prometheus.\n  uv_timer_t stats_timer_;\n\n  // Callback function for stop_async.\n  static void on_stop_async(uv_async_t *handle);\n  // RPC timer callback.\n  static void on_rpc_timer(uv_timer_t *timer);\n  // Internal stats timer callback.\n  static void on_stats_timer(uv_timer_t *timer);\n\n  // Reads incoming RPC messages that are enqueued in the RPC queue.\n  // Forwards received messages to the RPC handler object to handle.\n  // If any messages are handled, returns true. Otherwise returns false.\n  // Gets invoked periodically by the RPC timer.\n  bool handle_rpc();\n\n  // Called when the current timeslot is complete.\n  virtual void on_timeslot_complete();\n\n  // Subclasses implement to output internal stats to be scraped by a\n  // time-series DB.\n  // Gets invoked periodically by the internal stats timer.\n  virtual void write_internal_stats();\n\n  template <class T> friend T &local_core();\n};\n\n// Returns the core instance belonging to the current thread.\n//\ntemplate <class T> T &local_core()\n{\n  assert(Core::instance_);\n  return dynamic_cast<T &>(*Core::instance_);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/core_base.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"core.h\"\n\n#include <reducer/publisher.h>\n#include <reducer/rpc_stats.h>\n#include <reducer/util/index_dumper.h>\n\n#include <generated/ebpf_net/matching/auto_handles.h>\n\n#include <common/client_type.h>\n#include <platform/userspace-time.h>\n#include <util/short_string.h>\n\n#include <spdlog/fmt/fmt.h>\n\nnamespace reducer {\n\nclass InternalMetricsEncoder;\n\n// Helper for implementing core classes.\n//\ntemplate <typename Index, typename Protocol, typename Connection, typename TransformBuilder> class CoreBase : public Core {\nprotected:\n  struct RpcHandler : public IRpcHandler {\n    RpcHandler(\n        Index &index,\n        TransformBuilder &transform_builder,\n        ClientType client_type,\n        std::size_t client_index,\n        RpcReceiverStats &rpc_receiver_stats)\n        : protocol(transform_builder), connection(protocol, index), receiver_stats(rpc_receiver_stats)\n    {\n      conn_name = fmt::format(\"inproc-conn-{}\", client_index);\n    }\n\n    void handle(u64 current_timestamp_ns, char *buf, size_t len) override\n    {\n      const std::chrono::nanoseconds current_timestamp{current_timestamp_ns};\n\n      auto res = protocol.handle(buf, len);\n\n      if (res.client_timestamp.count() > 0) {\n        receiver_stats.record_latency(current_timestamp - res.client_timestamp);\n      }\n    }\n\n    void set_connection_authenticated() override { connection.on_connection_authenticated(); }\n\n    Protocol protocol;\n    Connection connection;\n    RpcReceiverStats &receiver_stats;\n    short_string<16> conn_name;\n  };\n\n  // Transform builder for JIT-ing incoming messages.\n  TransformBuilder transform_builder_;\n  // Index object belonging to this core.\n  Index index_;\n  // A rate-limited helper to dump the core's index.\n  IndexDumper index_dumper_;\n\n  template <typename... Args>\n  CoreBase(std::string_view app_name, size_t shard_num, u64 initial_timestamp, Args &&...args)\n      : Core(app_name, shard_num, initial_timestamp), transform_builder_(), index_(std::forward<Args>(args)...)\n  {}\n\n  void add_rpc_clients(std::vector<ElementQueue> const &queues, ClientType client_type, RpcReceiverStats &receiver_stats)\n  {\n    for (auto &queue : queues) {\n      const auto client_index = rpc_clients_.size();\n      rpc_clients_.emplace_back(\n          queue,\n          std::make_unique<RpcHandler>(index_, transform_builder_, client_type, client_index, receiver_stats),\n          client_type);\n    }\n\n    virtual_clock_.add_inputs(queues.size());\n  }\n\n  // Writes internal stats common to all core types.\n  void write_common_stats(InternalMetricsEncoder &encoder, u64 time_ns);\n\n  template <typename CoreStatsHandle> void write_common_stats_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns);\n\n  void dump_internal_state(std::chrono::milliseconds timestamp);\n};\n\n} // namespace reducer\n\n#include \"core_base.inl\"\n"
  },
  {
    "path": "reducer/core_base.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n\nnamespace reducer {\nstruct SpanStats {\n  std::string_view span_name;\n  std::size_t utilization;\n  double utilization_fraction;\n  std::size_t utilization_max;\n};\n\ntemplate <typename I, typename P, typename C, typename T>\nvoid CoreBase<I, P, C, T>::write_common_stats(InternalMetricsEncoder &encoder, u64 time_ns)\n{\n  const auto module = app_name();\n  const auto shard = shard_num();\n\n  index_.size_statistics(\n      [&](std::string_view span_name, std::size_t allocated, std::size_t max_allocated, std::size_t pool_size) {\n        SpanUtilizationStats stats;\n        stats.labels.span = span_name;\n        stats.labels.module = module;\n        stats.labels.shard = std::to_string(shard);\n        stats.metrics.utilization = allocated;\n        stats.metrics.utilization_fraction = (double)allocated / pool_size;\n        stats.metrics.utilization_max = max_allocated;\n        encoder.write_internal_stats(stats, time_ns);\n      });\n\n  for (size_t conn = 0; conn < rpc_clients_.size(); ++conn) {\n    auto &rpc_handler = static_cast<RpcHandler &>(*rpc_clients_[conn].handler);\n    auto &connection = rpc_handler.connection;\n\n    connection.message_stats.foreach ([&](std::string_view module, std::string_view msg, int severity, u64 count) {\n      ConnectionMessageStats stats;\n      stats.labels.module = module;\n      stats.labels.shard = std::to_string(shard);\n      stats.labels.connection = std::to_string(conn);\n      stats.labels.message = msg;\n      stats.labels.severity = std::to_string(severity);\n      stats.metrics.count = count;\n      encoder.write_internal_stats(stats, time_ns);\n    });\n\n    connection.message_errors.foreach ([&](std::string_view module, std::string_view msg, std::string_view error, u64 count) {\n      ConnectionMessageErrorStats stats;\n      stats.labels.module = module;\n      stats.labels.shard = std::to_string(shard);\n      stats.labels.connection = std::to_string(conn);\n      stats.labels.message = msg;\n      stats.labels.error = error;\n      stats.metrics.count = count;\n\n      encoder.write_internal_stats(stats, time_ns);\n    });\n  }\n\n  StatusStats stats;\n  stats.labels.module = module;\n  stats.labels.shard = std::to_string(shard);\n  stats.labels.program = kServiceName;\n  std::stringstream ss;\n  ss << versions::release;\n  stats.labels.version = ss.str();\n  stats.metrics.status = 1u;\n  encoder.write_internal_stats(stats, time_ns);\n}\n\ntemplate <typename I, typename P, typename C, typename T>\ntemplate <typename CoreStatsHandle>\nvoid CoreBase<I, P, C, T>::write_common_stats_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns)\n{\n  const auto module = app_name();\n  const auto shard = shard_num();\n\n  index_.size_statistics(\n      [&](std::string_view span_name, std::size_t allocated, std::size_t max_allocated, std::size_t pool_size) {\n        internal_metrics.span_utilization_stats(\n            jb_blob(span_name), jb_blob(module), shard, allocated, max_allocated, pool_size, time_ns);\n      });\n\n  for (size_t conn = 0; conn < rpc_clients_.size(); ++conn) {\n    auto &rpc_handler = static_cast<RpcHandler &>(*rpc_clients_[conn].handler);\n    auto &connection = rpc_handler.connection;\n\n    connection.message_stats.foreach ([&](std::string_view module, std::string_view msg, int severity, u64 count) {\n      internal_metrics.connection_message_stats(jb_blob(module), jb_blob(msg), shard, severity, conn, time_ns, count);\n    });\n\n    connection.message_errors.foreach ([&](std::string_view module, std::string_view msg, std::string_view error, u64 count) {\n      internal_metrics.connection_message_error_stats(\n          jb_blob(module), shard, conn, jb_blob(msg), jb_blob(error), count, time_ns);\n    });\n  }\n\n  std::stringstream ss;\n  ss << versions::release;\n  internal_metrics.status_stats(jb_blob(module), shard, jb_blob(std::string(kServiceName)), jb_blob(ss.str()), 1u, time_ns);\n}\n\ntemplate <typename I, typename P, typename C, typename T>\nvoid CoreBase<I, P, C, T>::dump_internal_state(std::chrono::milliseconds timestamp)\n{\n  index_dumper_.dump(app_name(), shard_num(), index_, std::chrono::duration_cast<std::chrono::seconds>(timestamp));\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/core_type.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME CoreType\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(ingest, 2, \"\")                                                                                                             \\\n  X(match, 3, \"\")                                                                                                              \\\n  X(aggregate, 4, \"\")\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "reducer/disabled_metrics.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"disabled_metrics.h\"\n\n#include <algorithm>\n#include <cctype>\n\n#include <util/string_view.h>\n\nnamespace {\n\n// helper to trim whitespace and enforce lowercase\nstd::string clean(std::string_view s)\n{\n  std::string ret(views::trim_ws(s));\n\n  std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) { return std::tolower(c); });\n\n  return ret;\n}\n\n} // namespace\n\nnamespace reducer {\n\nDisabledMetrics::DisabledMetrics(std::string_view disabled_metrics_arg, std::string_view enabled_metrics_arg)\n{\n  constexpr std::string_view NO_METRICS_DISABLED = \"none\";\n\n  // All internal stats are disabled by default except collector_health.\n  // Users can only use enable flags in order to turn internal stat(s).\n  disabled_defaults();\n\n  if (disabled_metrics_arg.compare(NO_METRICS_DISABLED) == 0) {\n    // nothing is disabled\n    return;\n  }\n\n  size_t current = 0;\n  size_t next = 0;\n\n  while (current < disabled_metrics_arg.size()) {\n    next = disabled_metrics_arg.find(\",\", current);\n\n    if (next == std::string_view::npos) {\n      next = disabled_metrics_arg.size();\n    }\n\n    std::string metric = clean(disabled_metrics_arg.substr(current, next - current));\n    disable_metric(metric);\n\n    current = next + 1;\n  }\n\n  current = 0;\n  while (current < enabled_metrics_arg.size()) {\n    next = enabled_metrics_arg.find(\",\", current);\n\n    if (next == std::string_view::npos) {\n      next = enabled_metrics_arg.size();\n    }\n\n    std::string metric = clean(enabled_metrics_arg.substr(current, next - current));\n    enable_metric(metric);\n\n    current = next + 1;\n  }\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/disabled_metrics.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include <util/log.h>\n\n#include \"outbound_metrics.h\"\n#include \"outbound_stats.h\"\n\n// This class manages disabling metrics at the group and individual metric\n// level.\n//\n// If you need to create a new group of metrics, see outbound_metrics.h\n//\n// To add additional metric groups to disable, merely add another line to\n// the METRIC_GROUPS macro below:\n//    X(ebpf_net, EbpfNetMetrics).\n// the macros and templates in the class take care of the rest.\n// However, if you need to make a certain metric in the group (or the metric\n// group itself) disabled by default, update the  disabled_defaults()\n// method below.\n\nnamespace reducer {\n\n#define METRIC_GROUPS(X)                                                                                                       \\\n  X(tcp, TcpMetrics)                                                                                                           \\\n  X(udp, UdpMetrics)                                                                                                           \\\n  X(dns, DnsMetrics)                                                                                                           \\\n  X(http, HttpMetrics)                                                                                                         \\\n  X(ebpf_net, EbpfNetMetrics)\n\n#define METRIC_FLAG(METRIC) METRIC##_disabled_flags_\n\nclass DisabledMetrics {\npublic:\n  // disable metrics by string.\n  // metrics are separated by commas\n  // to disable an entire group, specify <group>.all (e.g. tcp.all)\n  DisabledMetrics(std::string_view disabled_metrics_arg, std::string_view enabled_metrics_arg = \"\");\n\n  // determine if the group as a whole is disabled\n  // specialized versions of this template are declared later in this file.\n  template <typename T> bool is_metric_group_disabled() const\n  {\n    assert(false);\n    return true;\n  }\n\n  // inspect the disabled flags directly\n  // specialized versions of this template are declared later in this file.\n  template <typename T> std::uint64_t disabled_flags() const\n  {\n    assert(false);\n    return 0u;\n  }\n\n  // check if a particular metric is disabled\n#define METRIC_MEMBER_FNS(METRIC, ENUM_TYPE)                                                                                   \\\n  bool is_metric_disabled(ENUM_TYPE metric) const { return static_cast<std::uint64_t>(metric) & METRIC_FLAG(METRIC); }\n\n  METRIC_GROUPS(METRIC_MEMBER_FNS)\n#undef METRIC_MEMBER_FNS\n\nprivate:\n  void disabled_defaults()\n  {\n    // FUTURE insert any additional default disabled metrics here\n    disable_metric(\"tcp.rtt.num_measurements\");\n\n    // shut all the internal stats off by default\n    disable_metric(\"ebpf_net.all\");\n    // except the following\n    enable_metric(\"ebpf_net.collector_health\");\n    enable_metric(\"ebpf_net.bpf_log\");\n    enable_metric(\"ebpf_net.otlp_grpc.bytes_failed\");\n    enable_metric(\"ebpf_net.otlp_grpc.bytes_sent\");\n    enable_metric(\"ebpf_net.otlp_grpc.metrics_failed\");\n    enable_metric(\"ebpf_net.otlp_grpc.metrics_sent\");\n    enable_metric(\"ebpf_net.otlp_grpc.requests_failed\");\n    enable_metric(\"ebpf_net.otlp_grpc.requests_sent\");\n    enable_metric(\"ebpf_net.otlp_grpc.unknown_response_tags\");\n    enable_metric(\"ebpf_net.up\");\n  }\n\n  // disable a metric by its full name.\n  // a metric of <group>.all (e.g. tcp.all) will disable the entire group.\n  void disable_metric(const std::string &metric)\n  {\n    if (metric.empty()) {\n      return;\n    }\n\n    size_t dot_pos = metric.find('.');\n    if (dot_pos == std::string::npos) {\n      return;\n    }\n\n    std::string prefix = metric.substr(0, dot_pos);\n\n    disable_metric_by_prefix(prefix, metric);\n  }\n\n  // enable a metric by its full name.\n  // this is just a convenience function for the disabled_defaults() method.\n  // it has no user visibility (at this time).\n  void enable_metric(const std::string &metric)\n  {\n    if (metric.empty()) {\n      return;\n    }\n\n    size_t dot_pos = metric.find('.');\n    if (dot_pos == std::string::npos) {\n      return;\n    }\n\n    std::string prefix = metric.substr(0, dot_pos);\n\n    enable_metric_by_prefix(prefix, metric);\n  }\n\n  // helper for mapping the prefix (e.g. tcp) to its enum type and bitset.\n  void disable_metric_by_prefix(const std::string &prefix, const std::string &metric)\n  {\n#define IF_METRICS(METRIC, ENUM_TYPE)                                                                                          \\\n  if (prefix == #METRIC) {                                                                                                     \\\n    disable_metric<ENUM_TYPE>(metric, METRIC_FLAG(METRIC));                                                                    \\\n    return;                                                                                                                    \\\n  }\n\n    METRIC_GROUPS(IF_METRICS)\n#undef IF_METRICS\n\n    LOG::warn(\"Unable to disable unknown metric {}\", metric);\n  }\n\n  // helper for mapping the prefix (e.g. tcp) to its enum type and bitset.\n  void enable_metric_by_prefix(const std::string &prefix, const std::string &metric)\n  {\n#define IF_METRICS(METRIC, ENUM_TYPE)                                                                                          \\\n  if (prefix == #METRIC) {                                                                                                     \\\n    enable_metric<ENUM_TYPE>(metric, METRIC_FLAG(METRIC));                                                                     \\\n    return;                                                                                                                    \\\n  }\n\n    METRIC_GROUPS(IF_METRICS)\n#undef IF_METRICS\n\n    LOG::warn(\"Unable to enable unknown metric {}\", metric);\n  }\n\n  // disable the metric in the given bit field.\n  template <typename T> void disable_metric(const std::string &metric, std::uint64_t &flags);\n\n  // enable the metric in the given bit field.\n  template <typename T> void enable_metric(const std::string &metric, std::uint64_t &flags);\n\n  // declare the bit fields\n#define METRIC_MEMBERS(METRIC, ENUM_TYPE) std::uint64_t METRIC_FLAG(METRIC) = 0u;\n  METRIC_GROUPS(METRIC_MEMBERS)\n#undef METRIC_MEMBERS\n};\n\n// implementation of the above.\ntemplate <typename T> void DisabledMetrics::disable_metric(const std::string &metric, std::uint64_t &flags)\n{\n  T metric_enum = try_enum_from_string(metric, T::unknown);\n  if (metric_enum == T::unknown) {\n    LOG::warn(\"Unable to disable unknown metric {}\", metric);\n  } else {\n    LOG::info(\"Disabling metric {}\", metric);\n    flags |= (u64)metric_enum;\n  }\n}\n\n// implementation of the above.\ntemplate <typename T> void DisabledMetrics::enable_metric(const std::string &metric, std::uint64_t &flags)\n{\n  T metric_enum = try_enum_from_string(metric, T::unknown);\n  if (metric_enum == T::unknown) {\n    LOG::warn(\"Unable to enable unknown metric {}\", metric);\n  } else {\n    LOG::info(\"Enabling metric {}\", metric);\n    flags &= ~((u64)metric_enum);\n  }\n}\n\n// template specializations of the above.\n#define METRIC_MEMBER_SPECIALIZATIONS(METRIC, ENUM_TYPE)                                                                       \\\n  template <> inline bool DisabledMetrics::is_metric_group_disabled<ENUM_TYPE>() const                                         \\\n  {                                                                                                                            \\\n    return METRIC_FLAG(METRIC) == static_cast<std::uint64_t>(ENUM_TYPE::all);                                                  \\\n  }                                                                                                                            \\\n  template <> inline std::uint64_t DisabledMetrics::disabled_flags<ENUM_TYPE>() const                                          \\\n  {                                                                                                                            \\\n    return METRIC_FLAG(METRIC);                                                                                                \\\n  }\n\nMETRIC_GROUPS(METRIC_MEMBER_SPECIALIZATIONS)\n#undef METRIC_MEMBER_SPECIALIZATIONS\n\n#undef METRIC_GROUPS\n#undef METRIC_FLAG\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/disabled_metrics_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"disabled_metrics.h\"\n\n#include <gtest/gtest.h>\n\nnamespace reducer {\n\nTEST(DisabledMetricsTest, AllEbpfEnabled)\n{\n  DisabledMetrics disabled_metrics(\"http.all\", \"ebpf_net.all\");\n\n  EXPECT_EQ(disabled_metrics.disabled_flags<EbpfNetMetrics>(), 0u);\n  EXPECT_EQ(disabled_metrics.disabled_flags<HttpMetrics>(), 0xFFFFFFFF);\n}\n\nTEST(DisabledMetricsTest, IndividualInternalStatsEnabled)\n{\n  DisabledMetrics disabled_metrics(\"\", \"ebpf_net.message,ebpf_net.span_utilization_fraction\");\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::message));\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::span_utilization_fraction));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::client_handle_pool));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::rpc_write_stalls));\n}\n\nTEST(DisabledMetricsTest, NothingDisabledExceptDefaults)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"none\");\n\n  // assert\n  EXPECT_EQ(disabled_metrics.disabled_flags<UdpMetrics>(), 0u);\n  EXPECT_EQ(disabled_metrics.disabled_flags<DnsMetrics>(), 0u);\n  EXPECT_EQ(disabled_metrics.disabled_flags<HttpMetrics>(), 0u);\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::rtt_num_measurements));\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(TcpMetrics::new_sockets));\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(TcpMetrics::rtt_average));\n}\n\nTEST(DisabledMetricsTest, Enabled)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"tcp.all,udp.bytes,dns.timeouts\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::all));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(UdpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(DnsMetrics::timeouts));\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\nTEST(DisabledMetricsTest, HappyPath)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"tcp.all,udp.bytes,dns.timeouts\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::all));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(UdpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(DnsMetrics::timeouts));\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\nTEST(DisabledMetricsTest, HappyPathWhiteSpace)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\" tcp.all,    udp.bytes,\\tdns.timeouts\\n  , http.status_code   \");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::all));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(UdpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(DnsMetrics::timeouts));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(HttpMetrics::status_code));\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\nTEST(DisabledMetricsTest, InternalStats)\n{\n  DisabledMetrics disabled_metrics(\"\");\n\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::codetiming_avg_ns));\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(EbpfNetMetrics::collector_health));\n}\n\nTEST(DisabledMetricsTest, Casing)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"TcP.All, UDP.BYTES,dns.timeouts\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::all));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(UdpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(DnsMetrics::timeouts));\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\nTEST(DisabledMetricsTest, JustOneGroup)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"tcp.all\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::active));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\nTEST(DisabledMetricsTest, DanglingSemi)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"tcp.all,\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::bytes));\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::active));\n  EXPECT_TRUE(disabled_metrics.is_metric_group_disabled<TcpMetrics>());\n\n  EXPECT_FALSE(disabled_metrics.is_metric_disabled(HttpMetrics::active_sockets));\n}\n\n// TODO when we define what the default disabled_metrics set is, update this test\nTEST(DisabledMetricsTest, EmptyCtor)\n{\n  // arrange\n  DisabledMetrics disabled_metrics(\"\");\n\n  // assert\n  EXPECT_TRUE(disabled_metrics.is_metric_disabled(TcpMetrics::rtt_num_measurements));\n\n  EXPECT_EQ(disabled_metrics.disabled_flags<UdpMetrics>(), 0u);\n  EXPECT_EQ(disabled_metrics.disabled_flags<DnsMetrics>(), 0u);\n  EXPECT_EQ(disabled_metrics.disabled_flags<HttpMetrics>(), 0u);\n}\n\nTEST(MetricEnumToStringTest, TcpEnumToString)\n{\n  std::string_view metric_str = to_string(TcpMetrics::active);\n  EXPECT_STREQ(metric_str.data(), \"tcp.active\");\n  metric_str = to_string(TcpMetrics::all);\n  EXPECT_STREQ(metric_str.data(), \"tcp.all\");\n  metric_str = to_string(TcpMetrics::bytes);\n  EXPECT_STREQ(metric_str.data(), \"tcp.bytes\");\n  metric_str = to_string(TcpMetrics::new_sockets);\n  EXPECT_STREQ(metric_str.data(), \"tcp.new_sockets\");\n  metric_str = to_string(TcpMetrics::packets);\n  EXPECT_STREQ(metric_str.data(), \"tcp.packets\");\n  metric_str = to_string(TcpMetrics::resets);\n  EXPECT_STREQ(metric_str.data(), \"tcp.resets\");\n  metric_str = to_string(TcpMetrics::retrans);\n  EXPECT_STREQ(metric_str.data(), \"tcp.retrans\");\n  metric_str = to_string(TcpMetrics::rtt_average);\n  EXPECT_STREQ(metric_str.data(), \"tcp.rtt.average\");\n  metric_str = to_string(TcpMetrics::rtt_num_measurements);\n  EXPECT_STREQ(metric_str.data(), \"tcp.rtt.num_measurements\");\n  metric_str = to_string(TcpMetrics::syn_timeouts);\n  EXPECT_STREQ(metric_str.data(), \"tcp.syn_timeouts\");\n  metric_str = to_string(TcpMetrics::unknown);\n  EXPECT_STREQ(metric_str.data(), \"unknown\");\n}\n\nTEST(MetricEnumToStringTest, UdpEnumToString)\n{\n  std::string_view metric_str = to_string(UdpMetrics::active);\n  EXPECT_STREQ(metric_str.data(), \"udp.active\");\n  metric_str = to_string(UdpMetrics::all);\n  EXPECT_STREQ(metric_str.data(), \"udp.all\");\n  metric_str = to_string(UdpMetrics::bytes);\n  EXPECT_STREQ(metric_str.data(), \"udp.bytes\");\n  metric_str = to_string(UdpMetrics::drops);\n  EXPECT_STREQ(metric_str.data(), \"udp.drops\");\n  metric_str = to_string(UdpMetrics::packets);\n  EXPECT_STREQ(metric_str.data(), \"udp.packets\");\n  metric_str = to_string(UdpMetrics::unknown);\n  EXPECT_STREQ(metric_str.data(), \"unknown\");\n}\n\nTEST(MetricEnumToStringTest, DnsEnumToString)\n{\n  std::string_view metric_str = to_string(DnsMetrics::active_sockets);\n  EXPECT_STREQ(metric_str.data(), \"dns.active_sockets\");\n  metric_str = to_string(DnsMetrics::all);\n  EXPECT_STREQ(metric_str.data(), \"dns.all\");\n  metric_str = to_string(DnsMetrics::client_duration_average);\n  EXPECT_STREQ(metric_str.data(), \"dns.client.duration.average\");\n  metric_str = to_string(DnsMetrics::responses);\n  EXPECT_STREQ(metric_str.data(), \"dns.responses\");\n  metric_str = to_string(DnsMetrics::server_duration_average);\n  EXPECT_STREQ(metric_str.data(), \"dns.server.duration.average\");\n  metric_str = to_string(DnsMetrics::timeouts);\n  EXPECT_STREQ(metric_str.data(), \"dns.timeouts\");\n  metric_str = to_string(DnsMetrics::unknown);\n  EXPECT_STREQ(metric_str.data(), \"unknown\");\n}\n\nTEST(MetricEnumToStringTest, HttpEnumToString)\n{\n  std::string_view metric_str = to_string(HttpMetrics::active_sockets);\n  EXPECT_STREQ(metric_str.data(), \"http.active_sockets\");\n  metric_str = to_string(HttpMetrics::all);\n  EXPECT_STREQ(metric_str.data(), \"http.all\");\n  metric_str = to_string(HttpMetrics::client_duration_average);\n  EXPECT_STREQ(metric_str.data(), \"http.client.duration.average\");\n  metric_str = to_string(HttpMetrics::server_duration_average);\n  EXPECT_STREQ(metric_str.data(), \"http.server.duration.average\");\n  metric_str = to_string(HttpMetrics::status_code);\n  EXPECT_STREQ(metric_str.data(), \"http.status_code\");\n  metric_str = to_string(HttpMetrics::unknown);\n  EXPECT_STREQ(metric_str.data(), \"unknown\");\n}\n\nTEST(StringToMetricEnumTest, StringToTcpEnum)\n{\n  EXPECT_EQ(try_enum_from_string(\"tcp.active\", TcpMetrics::unknown), TcpMetrics::active);\n  EXPECT_EQ(try_enum_from_string(\"tcp.all\", TcpMetrics::unknown), TcpMetrics::all);\n  EXPECT_EQ(try_enum_from_string(\"tcp.bytes\", TcpMetrics::unknown), TcpMetrics::bytes);\n  EXPECT_EQ(try_enum_from_string(\"tcp.new_sockets\", TcpMetrics::unknown), TcpMetrics::new_sockets);\n  EXPECT_EQ(try_enum_from_string(\"tcp.packets\", TcpMetrics::unknown), TcpMetrics::packets);\n  EXPECT_EQ(try_enum_from_string(\"tcp.resets\", TcpMetrics::unknown), TcpMetrics::resets);\n  EXPECT_EQ(try_enum_from_string(\"tcp.retrans\", TcpMetrics::unknown), TcpMetrics::retrans);\n  EXPECT_EQ(try_enum_from_string(\"tcp.rtt.average\", TcpMetrics::unknown), TcpMetrics::rtt_average);\n  EXPECT_EQ(try_enum_from_string(\"tcp.rtt.num_measurements\", TcpMetrics::unknown), TcpMetrics::rtt_num_measurements);\n  EXPECT_EQ(try_enum_from_string(\"tcp.syn_timeouts\", TcpMetrics::unknown), TcpMetrics::syn_timeouts);\n\n  EXPECT_EQ(try_enum_from_string(\"G4RBAgE!?\", TcpMetrics::unknown), TcpMetrics::unknown);\n}\n\nTEST(StringToMetricEnumTest, StringToUdpEnum)\n{\n  EXPECT_EQ(try_enum_from_string(\"udp.active\", UdpMetrics::unknown), UdpMetrics::active);\n  EXPECT_EQ(try_enum_from_string(\"udp.all\", UdpMetrics::unknown), UdpMetrics::all);\n  EXPECT_EQ(try_enum_from_string(\"udp.bytes\", UdpMetrics::unknown), UdpMetrics::bytes);\n  EXPECT_EQ(try_enum_from_string(\"udp.drops\", UdpMetrics::unknown), UdpMetrics::drops);\n  EXPECT_EQ(try_enum_from_string(\"udp.packets\", UdpMetrics::unknown), UdpMetrics::packets);\n\n  EXPECT_EQ(try_enum_from_string(\"G4RBAgE!?\", UdpMetrics::unknown), UdpMetrics::unknown);\n}\n\nTEST(StringToMetricEnumTest, StringToDnsEnum)\n{\n  EXPECT_EQ(try_enum_from_string(\"dns.active_sockets\", DnsMetrics::unknown), DnsMetrics::active_sockets);\n  EXPECT_EQ(try_enum_from_string(\"dns.all\", DnsMetrics::unknown), DnsMetrics::all);\n  EXPECT_EQ(try_enum_from_string(\"dns.client.duration.average\", DnsMetrics::unknown), DnsMetrics::client_duration_average);\n  EXPECT_EQ(try_enum_from_string(\"dns.responses\", DnsMetrics::unknown), DnsMetrics::responses);\n  EXPECT_EQ(try_enum_from_string(\"dns.server.duration.average\", DnsMetrics::unknown), DnsMetrics::server_duration_average);\n  EXPECT_EQ(try_enum_from_string(\"dns.timeouts\", DnsMetrics::unknown), DnsMetrics::timeouts);\n\n  EXPECT_EQ(try_enum_from_string(\"G4RBAgE!?\", DnsMetrics::unknown), DnsMetrics::unknown);\n}\n\nTEST(StringToMetricEnumTest, StringToHttpEnum)\n{\n  EXPECT_EQ(try_enum_from_string(\"http.active_sockets\", HttpMetrics::unknown), HttpMetrics::active_sockets);\n  EXPECT_EQ(try_enum_from_string(\"http.all\", HttpMetrics::unknown), HttpMetrics::all);\n  EXPECT_EQ(try_enum_from_string(\"http.client.duration.average\", HttpMetrics::unknown), HttpMetrics::client_duration_average);\n  EXPECT_EQ(try_enum_from_string(\"http.server.duration.average\", HttpMetrics::unknown), HttpMetrics::server_duration_average);\n  EXPECT_EQ(try_enum_from_string(\"http.status_code\", HttpMetrics::unknown), HttpMetrics::status_code);\n\n  EXPECT_EQ(try_enum_from_string(\"G4RBAgE!?\", HttpMetrics::unknown), HttpMetrics::unknown);\n}\n\n}; // namespace reducer\n"
  },
  {
    "path": "reducer/dns_cache.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/keys.h>\n#include <netinet/in.h>\n#include <platform/platform.h>\n#include <util/LRU.h>\n#include <util/ip_address.h>\n\nnamespace reducer {\n\n/* forward declarations */\nnamespace dns {\nstruct hash_ipv6_address;\nstruct eq_ipv6_address;\nstatic constexpr u32 max_len = 256;\ntypedef short_string<max_len> dns_record;\n} // namespace dns\n\ntemplate <std::size_t ELEM_POOL_SZ>\nclass DnsCache : public LRU<IPv6Address, dns::dns_record, ELEM_POOL_SZ, dns::hash_ipv6_address, dns::eq_ipv6_address> {\npublic:\n  using dns_record = dns::dns_record;\n};\n\nnamespace dns {\n\nstruct hash_ipv6_address {\n  typedef IPv6Address argument_type;\n  typedef std::size_t result_type;\n  result_type operator()(IPv6Address const &addr) const noexcept\n  {\n    std::array<uint32_t, 4> addr32;\n    addr.write_to(&addr32);\n\n    return (result_type)fp_jhash_nwords(addr32[3], addr32[2], addr32[1], addr32[0]);\n  }\n};\n\n/* equality operator for ipv6 addresses */\nstruct eq_ipv6_address {\n  bool operator()(const IPv6Address &lhs, const IPv6Address &rhs) const { return lhs == rhs; }\n};\n\n} /* namespace dns */\n\n} /* namespace reducer */\n"
  },
  {
    "path": "reducer/entrypoint.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include <channel/component.h>\n#include <common/client_type.h>\n#include <reducer/constants.h>\n#include <reducer/ingest/component.h>\n#include <reducer/matching/component.h>\n#include <reducer/reducer.h>\n#include <reducer/reducer_config.h>\n#include <util/log.h>\n#include <util/log_whitelist.h>\n#include <util/signal_handler.h>\n\n#include <algorithm>\n#include <cctype>\n#include <reducer_entrypoint_cxxbridge.h>\n\nnamespace {\n\n// Map cxx bridge TsdbFormat to native\nstatic inline reducer::TsdbFormat map_tsdb_format(reducer_cfg::TsdbFormat f)\n{\n  switch (f) {\n  case reducer_cfg::TsdbFormat::prometheus:\n    return reducer::TsdbFormat::prometheus;\n  case reducer_cfg::TsdbFormat::json:\n    return reducer::TsdbFormat::json;\n  case reducer_cfg::TsdbFormat::otlp_grpc:\n    return reducer::TsdbFormat::otlp_grpc;\n  }\n  return reducer::TsdbFormat::prometheus;\n}\n\n// Trim helper\nstatic inline std::string trim(std::string s)\n{\n  auto not_space = [](int ch) { return !std::isspace(ch); };\n  s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));\n  s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());\n  return s;\n}\n\ntemplate <typename Enum> bool parse_and_set_whitelist(std::string const &value)\n{\n  if (value.empty()) {\n    return true;\n  }\n  // '*' => enable all for this whitelist\n  if (value == \"*\") {\n    log_whitelist_all<Enum>();\n    return true;\n  }\n\n  std::list<Enum> enums;\n  std::list<std::string> errors;\n  std::size_t start = 0;\n  while (start <= value.size()) {\n    std::size_t comma = value.find(',', start);\n    auto token = trim(value.substr(start, comma == std::string::npos ? std::string::npos : comma - start));\n    if (!token.empty()) {\n      Enum e{};\n      if (enum_from_string(token, e)) {\n        enums.push_back(e);\n      } else {\n        errors.push_back(token);\n      }\n    }\n    if (comma == std::string::npos) {\n      break;\n    }\n    start = comma + 1;\n  }\n\n  if (!errors.empty()) {\n    std::string err;\n    for (auto const &t : errors) {\n      if (!err.empty())\n        err += \",\";\n      err += t;\n    }\n    LOG::error(\"Unable to enable requested logs for whitelist: {}\", err);\n    return false;\n  }\n\n  if (!enums.empty()) {\n    set_log_whitelist<Enum>(std::move(enums));\n  }\n  return true;\n}\n\nstatic reducer::ReducerConfig from_ffi(reducer_cfg::ReducerConfig const &in)\n{\n  reducer::ReducerConfig out;\n  out.telemetry_port = in.telemetry_port;\n\n  out.num_ingest_shards = in.num_ingest_shards;\n  out.num_matching_shards = in.num_matching_shards;\n  out.num_aggregation_shards = in.num_aggregation_shards;\n  out.partitions_per_shard = in.partitions_per_shard;\n\n  out.enable_id_id = in.enable_id_id;\n  out.enable_az_id = in.enable_az_id;\n  out.enable_flow_logs = in.enable_flow_logs;\n\n  out.enable_otlp_grpc_metrics = in.enable_otlp_grpc_metrics;\n  out.otlp_grpc_metrics_address = std::string(in.otlp_grpc_metrics_address);\n  out.otlp_grpc_metrics_port = in.otlp_grpc_metrics_port;\n  out.otlp_grpc_batch_size = in.otlp_grpc_batch_size;\n  out.enable_otlp_grpc_metric_descriptions = in.enable_otlp_grpc_metric_descriptions;\n\n  out.disable_prometheus_metrics = in.disable_prometheus_metrics;\n  out.shard_prometheus_metrics = in.shard_prometheus_metrics;\n  out.prom_bind = std::string(in.prom_bind);\n  if (in.scrape_size_limit_bytes == 0) {\n    out.scrape_size_limit_bytes = std::nullopt;\n  } else {\n    out.scrape_size_limit_bytes = in.scrape_size_limit_bytes;\n  }\n  out.internal_prom_bind = std::string(in.internal_prom_bind);\n  if (in.stats_scrape_size_limit_bytes == 0) {\n    out.stats_scrape_size_limit_bytes = std::nullopt;\n  } else {\n    out.stats_scrape_size_limit_bytes = in.stats_scrape_size_limit_bytes;\n  }\n  out.scrape_metrics_tsdb_format = map_tsdb_format(in.scrape_metrics_tsdb_format);\n\n  out.disable_node_ip_field = in.disable_node_ip_field;\n  out.enable_autonomous_system_ip = in.enable_autonomous_system_ip;\n\n  if (!in.geoip_path.empty()) {\n    out.geoip_path = std::string(in.geoip_path);\n  } else {\n    out.geoip_path = std::nullopt;\n  }\n\n  out.enable_aws_enrichment = in.enable_aws_enrichment;\n  out.enable_percentile_latencies = in.enable_percentile_latencies;\n\n  out.disable_metrics = std::string(in.disable_metrics);\n  out.enable_metrics = std::string(in.enable_metrics);\n\n  out.index_dump_interval = in.index_dump_interval;\n\n  return out;\n}\n\n} // namespace\n\n// Thin C++ entrypoint: accepts final config from Rust and runs the reducer.\nnamespace reducer_cfg {\nint otn_reducer_main_with_config(reducer_cfg::ReducerConfig const &cfg)\n{\n  uv_loop_t loop;\n  CHECK_UV(uv_loop_init(&loop));\n\n  SignalManager signal_manager(loop, \"reducer\");\n  // Initialize minimal signal handler behavior (ignore SIGPIPE, disable core dumps)\n  signal_manager.handle();\n\n  // Apply logging whitelist configuration\n  if (cfg.log_whitelist_all) {\n    log_whitelist_all_globally();\n  }\n  (void)parse_and_set_whitelist<ClientType>(std::string(cfg.log_whitelist_client_type));\n  (void)parse_and_set_whitelist<NodeResolutionType>(std::string(cfg.log_whitelist_node_resolution_type));\n  (void)parse_and_set_whitelist<channel::Component>(std::string(cfg.log_whitelist_channel));\n  (void)parse_and_set_whitelist<reducer::ingest::Component>(std::string(cfg.log_whitelist_ingest));\n  (void)parse_and_set_whitelist<reducer::matching::Component>(std::string(cfg.log_whitelist_matching));\n\n  // Convert config\n  reducer::ReducerConfig config = from_ffi(cfg);\n\n  // Validate TSDB format for scraped metrics: only prometheus/json are supported here\n  if (config.scrape_metrics_tsdb_format != reducer::TsdbFormat::prometheus &&\n      config.scrape_metrics_tsdb_format != reducer::TsdbFormat::json) {\n    LOG::critical(\n        \"Invalid TSDB format for scraped metrics: {}. Supported formats: {}, {}\",\n        to_string(config.scrape_metrics_tsdb_format),\n        reducer::TsdbFormat::prometheus,\n        reducer::TsdbFormat::json);\n    return 1;\n  }\n\n  reducer::Reducer reducer(loop, config);\n  signal_manager.handle_signals({SIGINT, SIGTERM}, std::bind(&reducer::Reducer::shutdown, &reducer));\n  reducer.startup();\n\n  return 0;\n}\n\nvoid otn_init_logging(bool log_console, bool no_log_file)\n{\n  auto const log_file = std::string(LOG::log_file_path());\n  LOG::init(log_console, no_log_file ? nullptr : &log_file);\n}\n\nvoid otn_set_log_level(int level_code)\n{\n  switch (level_code) {\n  case 0:\n    spdlog::set_level(spdlog::level::trace);\n    break;\n  case 1:\n    spdlog::set_level(spdlog::level::debug);\n    break;\n  case 2:\n    spdlog::set_level(spdlog::level::info);\n    break;\n  case 3:\n    spdlog::set_level(spdlog::level::warn);\n    break;\n  case 4:\n    spdlog::set_level(spdlog::level::err);\n    break;\n  case 5:\n    spdlog::set_level(spdlog::level::critical);\n    break;\n  default:\n    break; // no-op on unknown\n  }\n}\n} // namespace reducer_cfg\n"
  },
  {
    "path": "reducer/entrypoint.h",
    "content": "#pragma once\nnamespace reducer_cfg {\n// forward declaration\nstruct ReducerConfig;\n\nint otn_reducer_main_with_config(ReducerConfig const &cfg);\n\n// Logging initialization and level control (called from Rust CLI)\nvoid otn_init_logging(bool log_console, bool no_log_file);\n// Level code mapping: 0=trace,1=debug,2=info,3=warning,4=error,5=critical\nvoid otn_set_log_level(int level_code);\n} // namespace reducer_cfg\n"
  },
  {
    "path": "reducer/entrypoint.sh",
    "content": "#!/bin/bash -x\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# shellcheck disable=SC1091\n[[ ! -e ./debug-info.conf ]] || source ./debug-info.conf\n\ninstall_dir=${EBPF_NET_INSTALL_DIR:-/srv}\nreducer=\"${install_dir}/opentelemetry-ebpf-reducer\"\n\ndata_dir=${EBPF_NET_DATA_DIR:-/var/run/ebpf_net}\ndump_dir=\"${data_dir}/dump\"\nmkdir -p \"${data_dir}\" \"${dump_dir}\"\n\nif [ -n \"$HEADLOG\" ]; then\n  \"${reducer}\" \"$@\" 2>&1 | sed -n '1,1000000p' > /tmp/logtmp\nelif [ -n \"${EBPF_NET_RUN_UNDER_GDB}\" ]; then\n  if [[ \"${#EBPF_NET_GDB_COMMANDS[@]}\" -lt 1 ]]; then\n    # default behavior is to run the pipeline server, print a stack trace after it exits\n    # and exit gdb without confirmation\n    EBPF_NET_GDB_COMMANDS=( \\\n      'set pagination off'\n      'handle SIGPIPE nostop pass'\n      'handle SIGUSR1 nostop pass'\n      'handle SIGUSR2 nostop pass'\n      run\n      bt\n      'server q'\n    )\n  fi\n\n  GDB_ARGS=()\n  for gdb_cmd in \"${EBPF_NET_GDB_COMMANDS[@]}\"; do\n    GDB_ARGS+=(-ex \"${gdb_cmd}\")\n  done\n\n  (set -x; exec \"${EBPF_NET_RUN_UNDER_GDB}\" -q \"${GDB_ARGS[@]}\" --args \"${reducer}\" \"$@\")\nelse\n  exec \"${reducer}\" \"$@\"\nfi\n"
  },
  {
    "path": "reducer/health_check.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nset -e\n\nfunction print_little_endian_bytes {\n  if [[ \"$#\" -ne 2 ]]; then exit 1; fi\n  number=\"$1\"; shift\n  byte_pad=\"$1\"; shift\n  padding_size=\"$((byte_pad * 2))\"\n  hex_number=\"$(echo \"obase=16;ibase=10;${number}\"|bc)\"\n  padded_hex_number=\"$(printf \"%${padding_size}s\" \"${hex_number}\" | tr ' ' 0)\"\n  little_endian_hex_number=\"$(echo \"${padded_hex_number}\" | rev | sed -e 's/\\(.\\)\\(.\\)/\\2\\1/g')\"\n  # bash regexes aren't cutting it so we go with sed\n  # shellcheck disable=SC2001\n  formatted_hex_number=\"$(echo \"${little_endian_hex_number}\" | sed -e 's/../\\\\x&/g')\"\n  # string contains format directives for printf so don't use \"%s\"\n  # shellcheck disable=SC2059\n  printf \"${formatted_hex_number}\"\n}\n\nfunction build_health_check_packet {\n  fixed_message_size=5\n\n  if [[ \"$#\" -ne 2 ]]; then exit 1; fi\n  client_type=\"$1\"; shift\n  location=\"$1\"; shift\n  location_size=\"${#location}\"\n\n  print_little_endian_bytes \"$(date +%s%N)\" 8\n  print_little_endian_bytes 409 2 # health check rpc id\n  print_little_endian_bytes \"$((fixed_message_size + location_size))\" 2\n  print_little_endian_bytes \"${client_type}\" 1\n  echo -n \"${location}\"\n}\n\nif [[ \"$#\" -ne 3 ]]; then exit 1; fi\n\ncase \"$1\" in\n  liveness_probe)\n    client_type=7\n    ;;\n  readiness_probe)\n    client_type=8\n    ;;\n  *)\n    echo \"unsupported client type: '$1'\"\n    exit 1\n    ;;\nesac\n\nlocation=\"$2\"\nserver_port=\"$3\"\n\nbuild_health_check_packet \"${client_type}\" \"${location}\" \\\n  | nc -q 1 localhost \"${server_port}\"\n"
  },
  {
    "path": "reducer/ingest/agent_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/ingest/agent_span.h>\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/uid_key.h>\n\n#include <common/kernel_headers_source.h>\n#include <common/linux_distro.h>\n#include <common/operating_system.h>\n\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/modifiers.h>\n#include <generated/ebpf_net/ingest/spans.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <util/environment_variables.h>\n#include <util/ip_address.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/lookup3.h>\n#include <util/short_string.h>\n\n#include <config.h>\n\n#include <chrono>\n#include <cstdlib>\n#include <iostream>\n#include <random>\n#include <regex>\n#include <sstream>\n#include <tuple>\n\nnamespace reducer::ingest {\n\nstd::atomic<u64> AgentSpan::agent_counter(0);\n\nnamespace {\n\nu64 create_random_agent_id()\n{\n  static thread_local auto *const rng = new std::mt19937_64(std::random_device()());\n  return std::uniform_int_distribution<u64>()(*rng);\n}\n\n} // namespace\n\nAgentSpan::AgentSpan()\n    : agent_id_(create_random_agent_id()),\n      hostname_(kUnknown),\n      namespace_(kUnknown),\n      cluster_(kUnknown),\n      node_id_(\"unknown-\" + std::to_string(++agent_counter)),\n      node_az_(kUnknown),\n      node_role_(kUnknown),\n      instance_type_(kUnknown)\n{}\n\nAgentSpan::~AgentSpan()\n{\n  // TODO: now that the local index is thread local, this clean up must be done\n  // in the agent thread\n  //       as it stands, this destructor is broken and will fail assertion on\n  //       core destruction\n\n  /* clean up all the k8s_pod handles before clearing the map */\n  for (auto &it : k8s_pods_) {\n    it.second.put(*local_index());\n  }\n\n  auto &addr_map = global_private_to_public_address_map();\n  for (auto private_addr : private_mapped_addrs_) {\n    addr_map.erase(private_addr);\n  }\n}\n\nvoid AgentSpan::process_steady_state(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__process_steady_state *msg)\n{\n  LOG::debug(\"--------PROCESS STEADY STATE---------\");\n}\n\nvoid AgentSpan::socket_steady_state(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__socket_steady_state *msg)\n{\n  LOG::debug(\"--------SOCKET STEADY STATE---------\");\n  is_socket_steady_state_ = true;\n}\n\nvoid AgentSpan::version_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__version_info *msg)\n{\n  version_.set(msg->major, msg->minor, msg->patch);\n\n  LOG::info(\"agent version: {}\", version_);\n\n  if (version_ < versions::client::MINIMUM_ACCEPTED_VERSION) {\n    throw std::runtime_error(\"version is lower than minimum\");\n  }\n}\n\nvoid AgentSpan::cloud_platform(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__cloud_platform *msg)\n{\n  cloud_platform_ = sanitize_enum(static_cast<CloudPlatform>(msg->cloud_platform));\n}\n\nvoid AgentSpan::cloud_platform_account_info(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__cloud_platform_account_info *msg)\n{\n  cloud_platform_account_id_ = msg->account_id;\n}\n\nvoid AgentSpan::collector_health(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__collector_health *msg)\n{\n  status_ = sanitize_enum(static_cast<::collector::CollectorStatus>(msg->status));\n  status_detail_ = msg->detail;\n}\n\nvoid AgentSpan::system_wide_process_settings(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__system_wide_process_settings *msg)\n{\n  LOG::trace_in(\n      Component::agent,\n      \"agent_span::system_wide_process_settings\"\n      \" clock_ticks_per_second={}\"\n      \" memory_page_bytes={}\",\n      msg->clock_ticks_per_second,\n      msg->memory_page_bytes);\n  clock_ticks_per_second_ = msg->clock_ticks_per_second;\n  memory_page_bytes_ = msg->memory_page_bytes;\n}\n\nvoid AgentSpan::report_cpu_cores(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__report_cpu_cores *msg)\n{\n  cpu_cores_ = msg->cpu_core_count;\n}\n\nvoid AgentSpan::collect_blob(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__collect_blob *msg)\n{\n  blob_collector_.collect(msg->blob_type, msg->subtype, msg->metadata, msg->blob);\n}\n\nvoid AgentSpan::set_node_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_node_info *msg)\n{\n  // node az, role, id, namespace, and cluster are pre-set to unknown on construction as a fallback when resolution fails\n\n  if (!namespace_override_.empty()) {\n    // use namespace label override if available\n    namespace_ = namespace_override_;\n  }\n\n  if (!cluster_override_.empty()) {\n    // use cluster label override if available\n    cluster_ = cluster_override_;\n  }\n\n  if (!zone_override_.empty()) {\n    // use zone label override if available\n    node_az_ = zone_override_;\n  } else if (auto const az = msg->az.string_view(); !az.empty()) {\n    // otherwise use given az if available\n    node_az_.assign(az.data(), az.size());\n  }\n\n  if (!service_override_.empty()) {\n    // use service label override if available\n    node_role_ = service_override_;\n  } else if (auto const role = msg->role.string_view(); !role.empty()) {\n    // otherwise use given role if available\n    node_role_.assign(role.data(), role.size());\n  }\n\n  if (!host_override_.empty()) {\n    // use host label override if available\n    node_id_ = host_override_;\n  } else if (hostname_ != kUnknown) {\n    // otherwise use hostname if available\n    node_id_ = hostname_;\n  } else if (auto const id = msg->instance_id.string_view(); !id.empty()) {\n    // otherwise use given id if available\n    node_id_.assign(id.data(), id.size());\n  }\n\n  if (auto const instance_type = msg->instance_type.string_view(); !instance_type.empty()) {\n    instance_type_.assign(instance_type.data(), instance_type.size());\n  }\n}\n\nvoid AgentSpan::set_config_label(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_config_label *msg)\n{\n  auto const key = msg->key.string_view();\n  auto const value = msg->value.string_view();\n\n  LOG::trace_in(Component::agent, \"custom config label: '{}'='{}'\", key, value);\n\n  if (key == \"namespace\") {\n    LOG::trace_in(Component::agent, \"\\\"namespace\\\" override detected!\");\n    namespace_override_.assign(value.data(), value.size());\n  }\n  if (key == \"environment\" || key == \"cluster\") {\n    LOG::trace_in(Component::agent, \"\\\"cluster\\\" override detected!\");\n    cluster_override_.assign(value.data(), value.size());\n  }\n  if (key == \"service\") {\n    LOG::trace_in(Component::agent, \"\\\"service\\\" override detected!\");\n    service_override_.assign(value.data(), value.size());\n  }\n  if (key == \"host\") {\n    LOG::trace_in(Component::agent, \"\\\"host\\\" override detected!\");\n    host_override_.assign(value.data(), value.size());\n  }\n  if (key == \"zone\") {\n    LOG::trace_in(Component::agent, \"\\\"zone\\\" override detected!\");\n    zone_override_.assign(value.data(), value.size());\n  }\n}\n\nvoid AgentSpan::set_availability_zone_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_availability_zone_deprecated *msg)\n{\n  std::string az;\n\n  if (!zone_override_.empty()) {\n    // always use zone label for az, if it was defined\n    az = zone_override_;\n  } else if (msg->retcode == 0) {\n    az.assign((char *)msg->az, strnlen((char *)msg->az, sizeof(msg->az)));\n    LOG::trace_in(Component::agent, \"AgentSpan::set_availability_zone_deprecated: az='{}'\", az);\n  } else {\n    LOG::trace_in(Component::agent, \"AgentSpan::set_availability_zone_deprecated: retcode={}\", msg->retcode);\n  }\n\n  if (!az.empty()) {\n    LOG::trace_in(Component::agent, \"Node az='{}', agent={}\", az, agent_id_);\n    node_az_ = az;\n  }\n}\n\nvoid AgentSpan::set_iam_role_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_iam_role_deprecated *msg)\n{\n  std::string role;\n\n  if (!service_override_.empty()) {\n    // always use service label for role, if it was defined\n    role = service_override_;\n  } else if (msg->retcode == 0) {\n    role.assign((char *)msg->role, strnlen((char *)msg->role, sizeof(msg->role)));\n    LOG::trace_in(Component::agent, \"AgentSpan::set_iam_role_deprecated: role='{}'\", role);\n  } else {\n    LOG::trace_in(Component::agent, \"AgentSpan::set_iam_role_deprecated: retcode={}\", msg->retcode);\n  }\n\n  if (!role.empty()) {\n    LOG::trace_in(Component::agent, \"Node role='{}', agent={}\", role, agent_id_);\n    node_role_ = role;\n  }\n}\n\nvoid AgentSpan::set_instance_id_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_instance_id_deprecated *msg)\n{\n  std::string id;\n\n  if (!host_override_.empty()) {\n    // always use host label for id, if it was defined\n    id = host_override_;\n  } else if (!hostname_.empty()) {\n    id = hostname_;\n  } else if (msg->retcode == 0) {\n    id = \"i-\";\n    id.append((char *)msg->id, strnlen((char *)msg->id, sizeof(msg->id)));\n    LOG::trace_in(Component::agent, \"AgentSpan::set_instance_id_deprecated: id='{}'\", id);\n  } else {\n    LOG::trace_in(Component::agent, \"AgentSpan::set_instance_id_deprecated: retcode={}\", msg->retcode);\n  }\n\n  if (!id.empty()) {\n    LOG::trace_in(Component::agent, \"Node id='{}', agent={}\", id, agent_id_);\n    node_id_ = id;\n  }\n}\n\nvoid AgentSpan::set_instance_type_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_instance_type_deprecated *msg)\n{\n  if (msg->retcode == 0) {\n    instance_type_.assign((char *)msg->val, strnlen((char *)msg->val, sizeof(msg->val)));\n  }\n}\n\nvoid AgentSpan::dns_response_fake(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__dns_response_fake *msg)\n{\n  in_addr *ipv4_addrs = (in_addr *)msg->ips.buf;\n  int num_ipv4_addrs = msg->ips.len / sizeof(in_addr);\n\n  map_ips_to_domain(ipv4_addrs, num_ipv4_addrs, nullptr, 0, {msg->domain_name.buf, msg->domain_name.len});\n}\n\nvoid AgentSpan::dns_response_dep_a_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__dns_response_dep_a_deprecated *msg)\n{\n  /* copy because we're not sure of message alignment */\n\n  int num_ipv4_addrs = msg->ipv4_addrs.len / sizeof(in_addr);\n  in_addr ipv4_addrs[num_ipv4_addrs];\n  memcpy(ipv4_addrs, msg->ipv4_addrs.buf, sizeof(in_addr) * num_ipv4_addrs);\n\n  int num_ipv6_addrs = msg->ipv6_addrs.len / sizeof(in6_addr);\n  in6_addr ipv6_addrs[num_ipv6_addrs];\n  memcpy(ipv6_addrs, msg->ipv6_addrs.buf, sizeof(in6_addr) * num_ipv6_addrs);\n\n  map_ips_to_domain(ipv4_addrs, num_ipv4_addrs, ipv6_addrs, num_ipv6_addrs, {msg->domain_name.buf, msg->domain_name.len});\n}\n\nvoid AgentSpan::map_ips_to_domain(\n    in_addr *ipv4_addrs, int num_ipv4_addrs, in6_addr *ipv6_addrs, int num_ipv6_addrs, std::string_view domain_name)\n{\n  static constexpr auto max_len = dns::dns_record::max_len;\n\n  char const *dn_buf = domain_name.data();\n  size_t dn_len = domain_name.length();\n\n  /* if record is too big, truncate the beginning */\n  if (dn_len > max_len) {\n    dn_buf += dn_len - max_len;\n    dn_len = max_len;\n  }\n\n  /* prepare DNS record */\n  dns::dns_record rec(dn_buf, dn_len);\n\n  /* prepare an IPv6-mapped IPv4 address */\n  struct in6_addr addr = {};\n  addr.s6_addr16[5] = 0xffff;\n\n  /* add LRU for each of the reported IP addresses */\n\n  for (int i = 0; i < num_ipv4_addrs; ++i) {\n    addr.s6_addr32[3] = ipv4_addrs[i].s_addr;\n    map_ip_to_domain(addr, rec);\n  }\n\n  for (int i = 0; i < num_ipv6_addrs; ++i) {\n    map_ip_to_domain(ipv6_addrs[i], rec);\n  }\n}\n\nvoid AgentSpan::map_ip_to_domain(in6_addr const &ip_addr, dns::dns_record const &rec)\n{\n  LOG::trace_in(\n      Component::agent, \"AgentSpan::map_ip_to_domain: ip_addr={}, rec={}\", IPv6Address::from(ip_addr), rec.to_string_view());\n\n  auto const addr = IPv6Address::from(ip_addr);\n\n  /* is the IP already in the LRU? */\n  auto *found = ip_to_domain_.find(addr);\n  if (found != nullptr) {\n    /* remove the old entry */\n    LOG::trace_in(Component::agent, \"reducer::AgentSpan::map_ip_to_domain - removing old entry\");\n    ip_to_domain_.remove(addr);\n  }\n\n  if (rec.len == 0) {\n    LOG::warn(\"attempting to insert an empty DNS record for ip {}\", addr);\n  }\n\n  /* insert */\n  auto *inserted = ip_to_domain_.insert(addr, rec);\n  if (inserted == nullptr) {\n    local_logger().failed_to_insert_dns_record();\n  }\n\n  LOG::trace_in(Component::agent, \"reducer::AgentSpan::map_ip_to_domain - LRU.size = {}\", ip_to_domain_.size());\n}\n\nvoid AgentSpan::set_config_label_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_config_label_deprecated *msg)\n{\n  jsrv_ingest__set_config_label message;\n  message.key = render_array_to_string_view(msg->key);\n  message.value = render_array_to_string_view(msg->val);\n  set_config_label(span_ref, timestamp, &message);\n}\n\nvoid AgentSpan::span_duration_info(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__span_duration_info *msg)\n{\n  // OBSOLETED\n}\n\nvoid AgentSpan::connect(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__connect *msg)\n{\n  auto const npm_connection = local_connection();\n  assert(npm_connection);\n\n  if (msg->hostname.len) {\n    hostname_.assign(msg->hostname.buf, msg->hostname.len);\n  }\n\n  type_ = sanitize_enum(static_cast<ClientType>(msg->collector_type));\n\n  npm_connection->set_client_info(hostname_, type_);\n\n  auto const connection = npm_connection->ingest_connection();\n  connection->on_connection_authenticated();\n}\n\nvoid AgentSpan::os_info_deprecated(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__os_info_deprecated *msg)\n{\n  auto const os = sanitize_enum(static_cast<OperatingSystem>(msg->os));\n  os_ = to_string(os);\n\n  switch (os) {\n  case OperatingSystem::Linux:\n    os_flavor_ = to_string(static_cast<LinuxDistro>(msg->flavor));\n    break;\n\n  default:\n    os_flavor_ = \"unknown\";\n    break;\n  }\n\n  if (!msg->kernel_version.empty()) {\n    kernel_version_ = msg->kernel_version.to_string();\n  }\n}\n\nvoid AgentSpan::os_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__os_info *msg)\n{\n  auto const os = sanitize_enum(static_cast<OperatingSystem>(msg->os));\n  os_ = to_string(os);\n\n  switch (os) {\n  case OperatingSystem::Linux:\n    os_flavor_ = to_string(static_cast<LinuxDistro>(msg->flavor));\n    break;\n\n  default:\n    os_flavor_ = \"unknown\";\n    break;\n  }\n\n  if (!msg->os_version.empty()) {\n    os_version_ = msg->os_version.to_string();\n  }\n\n  if (!msg->kernel_version.empty()) {\n    kernel_version_ = msg->kernel_version.to_string();\n  }\n}\n\nvoid AgentSpan::kernel_headers_source(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__kernel_headers_source *msg)\n{\n  kernel_headers_source_ = to_string(static_cast<KernelHeadersSource>(msg->source));\n}\n\nvoid AgentSpan::entrypoint_error(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__entrypoint_error *msg)\n{\n  LOG::error(\n      \"error while booting up client {} at {} running under OS {}/{}: {}\", type_, hostname_, os_, os_flavor_, msg->error);\n}\n\nvoid AgentSpan::health_check(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__health_check *msg)\n{\n  type_ = sanitize_enum(static_cast<ClientType>(msg->client_type));\n  if (msg->origin.len) {\n    hostname_.assign(msg->origin.buf, msg->origin.len);\n  }\n\n  LOG::trace_in(type_, \"AgentSpan::health_check from client {} at '{}'\", type_, hostname_);\n\n  auto const npm_connection = local_connection();\n  assert(npm_connection);\n  npm_connection->set_client_info(hostname_, type_);\n}\n\n// XXX: currently all {private/public}_ipv4 and ipv6 messages come with\n// a vpc_id, which gets ignored.\nvoid AgentSpan::private_ipv4_addr(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__private_ipv4_addr *msg)\n{\n  auto private_addr = IPv4Address::from(msg->addr).to_ipv6();\n  host_ips_.insert(private_addr);\n\n  // check that this ipv4 address was not used in a private-to-public mapping\n  auto &addr_map = global_private_to_public_address_map();\n  if (auto existing_public = addr_map.get(private_addr); existing_public) {\n    local_logger().private_ip_in_private_to_public_ip_mapping(jb_blob(private_addr.str()), jb_blob(existing_public->str()));\n  }\n}\n\nvoid AgentSpan::ipv6_addr(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__ipv6_addr *msg)\n{\n\n  auto private_addr = IPv6Address::from(msg->addr);\n  host_ips_.insert(private_addr);\n\n  // check that the ipv6 address is not used as a private address in the mapping\n  auto &addr_map = global_private_to_public_address_map();\n  if (auto existing_public = addr_map.get(private_addr); existing_public) {\n    local_logger().private_ip_in_private_to_public_ip_mapping(jb_blob(private_addr.str()), jb_blob(existing_public->str()));\n  }\n}\n\nvoid AgentSpan::public_to_private_ipv4(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__public_to_private_ipv4 *msg)\n{\n  auto public_addr = IPv4Address::from(msg->public_addr).to_ipv6();\n  auto private_addr = IPv4Address::from(msg->private_addr).to_ipv6();\n\n  LOG::trace_in(Component::agent, \"AgentSpan::public_to_private: public_addr={}, private_addr={}\", public_addr, private_addr);\n\n  host_ips_.insert(public_addr);\n  host_ips_.insert(private_addr);\n\n  private_mapped_addrs_.insert(private_addr);\n\n  auto &addr_map = global_private_to_public_address_map();\n\n  if (auto existing_public = addr_map.get(private_addr); existing_public && !(*existing_public == public_addr)) {\n    local_logger().rewriting_private_to_public_ip_mapping(\n        jb_blob(private_addr.str()), jb_blob(existing_public->str()), jb_blob(public_addr.str()));\n  }\n\n  addr_map.insert(private_addr, public_addr);\n}\n\nbool AgentSpan::is_host_address(IPv6Address const &addr) const\n{\n  return host_ips_.contains(addr);\n}\n\nvoid AgentSpan::metadata_complete(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__metadata_complete *msg)\n{\n  std::cout << \"--------METADATA COMPLETE---------\\n\";\n  std::cout << \"Agent info:\\n\";\n  std::cout << \"\\tVersion:               \" << version_ << '\\n';\n  std::cout << \"\\tOS:                    \" << os_ << \" (\" << os_flavor_ << \")\\n\";\n  std::cout << \"\\tKernel:                \" << kernel_version_ << '\\n';\n  std::cout << \"\\tCPU Cores:             \" << cpu_cores_ << '\\n';\n  std::cout << \"\\tHostname:              \" << hostname_ << '\\n';\n  std::cout << \"\\tCollector:             \" << type_ << '\\n';\n  if (type_ == ClientType::kernel) {\n    std::cout << \"\\tKernel Headers Source: \" << kernel_headers_source_ << '\\n';\n  }\n  std::cout << \"\\tEntrypoint Error:      \" << entrypoint_error_ << '\\n';\n  std::cout << \"\\tRole:                  \" << node_role_ << '\\n';\n  std::cout << \"\\tAZ:                    \" << node_az_ << '\\n';\n  std::cout << \"\\tId:                    \" << node_id_ << '\\n';\n  std::cout << \"\\tInstance:              \" << instance_type_ << '\\n';\n  std::cout << \"\\tAgent:                 \" << agent_id_ << '\\n';\n  std::cout << \"\\tOverrides:\\n\";\n  std::cout << \"\\t\\tnamespace:   \" << namespace_override_ << '\\n';\n  std::cout << \"\\t\\tcluster:     \" << cluster_override_ << '\\n';\n  std::cout << \"\\t\\tservice:     \" << service_override_ << '\\n';\n  std::cout << \"\\t\\thost:        \" << host_override_ << '\\n';\n  std::cout << \"\\t\\tzone:        \" << zone_override_ << '\\n';\n  std::cout << \"\\tIPs:\\n\";\n  for (auto &addr : host_ips_) {\n    std::cout << \"\\t\\t\" << addr << '\\n';\n  }\n  std::cout << \"Metadata Report Complete.\" << std::endl;\n}\n\nvoid AgentSpan::heartbeat(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__heartbeat *msg)\n{\n  // TODO: 1. add more descriptive debug message.\n  //       2. add functionality in reducer such that it disconnects if\n  //          heartbeat does not come back regularly.\n  LOG::trace_in(Component::heartbeat, \"Got a heartbeat from {} at '{}' (timestamp={})\", type_, hostname_, timestamp);\n}\n\nvoid AgentSpan::agent_resource_usage(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__agent_resource_usage *msg)\n{}\n\nvoid AgentSpan::bpf_lost_samples(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__bpf_lost_samples *msg)\n{\n  const auto conn = local_connection();\n\n  local_logger().agent_lost_events(msg->count, jb_blob(conn->client_hostname()));\n}\n\nvoid AgentSpan::set_pod_new(\n    std::string_view uid,\n    std::string_view owner_name,\n    std::string_view owner_uid,\n    std::string_view pod_name,\n    std::string_view ns,\n    std::string_view version,\n    uint32_t ip)\n{\n  ::ebpf_net::ingest::keys::k8s_pod k8s_pod_key;\n\n  // Get the key for k8s_pod\n  u64 uid_u64 = uid_to_u64(uid);\n  k8s_pod_key.uid_hash = uid_u64;\n  uid_suffix(uid, k8s_pod_key.uid_suffix.data(), k8s_pod_key.uid_suffix.max_size());\n\n  // get the k8s_pod span\n  auto k8s_pod = local_index()->k8s_pod.by_key(k8s_pod_key);\n  if (!k8s_pod.valid()) {\n    LOG::trace_in(Component::k8s_pod, \"no space for new k8s pod in AgentSpan::set_pod_new\");\n    return; // no space.\n  }\n\n  LOG::trace_in(\n      std::make_tuple(NodeResolutionType::K8S_CONTAINER, ClientType::k8s),\n      \"set_pod_new: uid {}, ip {:x}, uid_hash {}, owner_name {}, ns {}, version {}\",\n      uid,\n      ip,\n      uid_u64,\n      owner_name,\n      ns,\n      version);\n\n  // populate the pod span with pod metadata\n  k8s_pod.set_pod_detail(jb_blob(owner_name), jb_blob{pod_name}, jb_blob(ns), jb_blob(version), jb_blob(owner_uid));\n\n  // Add pod to k8s_pod_set if it isn't already there.\n\n  // TODO: remove the delete_k8s_pod below\n\n  // Occasionally the k8s collector will die and restart without sending\n  // a `pod_delete` message. We must proactively delete the previous `k8s_pod`\n  // handle otherwise the handle we attempt to insert below will be destroyed\n  // without a corresponding `put()`, which will result in a broken assertion\n  // in render-generated code.\n  delete_k8s_pod(uid_u64);\n\n  [[maybe_unused]] auto res = k8s_pods_.emplace(uid_u64, std::move(k8s_pod.to_handle()));\n\n  // Should not occur given the above check, but verify anyway.\n  assert(res.second);\n}\n\nvoid AgentSpan::pod_new_legacy(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_legacy *msg)\n{\n  set_pod_new(msg->uid, msg->owner_name, msg->owner_uid, {}, msg->ns, {}, msg->ip);\n}\n\nvoid AgentSpan::pod_new_legacy2(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_legacy2 *msg)\n{\n  set_pod_new(msg->uid, msg->owner_name, msg->owner_uid, {}, msg->ns, msg->version, msg->ip);\n}\n\nvoid AgentSpan::pod_new_with_name(\n    ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_with_name *msg)\n{\n  set_pod_new(msg->uid, msg->owner_name, msg->owner_uid, msg->pod_name, msg->ns, msg->version, msg->ip);\n}\n\nvoid AgentSpan::pod_container(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_container *msg)\n{\n  const std::string uid(msg->uid.buf, msg->uid.len);\n  const std::string container_id(msg->container_id.buf, msg->container_id.len);\n  const std::string container_name(msg->container_name.buf, msg->container_name.len);\n  const std::string container_image(msg->container_image.buf, msg->container_image.len);\n\n  LOG::trace_in(Component::k8s_pod, \"AgentSpan::pod_container: uid={} container_id={}\", uid, container_id);\n\n  // compute key for the owner k8s_pod\n  ::ebpf_net::ingest::keys::k8s_pod k8s_pod_key;\n  k8s_pod_key.uid_hash = uid_to_u64(uid);\n  uid_suffix(uid, k8s_pod_key.uid_suffix.data(), k8s_pod_key.uid_suffix.max_size());\n\n  // get the owning k8s_pod\n  auto k8s_pod = local_index()->k8s_pod.by_key(k8s_pod_key);\n  if (!k8s_pod.valid()) {\n    LOG::trace_in(Component::k8s_pod, \"AgentSpan::pod_container: failed to reference a pod (uid={})\", uid);\n    return;\n  }\n\n  // notify the owner pod's span of the new container\n  k8s_pod.impl().pod_container(k8s_pod, container_id, container_name, container_image);\n}\n\nvoid AgentSpan::pod_delete(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_delete *msg)\n{\n  const std::string uid(msg->uid.buf, msg->uid.len);\n\n  LOG::trace_in(std::make_tuple(NodeResolutionType::K8S_CONTAINER, ClientType::k8s), \"pod_delete: uid {}\", uid);\n\n  // When the message signifies removal of the pod, call put()\n  // on the handle and remove it from k8s_pod_set. This will\n  // clean up the endpoint and node handles\n  const u64 uid_u64 = uid_to_u64(uid);\n  if (!delete_k8s_pod(uid_u64)) {\n    local_logger().pod_not_found(jb_blob(uid), (u8) true);\n  }\n}\n\nvoid AgentSpan::pod_resync(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_resync *msg)\n{\n  const uint64_t resync_count = msg->resync_count;\n\n  LOG::trace_in(std::make_tuple(NodeResolutionType::K8S_CONTAINER, ClientType::k8s), \"Pod resync {}\", resync_count);\n\n  // put all the k8s pod handles that were added by this agent\n  for (auto &iter : k8s_pods_) {\n    iter.second.put(*local_index());\n  }\n\n  // clear the map of handles to get a clean slate for future updates\n  k8s_pods_.clear();\n}\n\nstd::optional<std::string_view> AgentSpan::find_dns_for_ip(const IPv6Address &addr)\n{\n  /* is the IP already in the LRU? */\n  auto *found = ip_to_domain_.find(addr);\n  if (found == nullptr) {\n    return std::nullopt;\n  }\n\n  return found->to_string_view();\n}\n\nbool AgentSpan::delete_k8s_pod(const uint64_t uid_u64)\n{\n  if (auto pod_it = k8s_pods_.find(uid_u64); pod_it != k8s_pods_.end()) {\n    // pod exists, delete it\n    pod_it->second.put(*local_index());\n    k8s_pods_.erase(pod_it);\n    return true;\n  } else {\n    // Doesn't exist.\n    return false;\n  }\n}\n\nvoid AgentSpan::log_message(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__log_message *msg)\n{\n  auto const level = static_cast<spdlog::level::level_enum>(msg->log_level);\n\n  switch (level) {\n  case spdlog::level::info: {\n    ++log_count_.info;\n    LOG::info(\"client log from {} collector {} at '{}': {}\", type_, version_, hostname_, msg->message.string_view());\n    break;\n  }\n\n  case spdlog::level::warn: {\n    ++log_count_.warning;\n    LOG::warn(\"client log from {} collector {} at '{}': {}\", type_, version_, hostname_, msg->message.string_view());\n    break;\n  }\n\n  case spdlog::level::err: {\n    ++log_count_.error;\n    LOG::error(\"client log from {} collector {} at '{}': {}\", type_, version_, hostname_, msg->message.string_view());\n    break;\n  }\n\n  case spdlog::level::critical: {\n    ++log_count_.critical;\n    LOG::critical(\"client log from {} collector {} at '{}': {}\", type_, version_, hostname_, msg->message.string_view());\n    break;\n  }\n\n  default: {\n    ++log_count_.ignored;\n    LOG::trace_in(type_, \"ignored log message with level={}: {}\", msg->log_level, msg->message.string_view());\n    break;\n  }\n  }\n}\n\nvoid AgentSpan::bpf_log(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__bpf_log *msg)\n{\n  bpf_logs_.emplace_back(bpf_log_entry{\n      .timestamp = std::chrono::nanoseconds{timestamp},\n      .filename = msg->filename.to_string(),\n      .line = msg->line,\n      .code = msg->code,\n      .arg0 = msg->arg0,\n      .arg1 = msg->arg1,\n      .arg2 = msg->arg2,\n  });\n}\n\nvoid AgentSpan::write_internal_stats(\n    ::ebpf_net::ingest::weak_refs::ingest_core_stats ingest_core_stats, u64 time_ns, int shard, std::string_view module)\n{\n  std::stringstream ss;\n  ss << version();\n  std::string version_as_string = ss.str();\n\n  ingest_core_stats.collector_health_stats(\n      jb_blob(module),\n      shard,\n      jb_blob(version_as_string),\n      jb_blob(std::to_string(integer_value(cloud_platform()))),\n      jb_blob(cluster()),\n      jb_blob(role()),\n      jb_blob(node_az()),\n      jb_blob(node_az()),\n      jb_blob(kernel_version()),\n      integer_value(client_type()),\n      jb_blob(hostname()),\n      jb_blob(os()),\n      jb_blob(os_version()),\n      time_ns,\n      jb_blob(std::to_string(integer_value(status_))),\n      jb_blob(std::to_string(status_detail_)));\n\n  for (auto const &data : bpf_logs_) {\n    ingest_core_stats.bpf_log_stats(\n        jb_blob(module),\n        shard,\n        jb_blob(version_as_string),\n        jb_blob(std::to_string(integer_value(cloud_platform()))),\n        jb_blob(cluster()),\n        jb_blob(role()),\n        jb_blob(node_az()),\n        jb_blob(node_id()),\n        jb_blob(kernel_version()),\n        integer_value(client_type()),\n        jb_blob(hostname()),\n        jb_blob(os()),\n        jb_blob(os_version()),\n        time_ns,\n        jb_blob(data.filename),\n        jb_blob(std::to_string(data.line)),\n        jb_blob(std::to_string(data.code)),\n        jb_blob(std::to_string(data.arg0)),\n        jb_blob(std::to_string(data.arg1)),\n        jb_blob(std::to_string(data.arg2)));\n  }\n\n  bpf_logs_.clear();\n}\n\nthread_local BlobCollector AgentSpan::blob_collector_;\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/agent_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/dns_cache.h>\n#include <reducer/util/blob_collector.h>\n\n#include <common/client_type.h>\n#include <common/cloud_platform.h>\n#include <common/collector_status.h>\n\n#include <generated/ebpf_net/ingest/auto_handles.h>\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/keys.h>\n#include <generated/ebpf_net/ingest/parsed_message.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n\n#include <platform/platform.h>\n\n#include <util/expected.h>\n#include <util/resource_usage.h>\n#include <util/time.h>\n#include <util/version.h>\n\n#include <absl/container/flat_hash_set.h>\n\n#include <chrono>\n#include <map>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\nnamespace reducer {\nclass InternalMetricsEncoder;\nclass SpanTest;\n} // namespace reducer\n\nnamespace reducer::ingest {\n\nclass AgentSpan : public ::ebpf_net::ingest::AgentSpanBase {\npublic:\n  /* DNS cache sizes */\n  static constexpr u32 dns_lru__pool_size = 100'000;\n\n  typedef DnsCache<dns_lru__pool_size> dns_cache_type;\n\n  AgentSpan(); // Auto-generates a random agent id.\n\n  ~AgentSpan();\n\n  /* handlers\n   */\n  void\n  process_steady_state(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__process_steady_state *msg);\n  void socket_steady_state(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__socket_steady_state *msg);\n  void version_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__version_info *msg);\n  void cloud_platform(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__cloud_platform *msg);\n  void cloud_platform_account_info(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__cloud_platform_account_info *msg);\n  void collector_health(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__collector_health *msg);\n  void system_wide_process_settings(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__system_wide_process_settings *msg);\n  void report_cpu_cores(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__report_cpu_cores *msg);\n  void collect_blob(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__collect_blob *msg);\n  void set_node_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_node_info *msg);\n  void set_config_label(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_config_label *msg);\n  void set_availability_zone_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_availability_zone_deprecated *msg);\n  void set_iam_role_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_iam_role_deprecated *msg);\n  void set_instance_id_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_instance_id_deprecated *msg);\n  void set_instance_type_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_instance_type_deprecated *msg);\n  void dns_response_fake(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__dns_response_fake *msg);\n  void dns_response_dep_a_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__dns_response_dep_a_deprecated *msg);\n  void set_config_label_deprecated(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__set_config_label_deprecated *msg);\n  void span_duration_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__span_duration_info *msg);\n  void connect(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__connect *msg);\n  void os_info_deprecated(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__os_info_deprecated *msg);\n  void os_info(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__os_info *msg);\n  void\n  kernel_headers_source(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__kernel_headers_source *msg);\n  void entrypoint_error(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__entrypoint_error *msg);\n  void health_check(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__health_check *msg);\n  void private_ipv4_addr(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__private_ipv4_addr *msg);\n  void ipv6_addr(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__ipv6_addr *msg);\n  void public_to_private_ipv4(\n      ::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__public_to_private_ipv4 *msg);\n  void metadata_complete(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__metadata_complete *msg);\n  void bpf_lost_samples(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__bpf_lost_samples *msg);\n  void heartbeat(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__heartbeat *msg);\n  void\n  agent_resource_usage(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__agent_resource_usage *msg);\n\n  // Handlers for arriving k8s-collector messages. These will be broadcast to\n  // every agent, and the actual business logic will be performed in the\n  // `pod_*_internal` messages below.\n  void pod_new_legacy2(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_legacy2 *msg);\n  void pod_new_legacy(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_legacy *msg);\n  void pod_new_with_name(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_new_with_name *msg);\n  void set_pod_new(\n      std::string_view uid,\n      std::string_view owner_name,\n      std::string_view owner_uid,\n      std::string_view pod_name,\n      std::string_view ns,\n      std::string_view version,\n      uint32_t ip);\n  void pod_container(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_container *msg);\n  void pod_delete(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_delete *msg);\n  void pod_resync(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__pod_resync *msg);\n\n  // Handlers for k8s-collector messages. These will be invoked via the visitor\n  // pattern on `TcpServer`.\n  void\n  pod_new_internal(std::string_view uid, std::string_view owner_name, std::string_view ns, uint32_t ip, bool is_host_network);\n  void pod_container_internal(uint64_t timestamp_ns, std::string_view uid, std::string_view container_id);\n  void pod_delete_internal(std::string_view uid);\n  void pod_resync_internal(uint64_t resync_count);\n\n  void log_message(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__log_message *msg);\n  void bpf_log(::ebpf_net::ingest::weak_refs::agent span_ref, u64 timestamp, jsrv_ingest__bpf_log *msg);\n\n  u64 agent_id() const { return agent_id_; }\n\n  VersionInfo const &version() const { return version_; }\n\n  std::string const &node_id() const { return node_id_; }\n  std::string const &node_az() const { return node_az_; }\n  std::string const &ns() const { return namespace_; }\n  std::string const &cluster() const { return cluster_; }\n  std::string const &role() const { return node_role_; }\n  std::string const &instance_type() const { return instance_type_; }\n\n  // Returns true if the Agent is in socket steady state.\n  bool is_socket_steady_state() const { return is_socket_steady_state_; }\n\n  struct LogCount {\n    u32 ignored = 0;\n    u32 info = 0;\n    u32 warning = 0;\n    u32 error = 0;\n    u32 critical = 0;\n  };\n\n  LogCount const &log_count() const { return log_count_; }\n\n  // Checks whether the supplied address is one of host's addresses.\n  bool is_host_address(IPv6Address const &addr) const;\n\n  // Finds most recent DNS query for the given IP\n  // @returns: a string_view to the domain name, if found. The string_view is\n  //   only valid temporarily (as long as no DNS messages are processed); user\n  //   should copy the contents if they are to be preserved.\n  std::optional<std::string_view> find_dns_for_ip(const IPv6Address &addr);\n\n  // Adds IP->domain mappings.\n  void map_ips_to_domain(\n      in_addr *ipv4_addrs, int num_ipv4_addrs, in6_addr *ipv6_addrs, int num_ipv6_addrs, std::string_view domain_name);\n\n  std::string const &hostname() const { return hostname_; }\n  ClientType client_type() const { return type_; }\n  std::string_view os() const { return os_; }\n  std::string_view os_flavor() const { return os_flavor_; }\n  std::string_view os_version() const { return os_version_; }\n  std::string_view kernel_version() const { return kernel_version_; }\n  std::string_view kernel_headers_source() const { return kernel_headers_source_; }\n  CloudPlatform cloud_platform() const { return cloud_platform_; }\n  std::string_view entrypoint_error() const { return entrypoint_error_; }\n  std::uint32_t cpu_cores() const { return cpu_cores_; }\n\n  std::string_view key_id() const { return key_id_; }\n\n  ::collector::CollectorStatus status() const { return status_; }\n  std::uint16_t status_detail() const { return status_detail_; }\n\n  void write_internal_stats(\n      ::ebpf_net::ingest::weak_refs::ingest_core_stats ingest_core_stats, u64 time_ns, int shard, std::string_view module);\n\n  std::uint64_t clock_ticks_per_second() const { return clock_ticks_per_second_; }\n  std::size_t memory_page_bytes() const { return memory_page_bytes_; }\n\nprivate:\n  friend class ::reducer::SpanTest;\n\n  static std::atomic<u64> agent_counter;\n  static const std::map<std::string, std::string> tenant_keys_;\n\n  const u64 agent_id_;\n\n  std::string hostname_;\n  ClientType type_ = ClientType::unknown;\n  std::string_view os_ = \"unknown\";\n  std::string_view os_flavor_ = \"unknown\";\n  std::string os_version_ = \"unknown\";\n  std::string kernel_version_ = \"unknown\";\n  std::string_view kernel_headers_source_ = \"unknown\";\n  CloudPlatform cloud_platform_ = CloudPlatform::unknown;\n  std::string_view entrypoint_error_;\n\n  std::string namespace_;\n  std::string cluster_;\n  std::string node_id_;\n  std::string node_az_;\n  std::string node_role_;\n\n  VersionInfo version_;\n\n  std::string instance_type_;\n  std::string namespace_override_;\n  std::string cluster_override_;\n  std::string service_override_;\n  std::string host_override_;\n  std::string zone_override_;\n\n  // Addresses belonging to the host on which the agent is running.\n  absl::flat_hash_set<IPv6Address> host_ips_;\n  // Host's private addresses that are mapped to public addresses.\n  absl::flat_hash_set<IPv6Address> private_mapped_addrs_;\n\n  // maps pod_uid_to_u64(<the uid string of a pod>) to a corresponding k8s_pod\n  // handle updated in: pod_new(): adds elements pod_delete(): removes elements\n  std::unordered_map<u64, ::ebpf_net::ingest::handles::k8s_pod> k8s_pods_;\n\n  dns_cache_type ip_to_domain_;\n\n  bool is_socket_steady_state_ = false;\n\n  ::collector::CollectorStatus status_ = ::collector::CollectorStatus::unknown;\n  std::uint16_t status_detail_ = 0;\n\n  std::string cloud_platform_account_id_;\n\n  void map_ip_to_domain(in6_addr const &ip_addr, dns::dns_record const &rec);\n\n  void maybe_disable_by_env_var();\n\n  // Deletes the pod identified by `uid_hash` from `k8s_pods_`. Returns true\n  // is the deletion was successful, otherwise returns false if there was\n  // nothing to delete.\n  bool delete_k8s_pod(uint64_t uid_hash);\n\n  LogCount log_count_;\n\n  std::uint64_t clock_ticks_per_second_ = {};\n  std::size_t memory_page_bytes_ = {};\n  std::uint32_t cpu_cores_ = 0;\n\n  std::string key_id_;\n\n  struct bpf_log_entry {\n    std::chrono::nanoseconds timestamp;\n    std::string filename;\n    u32 line;\n    u64 code;\n    u64 arg0;\n    u64 arg1;\n    u64 arg2;\n\n    template <typename Out> friend Out &&operator<<(Out &&out, bpf_log_entry const &what) {}\n  };\n\n  std::vector<bpf_log_entry> bpf_logs_;\n\n  static thread_local BlobCollector blob_collector_;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/aws_network_interface_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"aws_network_interface_span.h\"\n\n#include <reducer/constants.h>\n\n#include <util/log.h>\n\n#include <absl/strings/match.h>\n\nnamespace reducer::collector::cloud {\n\nconstexpr std::string_view amazon_owner_prefix = \"amazon-\";\nconstexpr std::string_view aws_nat_gateway_prefix = \"Interface for \";\n\nAwsNetworkInterfaceSpan::AwsNetworkInterfaceSpan() {}\n\nAwsNetworkInterfaceSpan::~AwsNetworkInterfaceSpan() {}\n\nvoid AwsNetworkInterfaceSpan::network_interface_info(\n    ::ebpf_net::ingest::weak_refs::aws_network_interface span_ref, u64 timestamp, jsrv_ingest__network_interface_info *msg)\n{\n  LOG::trace_in(\n      std::make_tuple(NodeResolutionType::AWS, ClientType::cloud),\n      \"ingest::AwsNetworkInterfaceSpan::network_interface_info: incoming\"\n      \" ip_owner_id={} vpc_id={} az={}\"\n      \" interface_id={} interface_type={} instance_id={} instance_owner_id={}\"\n      \" public_dns_name={} private_dns_name={} description='{}'\",\n      msg->ip_owner_id,\n      msg->vpc_id,\n      msg->az,\n      msg->interface_id,\n      msg->interface_type,\n      msg->instance_id,\n      msg->instance_owner_id,\n      msg->public_dns_name,\n      msg->private_dns_name,\n      msg->interface_description);\n\n  // AWS instances always have either \"Attachement.IpOwnerId\" or\n  // \"Attachment.InstanceOwnerId\" starting with \"amazon-aws\"\n  bool const owned_by_aws = msg->ip_owner_id.string_view().starts_with(amazon_owner_prefix) ||\n                            msg->instance_owner_id.string_view().starts_with(amazon_owner_prefix);\n\n  if (!owned_by_aws) {\n    LOG::trace_in(\n        std::make_tuple(NodeResolutionType::AWS, ClientType::cloud),\n        \"ingest::AwsNetworkInterfaceSpan::network_interface_info: not owned by \"\n        \"aws\"\n        \" ip_owner_id={} vpc_id={} az={}\"\n        \" interface_id={} interface_type={} instance_id={} instance_owner_id={}\"\n        \" public_dns_name={} private_dns_name={} description='{}'\",\n        msg->ip_owner_id,\n        msg->vpc_id,\n        msg->az,\n        msg->interface_id,\n        msg->interface_type,\n        msg->instance_id,\n        msg->instance_owner_id,\n        msg->public_dns_name,\n        msg->private_dns_name,\n        msg->interface_description);\n    return;\n  }\n\n  std::string_view aws_role = msg->interface_description;\n  auto const aws_az = msg->az.string_view();\n  auto const aws_id = msg->interface_id.string_view();\n\n  // cleans up nat gateway description\n  if (aws_role.starts_with(aws_nat_gateway_prefix)) {\n    assert(aws_role.size() >= aws_nat_gateway_prefix.size());\n    aws_role.remove_prefix(aws_nat_gateway_prefix.size());\n    LOG::trace_in(\n        std::make_tuple(NodeResolutionType::AWS, ClientType::cloud),\n        \"ingest::AwsNetworkInterfaceSpan::network_interface_info:\"\n        \" nat_gateway role truncated (prefix='{}' role='{}')\",\n        aws_nat_gateway_prefix,\n        aws_role);\n  } else {\n    LOG::trace_in(\n        std::make_tuple(NodeResolutionType::AWS, ClientType::cloud),\n        \"ingest::AwsNetworkInterfaceSpan::network_interface_info:\"\n        \" nat_gateway role doesn't need cleaning (prefix='{}' role='{}')\",\n        aws_nat_gateway_prefix,\n        aws_role);\n  }\n\n  bool const can_enrich = !aws_role.empty() && !aws_az.empty();\n\n  if (can_enrich) {\n    span_ref.aws_enrichment(jb_blob{aws_role}, jb_blob{aws_az}, jb_blob{aws_id});\n  } else {\n    LOG::warn(\n        \"ingest::AwsNetworkInterfaceSpan::network_interface_info:\"\n        \" refusing to enrich using AWS metadata:\"\n        \" ip_owner_id={} vpc_id={} az={}\"\n        \" interface_id={} interface_type={}\"\n        \" instance_id={} instance_owner_id={}\"\n        \" public_dns_name={} private_dns_name={} description='{}'\"\n        \" - enrichment[can={}]: role='{}' az='{}'\",\n        msg->ip_owner_id,\n        msg->vpc_id,\n        msg->az,\n        msg->interface_id,\n        static_cast<std::uint16_t>(msg->interface_type),\n        msg->instance_id,\n        msg->instance_owner_id,\n        msg->public_dns_name,\n        msg->private_dns_name,\n        msg->interface_description,\n        can_enrich,\n        aws_role,\n        aws_az);\n  }\n}\n\nvoid AwsNetworkInterfaceSpan::network_interface_info_deprecated(\n    ::ebpf_net::ingest::weak_refs::aws_network_interface span_ref,\n    u64 timestamp,\n    jsrv_ingest__network_interface_info_deprecated *msg)\n{\n  jsrv_ingest__network_interface_info message;\n  message.ip_owner_id = render_array_to_string_view(msg->ip_owner_id);\n  message.vpc_id = render_array_to_string_view(msg->vpc_id);\n  message.az = render_array_to_string_view(msg->az);\n  message.interface_id = msg->interface_id;\n  message.interface_type = msg->interface_type;\n  message.instance_id = msg->instance_id;\n  message.instance_owner_id = msg->instance_owner_id;\n  message.public_dns_name = msg->public_dns_name;\n  message.private_dns_name = msg->private_dns_name;\n  message.interface_description = msg->interface_description;\n  network_interface_info(span_ref, timestamp, &message);\n}\n\n} // namespace reducer::collector::cloud\n"
  },
  {
    "path": "reducer/ingest/aws_network_interface_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/span_base.h>\n\nnamespace reducer::collector::cloud {\n\nstruct AwsNetworkInterfaceSpan : ebpf_net::ingest::AwsNetworkInterfaceSpanBase {\n  AwsNetworkInterfaceSpan();\n  ~AwsNetworkInterfaceSpan();\n\n  void network_interface_info(\n      ::ebpf_net::ingest::weak_refs::aws_network_interface span_ref, u64 timestamp, jsrv_ingest__network_interface_info *msg);\n\n  void network_interface_info_deprecated(\n      ::ebpf_net::ingest::weak_refs::aws_network_interface span_ref,\n      u64 timestamp,\n      jsrv_ingest__network_interface_info_deprecated *msg);\n};\n\n} // namespace reducer::collector::cloud\n"
  },
  {
    "path": "reducer/ingest/cgroup_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"cgroup_span.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <reducer/constants.h>\n#include <reducer/uid_key.h>\n#include <reducer/util/docker_image.h>\n\n#include <common/constants.h>\n#include <common/port_protocol.h>\n\n#include <util/k8s_metadata.h>\n#include <util/log.h>\n#include <util/string_view.h>\n\n#include <generated/ebpf_net/ingest/containers.inl>\n#include <generated/ebpf_net/ingest/modifiers.h>\n#include <generated/ebpf_net/ingest/weak_refs.inl>\n\n#include <cstring>\n\nnamespace reducer::ingest {\n\nnamespace {\n\nconstexpr std::string_view UNKNOWN_JOB = \"<unknown>\";\nconstexpr std::string_view UNKNOWN_GROUP = \"<unknown>\";\n\nconstexpr std::string_view SYSTEMD_ROOT_CGROUP = \"system.slice\";\nconstexpr std::string_view SYSTEMD_SERVICE_SUFFIX = \".service\";\n\n} // namespace\n\nCgroupSpan::CgroupSpan() {}\n\nCgroupSpan::~CgroupSpan() {}\n\nvoid CgroupSpan::cgroup_create_deprecated(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_create_deprecated *msg)\n{\n  auto create_message = jsrv_ingest__cgroup_create{};\n  std::memcpy(create_message.name, msg->name, sizeof(msg->name));\n  create_message.cgroup = msg->cgroup;\n  create_message.cgroup_parent = msg->cgroup_parent;\n\n  // deprecated message has 64 bytes for name (instead of 256), otherwise the message is the same.\n  cgroup_create(span_ref, timestamp, &create_message);\n}\n\nvoid CgroupSpan::cgroup_create(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_create *msg)\n{\n  std::string_view name{(char const *)msg->name, strnlen((char const *)msg->name, sizeof(msg->name))};\n\n  LOG::trace_in(\n      Component::cgroup, \"CgroupSpan::cgroup_create cgroup={} cgroup_parent={} name={}\", msg->cgroup, msg->cgroup_parent, name);\n\n  // attempt to extract info from the cgroup.\n  CGroupParser parser{name};\n\n  set_pod(span_ref, parser);\n  set_parent(span_ref, msg->cgroup_parent);\n  set_service(span_ref, parser);\n  set_container(span_ref, parser);\n}\n\nvoid CgroupSpan::set_pod(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser)\n{\n  static constexpr size_t name_max_len = ::ebpf_net::ingest::modifiers::cgroup::name_t::max_len;\n  // See CGroupParser.  The pod id may be embedded somewhere in the name.\n  auto name = parser.cgroup_name();\n  auto info = parser.get();\n\n  // NOTE: if pod_uid is left empty, this will still do the right thing\n  u64 pod_uid_hash = uid_to_u64(info.pod_id);\n  std::array<u8, 64> pod_uid_suffix;\n  uid_suffix(info.pod_id, pod_uid_suffix.data(), pod_uid_suffix.max_size());\n\n  span_ref.modify()\n      .name({name.data(), std::min(name.size(), name_max_len)})\n      .pod_uid_hash(pod_uid_hash)\n      .pod_uid_suffix(pod_uid_suffix)\n      .cpu_soft_limit(kernel::MAX_CGROUP_CPU_SHARES)\n      .cpu_hard_quota(kernel::DEFAULT_CGROUP_QUOTA)\n      .cpu_hard_period(kernel::DEFAULT_CGROUP_QUOTA);\n}\n\nvoid CgroupSpan::set_parent(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 cgroup_parent)\n{\n  auto *conn = local_connection()->ingest_connection();\n\n  if (auto parent_ref = conn->get_cgroup(cgroup_parent); parent_ref.valid()) {\n    span_ref.modify().parent(parent_ref.get());\n  } else {\n    LOG::trace_in(Component::cgroup, \"CgroupSpan: unable to find parent cgroup\");\n  }\n}\n\nvoid CgroupSpan::set_container(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser)\n{\n  static constexpr size_t id_max_len = ::ebpf_net::ingest::modifiers::container::id_t::max_len;\n\n  auto name = parser.cgroup_name();\n  auto info = parser.get();\n\n  // mostly following the previous behavior, where it was always assigning the cgroup\n  // name as the container_id.  However, we do try and extract the container id if\n  // possible (for cgroups where it is embedded somewhere in the name).\n  std::string_view container_id = info.container_id.empty() ? name : info.container_id;\n\n  assert(!span_ref.container().valid());\n\n  ::ebpf_net::ingest::keys::container container_key;\n  container_key.id.set(container_id.data(), std::min(container_id.size(), id_max_len));\n\n  auto container = local_index()->container.by_key(container_key);\n  if (container.valid()) {\n    span_ref.modify().container(container.get());\n  } else {\n    LOG::trace_in(Component::cgroup, \"CgroupSpan: could not allocate a container span\");\n  }\n}\n\nvoid CgroupSpan::set_service(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser)\n{\n  auto parent = span_ref.parent();\n\n  if (!parent.valid()) {\n    return;\n  }\n\n  if (parent.service().valid()) {\n    // Inherit parent's service.\n    span_ref.modify().service(parent.service().get());\n    return;\n  }\n\n  // cgroups where the name ends in \".service\" (see CGroupParser::parse_service())\n  auto info = parser.get();\n  if ((parent.name() == SYSTEMD_ROOT_CGROUP) && !info.service.empty()) {\n    auto service = local_index()->service.by_key({{short_string_behavior::truncate, info.service}});\n\n    if (!service.valid()) {\n      LOG::error(\"CgroupSpan: could not allocate a service span\");\n      return;\n    }\n\n    span_ref.modify().service(std::move(service));\n  }\n}\n\nvoid CgroupSpan::cgroup_close(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_close *msg)\n{\n  LOG::trace_in(Component::cgroup, \"CgroupSpan::cgroup_close cgroup:{}\", msg->cgroup);\n}\n\nvoid CgroupSpan::container_metadata(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_metadata *msg)\n{\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::container_metadata cgroup:{} container_name: {}\",\n      msg->cgroup,\n      msg->container_name.string_view());\n\n  using role_t = ::ebpf_net::ingest::spans::container::role_t;\n  using version_t = ::ebpf_net::ingest::spans::container::version_t;\n  using ns_t = ::ebpf_net::ingest::spans::container::ns_t;\n  using name_t = ::ebpf_net::ingest::spans::container::name_t;\n\n  auto container = span_ref.container();\n  if (!container.valid()) {\n    return;\n  }\n\n  auto const container_name = msg->container_name.string_view();\n  container.modify().name({container_name.data(), std::min(container_name.size(), name_t::max_len)});\n\n  std::string_view role;\n  std::string_view version;\n\n  if (msg->task_family.len > 0) {\n    auto const task_family = msg->task_family.string_view();\n    auto const task_version = msg->task_version.string_view();\n\n    LOG::trace_in(Component::cgroup, \"CgroupSpan: using task family='{}', version='{}' for role\", task_family, task_version);\n\n    role = task_family;\n    version = task_version;\n  } else if (!k8s_container_name_.empty() && k8s_container_name_ != K8sMetadata::POD_CONTAINER_NAME_VALUE) {\n    role = k8s_container_name_;\n\n    DockerImageMetadata const image{msg->image.string_view()};\n    version = image.version();\n  } else if (auto name = msg->name.string_view(); !name.empty()) {\n    DockerImageMetadata const image{msg->image.string_view()};\n\n    LOG::trace_in(Component::cgroup, \"CgroupSpan: using name='{}', image='{}' for role\", name, msg->image);\n\n    if (name.front() == '/') {\n      // strip the leading slash\n      name.remove_prefix(1);\n    }\n\n    role = name;\n\n    version = image.version();\n  }\n\n  auto const ns = msg->ns.string_view();\n\n  container.modify()\n      .role({role.data(), std::min(role.size(), role_t::max_len)})\n      .version({version.data(), std::min(version.size(), version_t::max_len)})\n      .ns({ns.data(), std::min(ns.size(), ns_t::max_len)})\n      .node_type(integer_value(NodeResolutionType::CONTAINER));\n\n  on_container_updated(container);\n\n  if (!role.empty()) {\n    LOG::trace_in(Component::cgroup, \"CgroupSpan: role='{}', version='{}', ns='{}'\", role, version, ns);\n  }\n}\n\nvoid CgroupSpan::container_annotation(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_annotation *msg)\n{\n  if (msg->key == K8sMetadata::CONTAINER_NAME) {\n    k8s_container_name_ = msg->value.to_string();\n  } else {\n    annotations_[msg->key.to_string()] = msg->value.to_string();\n  }\n}\n\nvoid CgroupSpan::pod_name(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__pod_name *msg)\n{\n  auto container = span_ref.container();\n  if (!container.valid()) {\n    return;\n  }\n\n  container.modify().pod_name({short_string_behavior::truncate, msg->name});\n\n  on_container_updated(container);\n}\n\nvoid CgroupSpan::k8s_metadata(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__k8s_metadata *msg)\n{\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::k8s_metadata(cgroup=`{}` container_name=`{}` pod_name=`{}` pod_ns=`{}`\"\n      \" pod_uid=`{}` sandbox_uid=`{}`)\",\n      msg->cgroup,\n      msg->container_name,\n      msg->pod_name,\n      msg->pod_ns,\n      msg->pod_uid,\n      msg->sandbox_uid);\n\n  auto container = span_ref.container();\n  if (!container.valid()) {\n    return;\n  }\n\n  container.modify()\n      .ns({short_string_behavior::truncate, msg->pod_ns})\n      .pod_name({short_string_behavior::truncate, msg->pod_name});\n\n  on_container_updated(container);\n}\n\nvoid CgroupSpan::k8s_metadata_port(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__k8s_metadata_port *msg)\n{\n  auto const protocol = sanitize_enum(static_cast<PortProtocol>(msg->protocol));\n\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::k8s_metadata_port(cgroup=`{}` port=`{}` protocol=`{}` name=`{}`)\",\n      msg->cgroup,\n      msg->port,\n      protocol,\n      msg->name);\n\n  // TODO: consume port mappings\n}\n\nvoid CgroupSpan::nomad_metadata(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__nomad_metadata *msg)\n{\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::nomad_metadata(cgroup=`{}` ns=`{}` group=`{}` task=`{}` job=`{}`)\",\n      msg->cgroup,\n      msg->ns,\n      msg->group_name,\n      msg->task_name,\n      msg->job_name);\n\n  auto container = span_ref.container();\n  if (!container.valid()) {\n    return;\n  }\n\n  bool updated = false;\n\n  if (auto const ns = msg->ns.string_view(); !ns.empty()) {\n    using ns_t = ::ebpf_net::ingest::spans::container::ns_t;\n    container.modify().ns({ns.data(), std::min(ns.size(), ns_t::max_len)});\n    updated = true;\n  }\n\n  if (auto const task_name = msg->task_name.string_view(); !task_name.empty()) {\n    auto const job_name = msg->job_name.string_view();\n    auto const group_name = msg->group_name.string_view();\n    auto const role = fmt::format(\n        \"{}.{}.{}\", job_name.empty() ? UNKNOWN_JOB : job_name, group_name.empty() ? UNKNOWN_GROUP : group_name, task_name);\n\n    using role_t = ::ebpf_net::ingest::spans::container::role_t;\n    container.modify().role({role.data(), std::min(role.size(), role_t::max_len)});\n    updated = true;\n  }\n\n  if (updated) {\n    container.modify().node_type(integer_value(NodeResolutionType::NOMAD));\n    on_container_updated(container);\n  }\n}\n\nvoid CgroupSpan::container_resource_limits_deprecated(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_resource_limits_deprecated *msg)\n{\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::container_resource_limits_deprecated: cgroup=`{}`\"\n      \" port=`{}` protocol=`{}` name=`{}`\",\n      \" cpu_shares=`{}` cpu_period=`{}` cpu_quota=`{}`\"\n      \" memory_swappiness=`{}` memory_limit=`{}` memory_soft_limit=`{}` total_memory_limit=`{}`\",\n      msg->cgroup,\n      msg->cpu_shares,\n      msg->cpu_period,\n      msg->cpu_quota,\n      msg->memory_swappiness,\n      msg->memory_limit,\n      msg->memory_soft_limit,\n      msg->total_memory_limit);\n\n  auto const period = msg->cpu_period ? msg->cpu_period : kernel::DEFAULT_CGROUP_QUOTA;\n  auto const quota = msg->cpu_quota ? msg->cpu_quota : period;\n\n  span_ref.modify().cpu_soft_limit(msg->cpu_shares).cpu_hard_quota(quota).cpu_hard_period(period);\n}\n\nvoid CgroupSpan::container_resource_limits(\n    ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_resource_limits *msg)\n{\n  LOG::trace_in(\n      Component::cgroup,\n      \"CgroupSpan::container_resource_limits: cgroup=`{}`\"\n      \" port=`{}` protocol=`{}` name=`{}`\",\n      \" cpu_shares=`{}` cpu_period=`{}` cpu_quota=`{}`\"\n      \" memory_swappiness=`{}` memory_limit=`{}` memory_soft_limit=`{}` total_memory_limit=`{}`\",\n      msg->cgroup,\n      msg->cpu_shares,\n      msg->cpu_period,\n      msg->cpu_quota,\n      msg->memory_swappiness,\n      msg->memory_limit,\n      msg->memory_soft_limit,\n      msg->total_memory_limit);\n\n  auto const period = msg->cpu_period ? msg->cpu_period : kernel::DEFAULT_CGROUP_QUOTA;\n  auto const quota = msg->cpu_quota ? msg->cpu_quota : period;\n\n  span_ref.modify().cpu_soft_limit(msg->cpu_shares).cpu_hard_quota(quota).cpu_hard_period(period);\n}\n\nvoid CgroupSpan::on_container_updated(::ebpf_net::ingest::weak_refs::container container_ref)\n{\n  // Increase the update counter to signal that the container metadata has\n  // changed and needs to be sent to the matching core.\n  container_ref.modify().update_count(container_ref.update_count() + 1);\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/cgroup_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <absl/container/flat_hash_map.h>\n\n#include <util/cgroup_parser.h>\n\n#include <string>\n\nnamespace reducer::ingest {\n\nclass CgroupSpan : public ::ebpf_net::ingest::CgroupSpanBase {\npublic:\n  CgroupSpan();\n  ~CgroupSpan();\n  /* deprecated handlers\n   */\n  void cgroup_create_deprecated(\n      ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_create_deprecated *msg);\n\n  /* handlers\n   */\n  void cgroup_create(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_create *msg);\n  void cgroup_close(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__cgroup_close *msg);\n  void container_metadata(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_metadata *msg);\n  void\n  container_annotation(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_annotation *msg);\n  void pod_name(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__pod_name *msg);\n  void container_resource_limits_deprecated(\n      ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_resource_limits_deprecated *msg);\n  void container_resource_limits(\n      ::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__container_resource_limits *msg);\n  void k8s_metadata(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__k8s_metadata *msg);\n  void k8s_metadata_port(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__k8s_metadata_port *msg);\n  void nomad_metadata(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 timestamp, jsrv_ingest__nomad_metadata *msg);\n\nprivate:\n  // Called whenever one or more container span fields have been modified.\n  void on_container_updated(::ebpf_net::ingest::weak_refs::container container_ref);\n\n  // Assigns the appropriate service span, if this cgroup is created by a\n  // service manager.\n  void set_service(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser);\n  // assigns a pod_uid and pod_uid_hash if applicable.\n  void set_pod(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser);\n  // assigns a cgroup parent, if found\n  void set_parent(::ebpf_net::ingest::weak_refs::cgroup span_ref, u64 cgroup_parent);\n  // assigns a container, if found\n  void set_container(::ebpf_net::ingest::weak_refs::cgroup span_ref, const CGroupParser &parser);\n\n  absl::flat_hash_map<std::string, std::string> annotations_;\n  std::string k8s_container_name_;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/component.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE reducer::ingest\n#define ENUM_NAME Component\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(udp, 1, \"\")                                                                                                                \\\n  X(socket, 2, \"\")                                                                                                             \\\n  X(process, 3, \"\")                                                                                                            \\\n  X(k8s_pod, 4, \"\")                                                                                                            \\\n  X(flow_update, 5, \"\")                                                                                                        \\\n  X(cgroup, 6, \"\")                                                                                                             \\\n  X(aws_network_interface, 7, \"\")                                                                                              \\\n  X(udp_drops, 8, \"\")                                                                                                          \\\n  X(dns, 9, \"\")                                                                                                                \\\n  X(agent, 10, \"\")                                                                                                             \\\n  X(heartbeat, 11, \"\")                                                                                                         \\\n  X(worker, 12, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "reducer/ingest/container_updater.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/constants.h>\n\n#include <jitbuf/jb.h>\n\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\nnamespace reducer::ingest {\n\n// Utility for sending container info updates to the matching core.\n// Updates will be sent only when container information changes.\n//\nclass ContainerUpdater {\npublic:\n  // Send container info update if the container information has changed since\n  // the last time this function was called.\n  // flow -- reference to the maching core's flow span\n  // container -- span that holds the container information\n  void\n  send_container_info(ebpf_net::ingest::weak_refs::flow flow, ebpf_net::ingest::weak_refs::container container, FlowSide side)\n  {\n    if (!container.valid()) {\n      return;\n    }\n\n    if ((last_container_loc_ == container.loc()) && (last_update_count_ == container.update_count())) {\n      // No change.\n      return;\n    }\n\n    last_container_loc_ = container.loc();\n    last_update_count_ = container.update_count();\n\n    // NOTE: cgroup->container reference is created unconditionally, even when a\n    //       cgroup is not functioning as a container. We treat a container span\n    //       as initialized after it has been updated at least once.\n    if (container.update_count() == 0) {\n      return;\n    }\n\n    flow.container_info(\n        (u8)side,\n        jb_blob(container.name()),\n        jb_blob(container.pod_name()),\n        jb_blob(container.role()),\n        jb_blob(container.version()),\n        jb_blob(container.ns()),\n        container.node_type());\n  }\n\nprivate:\n  using loc_t = ebpf_net::ingest::weak_refs::container::location_type;\n  using count_t = ebpf_net::ingest::weak_refs::container::update_count_t;\n\n  loc_t last_container_loc_ = ebpf_net::ingest::weak_refs::container::invalid;\n  count_t last_update_count_ = 0;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/flow_updater.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"flow_updater.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <reducer/constants.h>\n\n#include <generated/ebpf_net/ingest/keys.h>\n#include <generated/ebpf_net/ingest/modifiers.h>\n\n#include <platform/userspace-time.h>\n\n#include <util/cgroup_parser.h>\n#include <util/log.h>\n#include <util/lookup3.h>\n#include <util/string_view.h>\n\n#include <tuple>\n\nnamespace reducer::ingest {\n\nnamespace {\n\ntemplate <size_t N> jb_blob jb(short_string<N> const &s)\n{\n  return {s.buf, s.len};\n}\n\njb_blob jb(std::string const &s)\n{\n  return {s.c_str(), (u16)s.length()};\n}\n\nu32 reverse_connector(u32 connector)\n{\n  switch (connector) {\n  case 1:\n    return 2;\n  case 2:\n    return 1;\n  default:\n    return connector;\n  }\n}\n\n} // namespace\n\nFlowUpdater::FlowUpdater(\n    ::ebpf_net::ingest::weak_refs::process process_ref,\n    ::ebpf_net::ingest::weak_refs::agent agent_ref,\n    IPv6Address local_addr,\n    u16 local_port,\n    IPv6Address remote_addr,\n    u16 remote_port,\n    u32 is_connector,\n    std::optional<dns::dns_record> remote_dns)\n    : local_port_(local_port),\n      remote_port_(remote_port),\n      is_connector_(is_connector),\n      local_addr_(local_addr),\n      remote_addr_(remote_addr),\n      remote_dns_(remote_dns),\n      ignore_updates_(false)\n{\n  // keep a handle to the process in `process_ref`\n  if (process_ref.valid()) {\n    process_handle_ = process_ref.get().to_handle();\n  } else {\n    LOG::error(\"FlowUpdater: got invalid process for local_addr={}\", local_addr);\n  }\n\n  // keep a handle to the agent in the `agent_ref`\n  if (agent_ref.valid()) {\n    agent_handle_ = agent_ref.get().to_handle();\n  } else {\n    LOG::error(\"FlowUpdater: got invalid agent for local_addr={}\", local_addr);\n  }\n\n  (void)create_flow();\n}\n\nFlowUpdater::~FlowUpdater()\n{\n  put_handles();\n}\n\nFlowUpdater &FlowUpdater::operator=(FlowUpdater &&other)\n{\n  put_handles();\n\n  local_port_ = other.local_port_;\n  remote_port_ = other.remote_port_;\n  is_connector_ = other.is_connector_;\n  local_addr_ = other.local_addr_;\n  remote_addr_ = other.remote_addr_;\n  remote_dns_ = other.remote_dns_;\n\n  process_handle_ = std::move(other.process_handle_);\n  agent_handle_ = std::move(other.agent_handle_);\n  flow_handle_ = std::move(other.flow_handle_);\n\n  side_ = other.side_;\n  ignore_updates_ = other.ignore_updates_;\n\n  return *this;\n}\n\nvoid FlowUpdater::put_handles()\n{\n  flow_handle_.put(*local_index());\n  process_handle_.put(*local_index());\n  agent_handle_.put(*local_index());\n}\n\n::ebpf_net::ingest::weak_refs::flow FlowUpdater::create_flow()\n{\n  u128 local_addr_int = local_addr_.as_int();\n  u128 remote_addr_int = remote_addr_.as_int();\n\n  bool ordered = (std::tie(local_addr_int, local_port_) < std::tie(remote_addr_int, remote_port_));\n\n  ::ebpf_net::ingest::keys::flow flow_key;\n  if (ordered) {\n    flow_key.addr1 = local_addr_int;\n    flow_key.port1 = local_port_;\n    flow_key.addr2 = remote_addr_int;\n    flow_key.port2 = remote_port_;\n  } else {\n    flow_key.addr1 = remote_addr_int;\n    flow_key.port1 = remote_port_;\n    flow_key.addr2 = local_addr_int;\n    flow_key.port2 = local_port_;\n  }\n\n  auto flow = local_index()->flow.by_key(flow_key);\n\n  if (!flow.valid()) {\n    LOG::trace_in(Component::flow_update, \"FlowUpdater::create_flow: could not allocate a flow span\");\n    return ::ebpf_net::ingest::handles::flow().access(*local_index());\n  }\n\n  if (flow_handle_.valid() && flow_handle_.loc() == flow.loc()) {\n    // unchanged\n    return flow_handle_.access(*local_index());\n  }\n\n  if (ordered) {\n    // override the flow's \"connector\" if it is not unknown, otherwise leave it\n    // be\n    if (is_connector_ != 0) {\n      flow.modify().is_connector(is_connector_);\n    }\n    side_ = FlowSide::SIDE_A;\n  } else {\n    // override the flow's \"connector\" if it is not unknown, otherwise leave it\n    // be\n    if (is_connector_ != 0) {\n      flow.modify().is_connector(reverse_connector(is_connector_));\n    }\n    side_ = FlowSide::SIDE_B;\n  }\n\n  if (auto process = process_handle_.access(*local_index()); process.valid()) {\n    if (side_ == FlowSide::SIDE_A) {\n      flow.modify().process1(process.get());\n    } else {\n      flow.modify().process2(process.get());\n    }\n\n    std::string_view cgroup_name;\n    std::string_view service_name;\n\n    if (auto service = process.service(); service.valid()) {\n      service_name = service.name().to_string_view();\n    }\n\n    if (auto cgroup = process.cgroup(); cgroup.valid()) {\n      cgroup_name = cgroup.name();\n      auto cgroup_info = CGroupParser{cgroup_name}.get();\n\n      if (auto service = cgroup.service(); service.valid()) {\n        // Overrides process service.\n        service_name = service.name().to_string_view();\n      }\n\n      // for cgroupfs style cgroup hierarchies, the container is the child of the pod\n      // so if we are looking at the child, and the parent is valid and has a pod id,\n      // send it on.\n      if (auto parent = cgroup.parent(); parent.valid() && (parent.pod_uid_suffix().at(0) != 0)) {\n        flow.k8s_info((u8)side_, parent.pod_uid_suffix().data(), parent.pod_uid_hash());\n      } else if (\n          // for systemd style, the cgroup has the entire hierarchy embedded in the cgroup\n          // so parse it, and look to see if one has a pod id, a container id, and if\n          // this pod_uid_suffix has been assigned.  if so, send that up.\n          !cgroup_info.pod_id.empty() && !cgroup_info.container_id.empty() && cgroup.pod_uid_suffix().at(0) != 0) {\n        flow.k8s_info((u8)side_, cgroup.pod_uid_suffix().data(), cgroup.pod_uid_hash());\n      }\n    }\n\n    // Send task information to flow.\n    flow.task_info((u8)side_, jb_blob(process.comm()), jb_blob(cgroup_name));\n\n    if (!service_name.empty()) {\n      // Send service information, if any.\n      flow.service_info((u8)side_, jb_blob(service_name));\n    }\n  }\n\n  {\n    uint8_t local_addr_buf[16];\n    local_addr_.write_to(local_addr_buf);\n\n    uint8_t remote_addr_buf[16];\n    remote_addr_.write_to(remote_addr_buf);\n\n    std::string_view remote_dns_name;\n    if (remote_dns_.has_value()) {\n      remote_dns_name = remote_dns_.value().to_string_view();\n    }\n\n    // send socket information to flow\n    flow.socket_info(\n        (u8)side_, local_addr_buf, local_port_, remote_addr_buf, remote_port_, (u8)is_connector_, jb_blob(remote_dns_name));\n  }\n\n  if (auto agent_ref = agent_handle_.access(*local_index()); agent_ref.valid()) {\n\n    auto &agent = agent_ref.impl();\n\n    // send agent information to flow\n    flow.agent_info((u8)side_, jb(agent.node_id()), jb(agent.node_az()), jb(agent.cluster()), jb(agent.role()), jb(agent.ns()));\n  }\n\n  LOG::trace_in(\n      Component::flow_update,\n      \"Flow::get_flow: flow={}, local_ip={}:{}, remote_ip={}:{}\",\n      flow.loc(),\n      local_addr_,\n      local_port_,\n      remote_addr_,\n      remote_port_);\n\n  flow_handle_.put(*local_index());\n  flow_handle_ = flow.get().to_handle();\n\n  return flow_handle_.access(*local_index());\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n::ebpf_net::ingest::weak_refs::flow FlowUpdater::get_flow_for_update()\n{\n  if (ignore_updates_) {\n    return ::ebpf_net::ingest::handles::flow().access(*local_index());\n  }\n\n  auto flow = create_flow();\n\n  if (!flow.valid()) {\n    return ::ebpf_net::ingest::handles::flow().access(*local_index());\n  }\n\n  auto proc1 = flow.process1();\n  auto proc2 = flow.process2();\n\n  // Is any of the two processes involved in this flow a \"proxy process\"?\n  // When a proxy process is in play, two flows are present: A -> P -> B.\n  // is_proxy is 1: proc1 is docker proxy, 2: proc2 is docker proxy. 0: neither\n  u32 is_proxy = 0;\n  if (proc1.valid() && (proc1.comm().to_string() == \"docker-proxy\")) {\n    is_proxy = 1;\n  } else if (proc2.valid() && (proc2.comm().to_string() == \"docker-proxy\")) {\n    is_proxy = 2;\n  }\n\n  if ((is_proxy != 0) && (is_proxy == flow.is_connector())) {\n    // This is a flow where the proxy process is the connector (connection\n    // originator), i.e. the P -> B flow. Updates to this flow should be\n    // ignored, but first we assign B's role to P so the A -> P updates\n    // are attributed to A -> B.\n\n    if ((is_proxy == 1) && proc2.valid() && proc2.cgroup().valid()) {\n      proc1.modify().cgroup_override(proc2.cgroup().get());\n      ignore_updates_ = true;\n    } else if ((is_proxy == 2) && proc1.valid() && proc1.cgroup().valid()) {\n      proc2.modify().cgroup_override(proc1.cgroup().get());\n      ignore_updates_ = true;\n    }\n\n    return ::ebpf_net::ingest::handles::flow().access(*local_index());\n  }\n\n  if ((flow.container1_override().valid() && (flow.container1_override().loc() == flow.container2().loc())) ||\n      (flow.container2_override().valid() && (flow.container2_override().loc() == flow.container1().loc()))) {\n    // Hack for existing connections, when is_connector is unknown: some other\n    // flow updater redirected P's container to point to B's.\n    ignore_updates_ = true;\n    return ::ebpf_net::ingest::handles::flow().access(*local_index());\n  }\n\n  // at this point, if this flow is part of a A->P->B proxy transfer, this flow\n  // is not ignored, and one side has a container_override. We'll send the task\n  // info message again to ensure the matching core has the correct cgroup ID\n  if ((side_ == FlowSide::SIDE_A) && proc1.valid() && proc1.cgroup_override().valid()) {\n    auto cgroup = proc1.cgroup_override();\n    flow.task_info((u8)side_, jb(proc1.comm()), jb(cgroup.name()));\n  }\n  if ((side_ == FlowSide::SIDE_B) && proc2.valid() && proc2.cgroup_override().valid()) {\n    auto cgroup = proc2.cgroup_override();\n    flow.task_info((u8)side_, jb(proc2.comm()), jb(cgroup.name()));\n  }\n\n  auto container1 = flow.container1_override().valid() ? flow.container1_override() : flow.container1();\n  auto container2 = flow.container2_override().valid() ? flow.container2_override() : flow.container2();\n\n  // send container information if it has changed\n  if (container1.valid()) {\n    container1_updater_.send_container_info(flow, container1, FlowSide::SIDE_A);\n  }\n  if (container2.valid()) {\n    container2_updater_.send_container_info(flow, container2, FlowSide::SIDE_B);\n  }\n\n  return flow;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvoid FlowUpdater::tcp_update(u64 timestamp, ::ebpf_net::metrics::tcp_metrics_point &metrics, int is_rx)\n{\n  auto flow = get_flow_for_update();\n\n  if (!flow.valid()) {\n    return;\n  }\n\n  flow.tcp_update(\n      (u8)side_,\n      is_rx,\n      metrics.active_sockets,\n      metrics.sum_retrans,\n      metrics.sum_bytes,\n      metrics.sum_srtt,\n      metrics.sum_delivered,\n      metrics.active_rtts,\n      metrics.syn_timeouts,\n      metrics.new_sockets,\n      metrics.tcp_resets);\n}\n\nvoid FlowUpdater::udp_update(u64 timestamp, ::ebpf_net::metrics::udp_metrics_point &metrics, int is_rx)\n{\n  auto flow = get_flow_for_update();\n\n  if (!flow.valid()) {\n    return;\n  }\n\n  flow.udp_update(\n      (u8)side_, is_rx, metrics.active_sockets, metrics.addr_changes, metrics.packets, metrics.bytes, metrics.drops);\n}\n\nvoid FlowUpdater::http_update(u64 timestamp, ::ebpf_net::metrics::http_metrics_point &metrics, u8 client_server)\n{\n  auto flow = get_flow_for_update();\n\n  if (!flow.valid()) {\n    return;\n  }\n\n  flow.http_update(\n      (u8)side_,\n      client_server,\n      metrics.active_sockets,\n      metrics.sum_code_200,\n      metrics.sum_code_400,\n      metrics.sum_code_500,\n      metrics.sum_code_other,\n      metrics.sum_total_time_ns,\n      metrics.sum_processing_time_ns);\n}\n\nvoid FlowUpdater::dns_update(u64 timestamp, const ::ebpf_net::metrics::dns_metrics_point &metrics, u8 client_server)\n{\n  auto flow = get_flow_for_update();\n\n  if (!flow.valid()) {\n    return;\n  }\n\n  flow.dns_update(\n      (u8)side_,\n      client_server,\n      metrics.active_sockets,\n      metrics.requests_a,\n      metrics.requests_aaaa,\n      metrics.responses,\n      metrics.timeouts,\n      metrics.sum_total_time_ns,\n      metrics.sum_processing_time_ns);\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/flow_updater.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/ingest/container_updater.h>\n\n#include <reducer/constants.h>\n#include <reducer/dns_cache.h>\n\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <util/ip_address.h>\n\n#include <optional>\n\nnamespace reducer::ingest {\n\n// Encapsulates access to flow spans.\n//\n// For clients to keep a reference to a flow span, it is required that they\n// also keep references to both endpoints whose nodes are used for flow span's\n// key. This class helps to do exactly that.\n//\nclass FlowUpdater {\npublic:\n  // Constructs the object and creates the underlying flow span.\n  FlowUpdater(\n      ::ebpf_net::ingest::weak_refs::process process_ref,\n      ::ebpf_net::ingest::weak_refs::agent agent_ref,\n      IPv6Address local_addr,\n      u16 local_port,\n      IPv6Address remote_addr,\n      u16 remote_port,\n      u32 is_connector,\n      std::optional<dns::dns_record> remote_dns);\n\n  // Puts all handles back to index.\n  ~FlowUpdater();\n\n  // Moving is allowed.\n  //\n  FlowUpdater(FlowUpdater &&) = default;\n  FlowUpdater &operator=(FlowUpdater &&);\n\n  // Copying is not allowed.\n  //\n  FlowUpdater(const FlowUpdater &) = delete;\n  FlowUpdater &operator=(const FlowUpdater &) = delete;\n\n  // Returns the local address\n  IPv6Address local_addr() { return local_addr_; }\n\n  // Returns the remote address\n  IPv6Address remote_addr() { return remote_addr_; }\n\n  // Returns whether the flow span is valid.\n  //\n  // The flow span handle will be assigned only if both endpoints\n  // and their nodes are valid.\n  //\n  bool valid() { return flow_handle_.valid(); }\n\n  // Sends tcp_update to the flow span.\n  void tcp_update(u64 t, ::ebpf_net::metrics::tcp_metrics_point &m, int is_rx);\n\n  // Sends udp_update to the flow span.\n  void udp_update(u64 t, ::ebpf_net::metrics::udp_metrics_point &m, int is_rx);\n\n  // Sends http_update to the flow span.\n  void http_update(u64 t, ::ebpf_net::metrics::http_metrics_point &m, u8 client_server);\n\n  // Sends dns_update to the flow span.\n  void dns_update(u64 t, const ::ebpf_net::metrics::dns_metrics_point &m, u8 client_server);\n\nprivate:\n  u16 local_port_;\n  u16 remote_port_;\n  u32 is_connector_;\n  IPv6Address local_addr_;\n  IPv6Address remote_addr_;\n  std::optional<dns::dns_record> remote_dns_;\n  ::ebpf_net::ingest::handles::process process_handle_;\n  ::ebpf_net::ingest::handles::agent agent_handle_;\n\n  // Handle of the flow this object is encapsulating.\n  // This handle can change as endpoints' nodes are changing.\n  ::ebpf_net::ingest::handles::flow flow_handle_;\n\n  // Indicates whether local side is flow's side-a (node1) or side-b (node2).\n  FlowSide side_;\n\n  // Flag indicating whether updates to this flow should be ignored.\n  bool ignore_updates_;\n\n  // For sending container info updates to the matching core.\n  ContainerUpdater container1_updater_;\n  ContainerUpdater container2_updater_;\n\n  // Ensures that the flow span is created and properly constructed.\n  // Saves the flow handle to flow_handle_.\n  // If the flow key doesn't change between calls, returns the value\n  // store in the flow_handle_.\n  //\n  ::ebpf_net::ingest::weak_refs::flow create_flow();\n\n  // Prepares the flow for stats update.\n  // Should be called before any {tcp,udp,http,dns}_update method.\n  // If the reference returned is invalid, the update should be ignored.\n  //\n  ::ebpf_net::ingest::weak_refs::flow get_flow_for_update();\n\n  // Puts all handles back to index.\n  void put_handles();\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/ingest_core.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"ingest_core.h\"\n\n#include <reducer/ingest/shared_state.h>\n#include <reducer/ingest/tcp_server.h>\n\n#include <generated/ebpf_net/ingest/span_base.h>\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/rpc_queue_matrix.h>\n#include <reducer/tsdb_formatter.h>\n#include <reducer/util/thread_ops.h>\n\n#include <platform/userspace-time.h>\n#include <util/error_handling.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/time.h>\n#include <util/uv_helpers.h>\n\n#include <iomanip>\n#include <sstream>\n\n#include <config.h>\n\nnamespace reducer::ingest {\n\nnamespace {\nusing namespace std::literals::chrono_literals;\nconstexpr auto NO_MESSAGE_TIMEOUT = 30s;\nconstexpr auto MESSAGE_TIMEOUT_CHECK_INTERVAL = 45s;\nconstexpr auto WRITE_INTERNAL_STATS_TIMER_REPEAT = 10s;\nconstexpr auto PULSE_TIMER_REPEAT = 1s;\n} // namespace\n\nvoid IngestCore::on_write_internal_stats_timer_cb(uv_timer_t *timer)\n{\n  auto const core = reinterpret_cast<IngestCore *>(timer->data);\n  core->on_write_internal_stats_timer();\n}\n\nvoid IngestCore::on_pulse_timer_cb(uv_timer_t *timer)\n{\n  IngestCore *core = (IngestCore *)timer->data;\n  core->on_pulse_timer();\n}\n\nvoid IngestCore::on_stop_async(uv_async_t *handle)\n{\n  auto const core = reinterpret_cast<IngestCore *>(handle->data);\n  uv_stop(&core->loop_);\n}\n\nIngestCore::IngestCore(\n    RpcQueueMatrix &ingest_to_logging_queues, RpcQueueMatrix &ingest_to_matching_queues, u32 telemetry_port, bool localhost)\n{\n  auto const ingest_shard_count = ingest_to_matching_queues.num_senders();\n  int res;\n\n  res = uv_loop_init(&loop_);\n  if (res != 0)\n    throw std::runtime_error(\"uv_loop_init failed\");\n\n  res = uv_async_init(&loop_, &stop_async_, &on_stop_async);\n  if (res != 0) {\n    throw std::runtime_error(\"uv_async_init failed\");\n  }\n  stop_async_.data = this;\n\n  ASSUME(ingest_to_logging_queues.num_receivers() == 1).else_log(\"Not using multiple logging cores\");\n\n  // Create the ingest workers and start the telemetry TCP server.\n  ASSUME(ingest_shard_count > 0).else_log(\"Ingest shards should be > 0, instead got {}\", ingest_shard_count);\n\n  std::vector<std::unique_ptr<IngestWorker>> workers;\n  workers.reserve(ingest_shard_count);\n  for (uint32_t shard = 0; shard < ingest_shard_count; ++shard) {\n    workers.push_back(std::make_unique<IngestWorker>(ingest_to_logging_queues, ingest_to_matching_queues, shard));\n  }\n  tcp_server_.reset(new TcpServer(loop_, telemetry_port, localhost, std::move(workers)));\n  index_dumper_.resize(ingest_shard_count);\n  TcpServer::singleton()->instance = tcp_server_.get();\n\n  /* internal stats */\n  /* initialize the internal stats timer */\n  res = uv_timer_init(&loop_, &write_internal_stats_timer_);\n  if (res != 0)\n    throw std::runtime_error(\"could not init write internal stats timer\");\n\n  /* save 'this' for callback */\n  write_internal_stats_timer_.data = this;\n\n  /* start the internal stats timer */\n  res = uv_timer_start(\n      &write_internal_stats_timer_,\n      on_write_internal_stats_timer_cb,\n      integer_time<std::chrono::milliseconds>(WRITE_INTERNAL_STATS_TIMER_REPEAT),\n      integer_time<std::chrono::milliseconds>(WRITE_INTERNAL_STATS_TIMER_REPEAT));\n  if (res != 0) {\n    throw std::runtime_error(\"could not start write internal stats timer\");\n  }\n\n  CHECK_UV(uv_timer_init(&loop_, &pulse_timer_));\n  pulse_timer_.data = this;\n  CHECK_UV(uv_timer_start(\n      &pulse_timer_,\n      on_pulse_timer_cb,\n      integer_time<std::chrono::milliseconds>(PULSE_TIMER_REPEAT),\n      integer_time<std::chrono::milliseconds>(PULSE_TIMER_REPEAT)));\n\n  connection_timeout_handler_.emplace(loop_, [this] {\n    check_connection_timeouts();\n    return scheduling::JobFollowUp::ok;\n  });\n  connection_timeout_handler_->start(MESSAGE_TIMEOUT_CHECK_INTERVAL);\n}\n\nIngestCore::~IngestCore()\n{\n  TcpServer::singleton()->instance = nullptr;\n}\n\nvoid IngestCore::run()\n{\n  set_self_thread_name(\"ingest_listen\").on_error([](auto const &error) {\n    LOG::warn(\"unable to set name for ingest core listening thread: {}\", error);\n  });\n  uv_run(&loop_, UV_RUN_DEFAULT);\n  close_uv_loop_cleanly(&loop_);\n  done_.Notify();\n}\n\nvoid IngestCore::stop_async()\n{\n  uv_async_send(&stop_async_);\n}\n\nvoid IngestCore::stop_sync()\n{\n  stop_async();\n  wait_for_shutdown();\n}\n\nvoid IngestCore::wait_for_shutdown()\n{\n  done_.WaitForNotification();\n}\n\nstd::string to_string(VersionInfo v)\n{\n  std::stringstream ss;\n  ss << v;\n  return ss.str();\n}\n\nvoid IngestCore::on_write_internal_stats_timer()\n{\n  u64 time_ns = fp_get_time_ns();\n  std::string_view module = \"ingest\";\n  TcpServer::Stats server_stats = tcp_server_->get_stats();\n\n  /* write span statistics */\n  tcp_server_->visit_indexes(\n      [&](const int shard, ::ebpf_net::ingest::Index *const index) {\n        index->size_statistics(\n            [&](std::string_view span_name, std::size_t allocated, std::size_t max_allocated, std::size_t pool_size) {\n              local_core_stats_handle().span_utilization_stats(\n                  jb_blob(std::string(span_name)), jb_blob(module), shard, allocated, max_allocated, pool_size, time_ns);\n            });\n\n        local_core_stats_handle().status_stats(\n            jb_blob(module), shard, jb_blob(std::string(kServiceName)), jb_blob(to_string(versions::release)), 1u, time_ns);\n\n        /* This internal stat registers total TCP server connects and disconnects. This needs to execute only once\n           and not in all shards/workers. */\n\n        if (shard == 0) {\n          local_ingest_core_stats_handle().server_stats(\n              jb_blob(module), server_stats.connection_counter, server_stats.disconnect_counter, time_ns);\n        }\n\n        index_dumper_[shard].dump(\n            \"ingest\", shard, *index, std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds{time_ns}));\n      },\n      true /* block */);\n\n  tcp_server_->visit_connections(\n      [&](const int shard, NpmConnection *const ftconn) {\n        auto *conn = ftconn->ingest_connection();\n        AgentSpan &agent = conn->agent().impl();\n\n        // skip internal stats for AgentSpans that are not initialized (unknown) and for short-lived probe agents\n        if (agent.client_type() == ClientType::unknown || agent.client_type() == ClientType::liveness_probe ||\n            agent.client_type() == ClientType::readiness_probe) {\n          return;\n        }\n\n        /* write client span utilization statistics */\n\n        auto const client_handle_utilization = [&](std::string_view span_name, u64 size, u64 capacity) {\n          local_ingest_core_stats_handle().client_handle_pool_stats(\n              jb_blob(module),\n              shard,\n              jb_blob(span_name),\n              jb_blob(to_string(agent.version())),\n              jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n              jb_blob(agent.cluster()),\n              jb_blob(agent.role()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.kernel_version()),\n              integer_value(agent.client_type()),\n              jb_blob(agent.hostname()),\n              jb_blob(agent.os()),\n              jb_blob(agent.os_version()),\n              time_ns,\n              size,\n              (double)(size / capacity));\n        };\n\n        client_handle_utilization(\"socket\", conn->socket__hash.size(), conn->socket__hash.capacity());\n        client_handle_utilization(\"udp_socket\", conn->udp_socket__hash.size(), conn->udp_socket__hash.capacity());\n        client_handle_utilization(\"process\", conn->process__hash.size(), conn->process__hash.capacity());\n        client_handle_utilization(\n            \"tracked_process\", conn->tracked_process__hash.size(), conn->tracked_process__hash.capacity());\n        client_handle_utilization(\"cgroup\", conn->cgroup__hash.size(), conn->cgroup__hash.capacity());\n        client_handle_utilization(\n            \"aws_network_interface\", conn->aws_network_interface__hash.size(), conn->aws_network_interface__hash.capacity());\n\n        /* write message statistics */\n        conn->message_stats.foreach ([&](std::string_view module, std::string_view msg, int severity, u64 count) {\n          local_ingest_core_stats_handle().agent_connection_message_stats(\n              jb_blob(module),\n              shard,\n              jb_blob(to_string(agent.version())),\n              jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n              jb_blob(agent.cluster()),\n              jb_blob(agent.role()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.kernel_version()),\n              integer_value(agent.client_type()),\n              jb_blob(agent.hostname()),\n              jb_blob(agent.os()),\n              jb_blob(agent.os_version()),\n              time_ns,\n              jb_blob(msg),\n              severity,\n              count);\n        });\n\n        conn->message_errors.foreach ([&](std::string_view module, std::string_view msg, std::string_view error, u64 count) {\n          local_ingest_core_stats_handle().agent_connection_message_error_stats(\n              jb_blob(module),\n              shard,\n              jb_blob(to_string(agent.version())),\n              jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n              jb_blob(agent.cluster()),\n              jb_blob(agent.role()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.kernel_version()),\n              integer_value(agent.client_type()),\n              jb_blob(agent.hostname()),\n              jb_blob(agent.os()),\n              jb_blob(agent.os_version()),\n              time_ns,\n              jb_blob(msg),\n              jb_blob(error),\n              uint64_t(count));\n        });\n\n        /* write connection statistics */\n        local_ingest_core_stats_handle().connection_stats(\n            jb_blob(module),\n            shard,\n            jb_blob(to_string(agent.version())),\n            jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n            jb_blob(agent.cluster()),\n            jb_blob(agent.role()),\n            jb_blob(agent.node_az()),\n            jb_blob(agent.node_az()),\n            jb_blob(agent.kernel_version()),\n            integer_value(agent.client_type()),\n            jb_blob(agent.hostname()),\n            jb_blob(agent.os()),\n            jb_blob(agent.os_version()),\n            time_ns,\n            static_cast<u64>(ftconn->time_since_last_message().count()),\n            static_cast<u64>(ftconn->clock_offset().count()));\n\n        /* write collector log statistics */\n        auto const write_log_count = [&](std::string_view severity, u32 count) {\n          local_ingest_core_stats_handle().collector_log_stats(\n              jb_blob(module),\n              shard,\n              jb_blob(to_string(agent.version())),\n              jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n              jb_blob(agent.cluster()),\n              jb_blob(agent.role()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.node_az()),\n              jb_blob(agent.kernel_version()),\n              integer_value(agent.client_type()),\n              jb_blob(agent.hostname()),\n              jb_blob(agent.os()),\n              jb_blob(agent.os_version()),\n              time_ns,\n              jb_blob(severity),\n              count);\n        };\n\n        auto const &log_count = agent.log_count();\n        write_log_count(\"ignored\", log_count.ignored);\n        write_log_count(\"info\", log_count.info);\n        write_log_count(\"warning\", log_count.warning);\n        write_log_count(\"error\", log_count.error);\n        write_log_count(\"critical\", log_count.critical);\n\n        /* write agent stats */\n        auto ingest_core_stats = local_ingest_core_stats_handle();\n        agent.write_internal_stats(ingest_core_stats, time_ns, shard, \"ingest\");\n\n        /* write entrypoint information */\n        local_ingest_core_stats_handle().entry_point_stats(\n            jb_blob(module),\n            shard,\n            jb_blob(to_string(agent.version())),\n            jb_blob(std::to_string(integer_value(agent.cloud_platform()))),\n            jb_blob(agent.cluster()),\n            jb_blob(agent.role()),\n            jb_blob(agent.node_az()),\n            jb_blob(agent.node_az()),\n            jb_blob(agent.kernel_version()),\n            integer_value(agent.client_type()),\n            jb_blob(agent.hostname()),\n            jb_blob(agent.os()),\n            jb_blob(agent.os_version()),\n            time_ns,\n            jb_blob(agent.kernel_version()),\n            jb_blob(agent.kernel_headers_source()),\n            1u);\n      },\n      true /* block */);\n\n  /* RPC queue statistics */\n  tcp_server_->visit_rpc_stats(\n      [&](int shard, RpcSenderStats &rpc_stats) {\n        auto core_stats_handle = local_core_stats_handle();\n        rpc_stats.write_internal_metrics_to_logging_core(core_stats_handle, time_ns);\n      },\n      true);\n}\n\nvoid IngestCore::on_pulse_timer()\n{\n  tcp_server_->visit_indexes(\n      [&](const int shard, ::ebpf_net::ingest::Index *const index) { index->send_pulse(); }, false /* block */);\n}\n\nvoid IngestCore::check_connection_timeouts()\n{\n  auto now = std::chrono::nanoseconds(fp_get_time_ns());\n\n  tcp_server_->visit_channels(\n      [&](const int shard, channel::TCPChannel *channel, std::chrono::nanoseconds last_message_seen) {\n        auto time_since_last_message = now - last_message_seen;\n        if (time_since_last_message >= NO_MESSAGE_TIMEOUT) {\n          LOG::warn(\"closing connection due to inactivity\");\n          channel->close_permanently();\n        }\n      },\n      true /* block */\n  );\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/ingest_core.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/publisher.h>\n#include <reducer/tsdb_format.h>\n\n#include <reducer/util/index_dumper.h>\n\n#include <platform/types.h>\n#include <scheduling/interval_scheduler.h>\n\n#include <absl/synchronization/notification.h>\n\n#include <uv.h>\n\nnamespace reducer {\nclass RpcQueueMatrix;\n}\n\nnamespace reducer::ingest {\n\nclass TcpServer;\n\nclass IngestCore {\npublic:\n  // Arguments:\n  //   - ingest_to_logging_queues - Queues for sending messages to the logging\n  //       core\n  //   - ingest_to_matching_queues - Queues for sending messages to the matching\n  //       core\n  //   - telemetry_port - The port to which agents will send data.\n  //   - stats_writer - Used to write internal metrics to prometheus\n  //   - shard_config - Per-shard configuration - will spawn as many shards as\n  //       the size of this parameter\n  //   - metrics_tsdb_format - Format of metrics published to TSDB\n  //   - localhost - Whether or not the ingest TCP listens to\n  //         0.0.0.0 (if `localhost` is false) or 127.0.0.1\n  IngestCore(\n      RpcQueueMatrix &ingest_to_logging_queues,\n      RpcQueueMatrix &ingest_to_matching_queues,\n      u32 telemetry_port,\n      bool localhost = false);\n\n  ~IngestCore();\n\n  /**\n   * runs the loop\n   */\n  void run();\n\n  /**\n   * stops the loop - can be run from any thread\n   */\n  void stop_async();\n  void stop_sync();\n  void wait_for_shutdown();\n\nprivate:\n  /* Callback function for stop_async. */\n  static void on_stop_async(uv_async_t *handle);\n\n  /* Core callbacks */\n  static void on_write_internal_stats_timer_cb(uv_timer_t *timer);\n  static void on_pulse_timer_cb(uv_timer_t *timer);\n\n  /**\n   * (internal) called when it's time to drain internal metrics for\n   *   connections and disconnects to prom_queue_\n   */\n  void on_write_internal_stats_timer();\n\n  /**\n   * Called every-now-and-then to send a liveness pulse to peer cores.\n   */\n  void on_pulse_timer();\n\n  /**\n   * Scans for timed-out connections and disconnects them.\n   */\n  void check_connection_timeouts();\n\n  // Libuv loop object.\n  uv_loop_t loop_;\n\n  // Async object used for stopping the loop from another thread.\n  uv_async_t stop_async_;\n\n  // Notification triggered when the loop is done running.\n  absl::Notification done_;\n\n  std::unique_ptr<TcpServer> tcp_server_;\n  std::vector<IndexDumper> index_dumper_;\n\n  uv_timer_t write_internal_stats_timer_;\n  uv_timer_t pulse_timer_;\n  std::optional<scheduling::IntervalScheduler> connection_timeout_handler_;\n\n  friend void __on_signal_cb(uv_signal_t *, int);\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/ingest_worker.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"ingest_worker.h\"\n#include \"npm_connection.h\"\n#include \"shared_state.h\"\n\n#include <reducer/rpc_queue_matrix.h>\n#include <reducer/worker.h>\n\n#include <generated/ebpf_net/ingest/index.h>\n#include <generated/ebpf_net/ingest/modifiers.h>\n\n#include <channel/callbacks.h>\n\n#include <platform/userspace-time.h>\n\n#include <util/boot_time.h>\n#include <util/error_handling.h>\n#include <util/log.h>\n\n#include <absl/time/time.h>\n\n#include <uv.h>\n\n#include <memory>\n#include <optional>\n\nnamespace reducer::ingest {\n\nIngestWorker::IngestWorker(RpcQueueMatrix &ingest_to_logging_queues, RpcQueueMatrix &ingest_to_matching_queues, u32 shard_num)\n    : ingest_to_logging_stats_(shard_num, \"ingest\", \"logging\", ingest_to_logging_queues),\n      ingest_to_matching_stats_(shard_num, \"ingest\", \"matching\", ingest_to_matching_queues),\n      index_(std::make_unique<ebpf_net::ingest::Index>(\n          ingest_to_logging_queues.make_writers<ebpf_net::logging::Writer>(shard_num, monotonic, get_boot_time()),\n          ingest_to_matching_queues.make_writers<ebpf_net::matching::Writer>(shard_num, monotonic, get_boot_time()))),\n      logger_(index_->logger.alloc()),\n      core_stats_(index_->core_stats.alloc()),\n      ingest_core_stats_(index_->ingest_core_stats.alloc())\n{}\n\nIngestWorker::~IngestWorker() {}\n\nvoid IngestWorker::register_close_callback(OnCloseCallback on_close_cb)\n{\n  on_close_cb_ = std::move(on_close_cb);\n}\n\nstd::shared_ptr<absl::Notification> IngestWorker::visit_index(IndexCb cb)\n{\n  return visit_thread([this, captured_cb = std::move(cb)] { captured_cb(index_.get()); });\n}\n\nstd::shared_ptr<absl::Notification> IngestWorker::visit_connections(ConnectionCb cb)\n{\n  return visit_callbacks([this, captured_cb = std::move(cb)](::channel::Callbacks *const callbacks) {\n    auto *const ingest_callbacks = static_cast<IngestWorker::Callbacks *>(callbacks);\n    captured_cb(ingest_callbacks->connection_.get());\n  });\n}\n\nstd::shared_ptr<absl::Notification> IngestWorker::visit_channels(ChannelCb cb)\n{\n  return visit_callbacks([this, captured_cb = std::move(cb)](::channel::Callbacks *const callbacks) {\n    auto *const ingest_callbacks = static_cast<IngestWorker::Callbacks *>(callbacks);\n    captured_cb(ingest_callbacks->channel_, ingest_callbacks->last_message_seen_);\n  });\n}\n\nstd::shared_ptr<absl::Notification> IngestWorker::visit_rpc_stats(RpcStatsCb cb)\n{\n  return visit_thread([this, captured_cb = std::move(cb)] {\n    captured_cb(ingest_to_logging_stats_);\n    captured_cb(ingest_to_matching_stats_);\n  });\n}\n\nvoid IngestWorker::on_thread_start()\n{\n  set_local_index(index_.get());\n  set_local_logger(&logger_);\n  set_local_core_stats_handle(&core_stats_);\n  set_local_ingest_core_stats_handle(&ingest_core_stats_);\n}\n\nvoid IngestWorker::on_thread_stop()\n{\n  set_local_index(nullptr);\n  set_local_logger(nullptr);\n  set_local_core_stats_handle(nullptr);\n  set_local_ingest_core_stats_handle(nullptr);\n  set_local_connection(nullptr);\n}\n\nstd::unique_ptr<::channel::Callbacks> IngestWorker::create_callbacks(uv_loop_t &loop, ::channel::TCPChannel *const tcp_channel)\n{\n  return std::make_unique<IngestWorker::Callbacks>(this, tcp_channel);\n}\n\nIngestWorker::Callbacks::Callbacks(IngestWorker *worker, channel::TCPChannel *channel)\n    : worker_(worker), channel_(channel), decompressor_(Worker::kBufferSize)\n{\n  assert(local_index() == worker_->index_.get());\n\n  connection_ = std::make_unique<NpmConnection>(*worker_->index_);\n\n  last_message_seen_ = std::chrono::nanoseconds(fp_get_time_ns());\n}\n\nIngestWorker::Callbacks::~Callbacks() {}\n\nuint32_t IngestWorker::Callbacks::received_data(const u8 *data, int data_len)\n{\n  const u8 *begin = data;\n  const u8 *const end = data + data_len;\n  u16 count = 0;\n\n  // Process the raw data as-is if the decompressor is not active.\n  while (!decompressor_active_) {\n    const std::optional<uint32_t> bytes_consumed = received_data_internal(begin, end - begin);\n    if (!bytes_consumed.has_value()) {\n      // If nullopt, then close the channel.\n      channel_->close_permanently();\n      return 0;\n    }\n\n    if (*bytes_consumed == 0) {\n      // Not enough data received.\n      return 0;\n    }\n\n    // Increment the begin pointer by the bytes consumed.\n    ASSUME(static_cast<int>(*bytes_consumed) <= (end - begin));\n    begin += *bytes_consumed;\n    ++count;\n\n    if (begin == end) {\n      // Everything has been consumed.\n      return data_len;\n    }\n  }\n\n  // If this loop is reached, either this is\n  // 1) The first message received (and whether or not compression is being\n  //    used is being negotiated).\n  // 2) This is a subsequent data message, and it is compressed.\n  size_t consumed_len = 0;\n  do {\n    const size_t res = decompressor_.process(begin, end - begin, &consumed_len);\n\n    // Check if decompression failed.\n    if (res != 0) {\n      local_logger().ingest_decompression_error(\n          static_cast<u8>(connection_->client_type()),\n          jb_blob(connection_->client_hostname()),\n          jb_blob(std::string_view(LZ4F_getErrorName(res))));\n      channel_->close_permanently();\n      return 0;\n    }\n\n    // Process the decompressed data.\n    begin += consumed_len;\n\n    ASSUME(decompressor_active_);\n    const std::optional<uint32_t> consumed_uncompressed =\n        received_data_internal(decompressor_.output_buf(), decompressor_.output_buf_size());\n\n    // An error occurred, close.\n    if (!consumed_uncompressed) {\n      channel_->close_permanently();\n      return 0;\n    }\n\n    // Remove the handled bytes from decompression buffer.\n    decompressor_.discard(*consumed_uncompressed);\n    ++count;\n\n    // * if we weren't able to decompress any bytes, can exit -- another\n    //   iteration will not make progress.\n    // * if we were able to decompress everything, no need for another\n    //   iteration.\n    // * if we were able to decompress, regardless of whether handle_multiple\n    //   processed bytes or not, another call to the decompressor might make\n    //   more bytes available, so if there are more compressed bytes, we should\n    //   make another iteration.\n  } while ((consumed_len > 0) && (begin < end));\n\n  worker_->ingest_to_logging_stats_.check_utilization();\n  worker_->ingest_to_matching_stats_.check_utilization();\n  worker_->invoke_visitors();\n\n  return end - data;\n}\n\nstd::optional<uint32_t> IngestWorker::Callbacks::received_data_internal(const u8 *const data, const int data_len)\n{\n  last_message_seen_ = std::chrono::nanoseconds(fp_get_time_ns());\n\n  auto *ft_conn = connection_.get();\n\n  try {\n    set_local_connection(ft_conn);\n    const int res = first_message_seen_ ? ft_conn->handle_multiple((const char *)data, data_len)\n                                        : ft_conn->handle((const char *)data, data_len);\n\n    // Check if the received data buffer is too small for the message type.\n    // (but do not close).\n    if (res == -EAGAIN) {\n      return 0;\n    }\n\n    // Process other types of errors.\n    if (res < 0) {\n      local_logger().ingest_processing_error(\n          static_cast<u8>(ft_conn->client_type()),\n          jb_blob(ft_conn->client_hostname()),\n          jb_blob(std::string_view(strerror(res))));\n      return std::nullopt;\n    }\n\n    // If this is the first message, turn on decompression of further messages.\n    if (!first_message_seen_) {\n      decompressor_active_ = true;\n      first_message_seen_ = true;\n    }\n\n    return res;\n  } catch (const std::exception &e) {\n    // Catch any thrown errors.\n    local_logger().ingest_processing_error(\n        static_cast<u8>(ft_conn->client_type()), jb_blob(ft_conn->client_hostname()), jb_blob(std::string_view(e.what())));\n    return std::nullopt;\n  }\n}\n\nvoid IngestWorker::Callbacks::on_error(const int err)\n{\n  const ClientType client_type = connection_->client_type();\n  const std::string_view client_hostname = connection_->client_hostname();\n\n  if (err == UV_EOF) {\n    // Client disconnected. Print message if it is a collector client.\n    if ((client_type != ClientType::liveness_probe) && (client_type != ClientType::readiness_probe)) {\n      LOG::info(\"Connection closed from {} collector at '{}'\", client_type, client_hostname);\n    }\n  } else {\n    // Connection error.\n    local_logger().ingest_connection_error(\n        static_cast<u8>(client_type), jb_blob(client_hostname), jb_blob(std::string_view(uv_strerror(err))));\n  }\n}\n\nvoid IngestWorker::Callbacks::on_closed()\n{\n  worker_->on_close_cb_();\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/ingest_worker.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"npm_connection.h\"\n\n#include <reducer/rpc_stats.h>\n#include <reducer/worker.h>\n\n#include <generated/ebpf_net/ingest/index.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n#include <generated/ebpf_net/logging/writer.h>\n\n#include <channel/callbacks.h>\n\n#include <util/log.h>\n#include <util/lz4_decompressor.h>\n\n#include <absl/time/time.h>\n#include <uv.h>\n\n#include <memory>\n#include <optional>\n\nnamespace reducer {\nclass RpcQueueMatrix;\n}\n\nnamespace reducer::ingest {\n\n// IngestWorker is responsible to reading npm_connection messages via TCP connections\n// from the agent and forwarding them to the various spans in the reducer.\n//\nclass IngestWorker : public Worker {\npublic:\n  using OnCloseCallback = std::function<void()>;\n\n  // Arguments:\n  // - index - The ingest index that will be owned by this class.\n  // Calling this constructor will set the `local_index()` value.\n  IngestWorker(RpcQueueMatrix &ingest_to_logging_queues, RpcQueueMatrix &ingest_to_matching_queues, u32 shard_num);\n  ~IngestWorker() override;\n\n  // Registers a callback that will be invoked everytime a TCP connection\n  // is closed. Overwrites the previous callback if this function has already\n  // been called.\n  void register_close_callback(OnCloseCallback on_close_cb);\n\n  // The set of callbacks invoked when data arrives over a TCP connection.\n  // There will be an instance of this class for every established connection.\n  // When `received_data` is invoked, `local_connection` will be overwritten by\n  // the NpmConnection instance owned by this class.\n  class Callbacks : public ::channel::Callbacks {\n  public:\n    Callbacks(IngestWorker *worker, channel::TCPChannel *tcp_channel);\n    ~Callbacks() override;\n\n    uint32_t received_data(const u8 *data, int data_len) override;\n    void on_error(int err) override;\n    void on_closed() override;\n\n  private:\n    friend class IngestWorker;\n\n    // Internal data processing callback. If successful, return the number of\n    // bytes processed. If an error occurred, returns nullopt (which will cause\n    // the connection to close).\n    std::optional<uint32_t> received_data_internal(const u8 *data, int data_len);\n\n    IngestWorker *worker_;\n    channel::TCPChannel *channel_;\n    Lz4Decompressor decompressor_;\n\n    std::unique_ptr<NpmConnection> connection_;\n\n    bool decompressor_active_ = false;\n    bool first_message_seen_ = false;\n    std::chrono::nanoseconds last_message_seen_;\n  };\n\n  // Invokes the provided callback in this worker's thread, allowing one to\n  // access the contained `Index` without worry about multithreading issues.\n  // See `Worker::visit_thread` for usage notes.\n  using IndexCb = std::function<void(::ebpf_net::ingest::Index *)>;\n  [[nodiscard]] std::shared_ptr<absl::Notification> visit_index(IndexCb cb);\n\n  // Same as above, but runs `cb` on each of this class's connections'\n  // `NpmConnection` objects.\n  using ConnectionCb = std::function<void(NpmConnection *)>;\n  [[nodiscard]] std::shared_ptr<absl::Notification> visit_connections(ConnectionCb cb);\n\n  // Same as above, but runs `cb` on each of this class's channel.\n  using ChannelCb = std::function<void(channel::TCPChannel *, std::chrono::nanoseconds)>;\n  [[nodiscard]] std::shared_ptr<absl::Notification> visit_channels(ChannelCb cb);\n\n  // Same as above, but runs `b` on worker's RpcSenderStats.\n  using RpcStatsCb = std::function<void(RpcSenderStats &)>;\n  std::shared_ptr<absl::Notification> visit_rpc_stats(RpcStatsCb cb);\n\nprotected:\n  void on_thread_start() override;\n  void on_thread_stop() override;\n\n  std::unique_ptr<::channel::Callbacks> create_callbacks(uv_loop_t &loop, ::channel::TCPChannel *tcp_channel) override;\n\nprivate:\n  OnCloseCallback on_close_cb_;\n  RpcSenderStats ingest_to_logging_stats_;\n  RpcSenderStats ingest_to_matching_stats_;\n  std::unique_ptr<::ebpf_net::ingest::Index> index_;\n  ::ebpf_net::ingest::auto_handles::logger logger_;\n  ::ebpf_net::ingest::auto_handles::core_stats core_stats_;\n  ::ebpf_net::ingest::auto_handles::ingest_core_stats ingest_core_stats_;\n\n  friend class Callbacks;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/k8s_pod_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"k8s_pod_span.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <reducer/uid_key.h>\n\n#include <generated/ebpf_net/ingest/modifiers.h>\n\n#include <util/log.h>\n\nnamespace reducer::ingest {\n\nK8sPodSpan::~K8sPodSpan()\n{\n  for (auto &container_handle : containers_) {\n    container_handle.put(*local_index());\n  }\n}\n\nvoid K8sPodSpan::pod_container(\n    ::ebpf_net::ingest::weak_refs::k8s_pod span,\n    std::string_view container_id,\n    std::string_view container_name,\n    std::string_view container_image)\n{\n  static constexpr char const *sep = \"://\";\n\n  LOG::trace_in(\n      Component::k8s_pod,\n      \"K8sPodSpan::pod_container: pod_uid_suffix={} container_id={}\",\n      std::string_view((const char *)span.uid_suffix().data(), span.uid_suffix().size()),\n      container_id);\n\n  // Kubernetes prefixes container IDs with the container engine type,\n  // e.g. \"docker://\" or \"containerd://\".\n  auto sep_pos = container_id.find(sep);\n  if (sep_pos != std::string::npos) {\n    container_id = container_id.substr(sep_pos + strlen(sep));\n  }\n\n  if (auto [_, inserted] = seen_container_ids_.insert(std::string(container_id)); inserted == false) {\n    LOG::trace_in(Component::k8s_pod, \"K8sPodSpan::pod_container: container already reported: {}\", container_id);\n    return;\n  }\n\n  auto container_key = make_uid_key<ebpf_net::ingest::keys::k8s_container>(container_id);\n  auto k8s_container = local_index()->k8s_container.by_key(container_key);\n  if (!k8s_container.valid()) {\n    LOG::trace_in(Component::k8s_pod, \"K8sPodSpan::pod_container: failed to reference container {}\", container_id);\n    return;\n  }\n\n  k8s_container.set_container_pod(span.uid_suffix().data(), span.uid_hash(), jb_blob(container_name), jb_blob(container_image));\n\n  containers_.push_back(k8s_container.to_handle());\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/k8s_pod_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <set>\n#include <vector>\n\nnamespace reducer::ingest {\n\nclass K8sPodSpan : public ::ebpf_net::ingest::K8sPodSpanBase {\npublic:\n  K8sPodSpan() = default;\n  ~K8sPodSpan();\n\n  void pod_container(\n      ::ebpf_net::ingest::weak_refs::k8s_pod span,\n      std::string_view container_id,\n      std::string_view container_name,\n      std::string_view container_tag);\n\nprivate:\n  using k8s_container_handle_t = ::ebpf_net::ingest::handles::k8s_container;\n\n  // Set of container IDs that were reported through |pod_container|.\n  std::set<std::string> seen_container_ids_;\n\n  // Container spans that this pod owns.\n  std::vector<k8s_container_handle_t> containers_;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/npm_connection.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"npm_connection.h\"\n\nnamespace reducer::ingest {\n\nNpmConnection::NpmConnection(::ebpf_net::ingest::Index &index)\n    : transform_builder_(), protocol_(transform_builder_), connection_(protocol_, index), time_tracker_()\n{}\n\nint NpmConnection::handle(const char *msg, uint32_t len)\n{\n  auto const handled = protocol_.handle(msg, len);\n  time_tracker_.message_received(handled.client_timestamp);\n  return handled.result;\n}\n\nint NpmConnection::handle_multiple(const char *msg, u64 len)\n{\n  auto const handled = protocol_.handle_multiple(msg, len);\n  time_tracker_.message_received(handled.client_timestamp);\n  return handled.result;\n}\n\nstd::chrono::nanoseconds NpmConnection::clock_offset() const\n{\n  return time_tracker_.clock_offset();\n}\n\nstd::chrono::nanoseconds NpmConnection::time_since_last_message() const\n{\n  return time_tracker_.time_since_last_message();\n}\n\nvoid NpmConnection::set_client_info(std::string_view hostname, ClientType type)\n{\n  client_hostname_ = hostname;\n  client_type_ = type;\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/npm_connection.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/tcp_channel.h>\n\n#include <generated/ebpf_net/ingest/connection.h>\n#include <generated/ebpf_net/ingest/index.h>\n#include <generated/ebpf_net/ingest/protocol.h>\n#include <generated/ebpf_net/ingest/transform_builder.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <reducer/util/time_tracker.h>\n#include <util/fixed_hash.h>\n\nnamespace reducer::ingest {\n\nclass NpmConnection {\npublic:\n  NpmConnection(::ebpf_net::ingest::Index &index);\n\n  int handle(const char *msg, uint32_t len);\n\n  int handle_multiple(const char *msg, u64 len);\n\n  ebpf_net::ingest::Connection *ingest_connection() { return &connection_; }\n\n  std::chrono::nanoseconds clock_offset() const;\n  std::chrono::nanoseconds time_since_last_message() const;\n\n  void set_client_info(std::string_view hostname, ClientType type);\n\n  std::string_view client_hostname() const { return client_hostname_; }\n\n  ClientType client_type() const { return client_type_; }\n\nprivate:\n  ebpf_net::ingest::TransformBuilder transform_builder_;\n  ebpf_net::ingest::Protocol protocol_;\n  ebpf_net::ingest::Connection connection_;\n  TimeTracker time_tracker_;\n  std::string_view client_hostname_ = kUnknown;\n  ClientType client_type_ = ClientType::unknown;\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/process_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"process_span.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <generated/ebpf_net/ingest/modifiers.h>\n\n#include <util/log.h>\n\n#include <cstring>\n#include <optional>\n#include <stdexcept>\n#include <string>\n\nnamespace reducer::ingest {\n\nnamespace {\n\nconstexpr std::string_view RUNIT_SERVICE_PROG_NAME = \"runsv\";\n\n} // namespace\n\nProcessSpan::ProcessSpan() {}\n\nProcessSpan::~ProcessSpan() {}\n\nvoid ProcessSpan::pid_info(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info *msg)\n{\n  std::string comm{(char const *)msg->comm, strnlen((char const *)msg->comm, sizeof(msg->comm))};\n\n  LOG::trace_in(Component::process, \"ProcessSpan::pid_info pid:{} comm:{}\", msg->pid, comm);\n\n  create_refs(span_ref, msg->pid, std::nullopt, std::move(comm), std::nullopt);\n}\n\nvoid ProcessSpan::pid_info_create_deprecated(\n    ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info_create_deprecated *msg)\n{\n  std::string comm{(char const *)msg->comm, strnlen((char const *)msg->comm, sizeof(msg->comm))};\n\n  LOG::trace_in(\n      Component::process, \"ProcessSpan::pid_info_create_deprecated pid:{} cgroup:{} comm:{}\", msg->pid, msg->cgroup, comm);\n\n  create_refs(span_ref, msg->pid, msg->cgroup, std::move(comm), std::nullopt);\n}\n\nvoid ProcessSpan::pid_info_create(\n    ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info_create *msg)\n{\n  std::string comm{(char const *)msg->comm, strnlen((char const *)msg->comm, sizeof(msg->comm))};\n\n  LOG::trace_in(\n      Component::process,\n      \"ProcessSpan::pid_info_create pid:{} cgroup:{} comm:{} cmdline:{}\",\n      msg->pid,\n      msg->cgroup,\n      comm,\n      msg->cmdline);\n\n  create_refs(span_ref, msg->pid, msg->cgroup, std::move(comm), msg->parent_pid);\n\n  parse_cmdline(span_ref, msg->cmdline.string_view());\n}\n\nvoid ProcessSpan::parse_cmdline(::ebpf_net::ingest::weak_refs::process span_ref, std::string_view cmdline)\n{\n  if (cmdline.size() == 0) {\n    return;\n  }\n\n  if (auto sep = cmdline.find('\\0'); sep != std::string_view::npos) {\n    auto prog = cmdline.substr(0, sep);\n    auto args = cmdline.substr(sep + 1);\n\n    std::string_view arg1;\n\n    if (auto sep2 = args.find('\\0'); sep2 != std::string_view::npos) {\n      arg1 = args.substr(0, sep2);\n    } else {\n      arg1 = args;\n    }\n\n    if ((prog == RUNIT_SERVICE_PROG_NAME) && !arg1.empty()) {\n      auto service_name = arg1;\n\n      auto service = span_ref.index().service.by_key({{short_string_behavior::truncate, service_name}});\n\n      if (!service.valid()) {\n        LOG::error(\"ProcessSpan: could not allocate a service span\");\n        return;\n      }\n\n      span_ref.modify().service(std::move(service));\n    }\n  }\n}\n\nvoid ProcessSpan::create_refs(\n    ::ebpf_net::ingest::weak_refs::process span_ref,\n    u32 pid,\n    const std::optional<u64> &cgroup,\n    std::string comm,\n    const std::optional<s32> &parent_pid)\n{\n  auto *conn = local_connection()->ingest_connection();\n\n  // TODO: make sure comm length is bounded\n  span_ref.modify().comm({comm.c_str(), comm.size()});\n\n  if (cgroup) {\n    if (auto cgroup_span = conn->get_cgroup(*cgroup); cgroup_span.valid()) {\n      span_ref.modify().cgroup(cgroup_span.get());\n    }\n  }\n\n  if (parent_pid.has_value() && (parent_pid.value() >= 0)) {\n    if (auto parent_span = conn->get_process(*parent_pid); parent_span.valid()) {\n      if (parent_span.service().valid()) {\n        // Inherit parent's service.\n        span_ref.modify().service(parent_span.service().get());\n      }\n    }\n  }\n}\n\nvoid ProcessSpan::pid_cgroup_move(\n    ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_cgroup_move *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n\n  LOG::trace_in(Component::process, \"ProcessSpan::pid_cgroup_move pid:{} cgroup:{}\", msg->pid, msg->cgroup);\n\n  auto cgroup_span = conn->get_cgroup(msg->cgroup);\n  if (!cgroup_span.valid()) {\n    local_logger().cgroup_not_found(msg->cgroup);\n    return;\n  }\n\n  span_ref.modify().cgroup(cgroup_span.get());\n}\n\nvoid ProcessSpan::pid_set_comm(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_set_comm *msg)\n{\n  std::string comm{(char const *)msg->comm, strnlen((char const *)msg->comm, sizeof(msg->comm))};\n\n  LOG::trace_in(Component::process, \"ProcessSpan::pid_set_comm pid:{}, comm:{}\", msg->pid, comm);\n\n  span_ref.modify().comm({comm.c_str(), comm.size()});\n}\n\nvoid ProcessSpan::pid_set_cmdline(\n    ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_set_cmdline *msg)\n{\n  LOG::trace_in(Component::process, \"ProcessSpan::pid_set_cmdline pid:{}\", msg->pid);\n\n  parse_cmdline(span_ref, msg->cmdline.string_view());\n}\n\nvoid ProcessSpan::pid_close_info(\n    ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_close_info *msg)\n{\n  LOG::trace_in(Component::process, \"ProcessSpan::pid_close_info pid:{}\", msg->pid);\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/process_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/handles.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n#include <generated/ebpf_net/ingest/weak_refs.h>\n\n#include <optional>\n#include <string>\n\nnamespace reducer::ingest {\n\nclass ProcessSpan : public ::ebpf_net::ingest::ProcessSpanBase {\npublic:\n  ProcessSpan();\n  ~ProcessSpan();\n\n  /* Deprecated handlers.\n   */\n  void pid_info(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info *msg);\n  void pid_info_create_deprecated(\n      ::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info_create_deprecated *msg);\n\n  /* Handlers.\n   */\n  void pid_info_create(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_info_create *msg);\n  void pid_cgroup_move(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_cgroup_move *msg);\n  void pid_set_comm(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_set_comm *msg);\n  void pid_set_cmdline(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_set_cmdline *msg);\n  void pid_close_info(::ebpf_net::ingest::weak_refs::process span_ref, u64 timestamp, jsrv_ingest__pid_close_info *msg);\n\nprivate:\n  void create_refs(\n      ::ebpf_net::ingest::weak_refs::process span_ref,\n      u32 pid,\n      const std::optional<u64> &cgroup,\n      std::string comm,\n      const std::optional<s32> &parent_pid);\n\n  void parse_cmdline(::ebpf_net::ingest::weak_refs::process span_ref, std::string_view cmdline);\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/shared_state.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"shared_state.h\"\n\n#include <generated/ebpf_net/ingest/index.h>\n\nnamespace reducer::ingest {\n\nnamespace {\n\nstruct GlobalState {\n  ThreadSafeMap<IPv6Address, IPv6Address> private_to_public_address_map;\n};\n\nstruct LocalState {\n  ::ebpf_net::ingest::Index *index = nullptr;\n  NpmConnection *connection = nullptr;\n  ::ebpf_net::ingest::auto_handles::logger *logger = nullptr;\n  ::ebpf_net::ingest::auto_handles::core_stats *core_stats = nullptr;\n  ::ebpf_net::ingest::auto_handles::ingest_core_stats *ingest_core_stats = nullptr;\n};\n\nGlobalState *global_state()\n{\n  static GlobalState state;\n  return &state;\n}\n\nLocalState *local_state()\n{\n  thread_local LocalState state;\n  return &state;\n}\n\n} // namespace\n\nThreadSafeMap<IPv6Address, IPv6Address> &global_private_to_public_address_map()\n{\n  return global_state()->private_to_public_address_map;\n}\n\n::ebpf_net::ingest::Index *local_index()\n{\n  assert(local_state()->index != nullptr);\n  return local_state()->index;\n}\n\nNpmConnection *local_connection()\n{\n  assert(local_state()->connection != nullptr);\n  return local_state()->connection;\n}\n\n::ebpf_net::ingest::weak_refs::logger local_logger()\n{\n  assert(local_state()->logger != nullptr);\n  return *(local_state()->logger);\n}\n\n::ebpf_net::ingest::weak_refs::core_stats local_core_stats_handle()\n{\n  assert(local_state()->core_stats != nullptr);\n  return *(local_state()->core_stats);\n}\n\n::ebpf_net::ingest::weak_refs::ingest_core_stats local_ingest_core_stats_handle()\n{\n  assert(local_state()->ingest_core_stats != nullptr);\n  return *(local_state()->ingest_core_stats);\n}\n\nvoid set_local_index(::ebpf_net::ingest::Index *const index)\n{\n  local_state()->index = index;\n}\n\nvoid set_local_connection(NpmConnection *const connection)\n{\n  local_state()->connection = connection;\n}\n\nvoid set_local_logger(::ebpf_net::ingest::auto_handles::logger *logger)\n{\n  local_state()->logger = logger;\n}\n\nvoid set_local_core_stats_handle(::ebpf_net::ingest::auto_handles::core_stats *core_stats)\n{\n  local_state()->core_stats = core_stats;\n}\n\nvoid set_local_ingest_core_stats_handle(::ebpf_net::ingest::auto_handles::ingest_core_stats *ingest_core_stats)\n{\n  local_state()->ingest_core_stats = ingest_core_stats;\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/shared_state.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/ingest/npm_connection.h>\n#include <reducer/thread_safe_map.h>\n\n#include <generated/ebpf_net/ingest/index.h>\n#include <generated/ebpf_net/logging/writer.h>\n\n#include <util/ip_address.h>\n\n// This library encapuslates a number of shared, mutable global values that are\n// accessed from many classes.\n// This library is used as a workaround for the inability to effectively pass\n// these values through render-generated code. Please, please do not add any\n// new values to this class if possible, since doing so makes things like\n// testing and behavior isolation much more difficult.\n\nnamespace reducer::ingest {\n\nThreadSafeMap<IPv6Address, IPv6Address> &global_private_to_public_address_map();\n\n// The Index and NpmConnection objects associated with the current thread.\n// The returned values are guaranteed to not be null, and will assert-fail if\n// they are.\n::ebpf_net::ingest::Index *local_index();\nNpmConnection *local_connection();\n::ebpf_net::ingest::weak_refs::logger local_logger();\n::ebpf_net::ingest::weak_refs::core_stats local_core_stats_handle();\n::ebpf_net::ingest::weak_refs::ingest_core_stats local_ingest_core_stats_handle();\n\n// Setters for the above values.\nvoid set_local_index(::ebpf_net::ingest::Index *index);\nvoid set_local_connection(NpmConnection *connection);\nvoid set_local_logger(::ebpf_net::ingest::auto_handles::logger *logger);\nvoid set_local_core_stats_handle(::ebpf_net::ingest::auto_handles::core_stats *core_stats);\nvoid set_local_ingest_core_stats_handle(::ebpf_net::ingest::auto_handles::ingest_core_stats *ingest_core_stats);\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/socket_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"socket_span.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <generated/ebpf_net/ingest/modifiers.h>\n\n#include <util/ip_address.h>\n#include <util/log.h>\n\n#include <arpa/inet.h>\n#include <config.h>\n\nnamespace reducer::ingest {\n\nSocketSpan::~SocketSpan()\n{\n  LOG::trace_in(\n      Component::socket, \"SocketSpan::~SocketSpan: {}:{} -> {}:{}\", local_addr_, local_port_, remote_addr_, remote_port_);\n}\n\nvoid SocketSpan::new_sock_info(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__new_sock_info *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n\n  // get a reference to the process\n  if (auto process_ref = conn->get_process(msg->pid); process_ref.valid()) {\n    span_ref.modify().process(process_ref.get());\n  } else {\n    local_logger().tcp_socket_failed_getting_process_reference(msg->pid);\n  }\n}\n\nvoid SocketSpan::set_state_ipv4(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__set_state_ipv4 *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  auto local_addr = IPv4Address::from(msg->src);\n  auto remote_addr = IPv4Address::from(msg->dest);\n  auto local_port = msg->sport;\n  auto remote_port = msg->dport;\n\n  LOG::trace_in(\n      Component::socket,\n      \"SocketSpan::set_state_ipv4: sk={}, src={}:{}, dst={}:{}, tx_rx={}\",\n      msg->sk,\n      local_addr,\n      local_port,\n      remote_addr,\n      remote_port,\n      msg->tx_rx);\n\n  auto local_addr6 = local_addr.to_ipv6();\n  auto remote_addr6 = remote_addr.to_ipv6();\n\n  if (flow_updater_ && flow_updater_->valid()) {\n    if (std::tie(local_addr6, local_port, remote_addr6, remote_port) !=\n        std::tie(local_addr_, local_port_, remote_addr_, remote_port_)) {\n      local_logger().socket_address_already_assigned();\n    }\n    return;\n  }\n\n  /* save the IP addresses as IPv6-mapped IPv4 */\n  local_addr_ = local_addr6;\n  remote_addr_ = remote_addr6;\n\n  /* save ports */\n  local_port_ = local_port;\n  remote_port_ = remote_port;\n\n  /* connector/acceptor */\n  is_connector_ = msg->tx_rx;\n\n  if (agent.is_socket_steady_state()) {\n    new_sockets_ = 1;\n  }\n\n  /* save the original remote address, before any address translations */\n  original_remote_addr_ = remote_addr_;\n\n  /* enrich with recent DNS query on remote endpoint, if available */\n  if (!remote_dns_.has_value()) {\n    if (auto dns = agent.find_dns_for_ip(original_remote_addr_)) {\n      remote_dns_.emplace(short_string_behavior::no_truncate, *dns);\n    }\n  }\n\n  get_flow(span_ref);\n}\n\nvoid SocketSpan::set_state_ipv6(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__set_state_ipv6 *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  auto local_addr = IPv6Address::from(msg->src);\n  auto remote_addr = IPv6Address::from(msg->dest);\n  auto local_port = msg->sport;\n  auto remote_port = msg->dport;\n\n  LOG::trace_in(\n      Component::socket,\n      \"SocketSpan::set_state_ipv6: sk={}, src={}:{}, dst={}:{}, tx_rx={}\",\n      msg->sk,\n      local_addr,\n      local_port,\n      remote_addr,\n      remote_port,\n      msg->tx_rx);\n\n  if (flow_updater_ && flow_updater_->valid()) {\n    if (std::tie(local_addr, local_port, remote_addr, remote_port) !=\n        std::tie(local_addr_, local_port_, remote_addr_, remote_port_)) {\n      local_logger().socket_address_already_assigned();\n    }\n    return;\n  }\n\n  /* save the IPv6 addresses */\n  local_addr_ = local_addr;\n  remote_addr_ = remote_addr;\n\n  /* save ports */\n  local_port_ = local_port;\n  remote_port_ = remote_port;\n\n  /* connector/acceptor */\n  is_connector_ = msg->tx_rx;\n\n  if (agent.is_socket_steady_state()) {\n    new_sockets_ = 1;\n  }\n\n  /* save the original remote address, before any address translations */\n  original_remote_addr_ = remote_addr_;\n\n  /* enrich with recent DNS query on remote endpoint, if available */\n  if (!remote_dns_.has_value()) {\n    if (auto dns = agent.find_dns_for_ip(original_remote_addr_)) {\n      remote_dns_.emplace(short_string_behavior::no_truncate, *dns);\n    }\n  }\n\n  get_flow(span_ref);\n}\n\nvoid SocketSpan::socket_stats(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__socket_stats *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  LOG::trace_in(\n      Component::socket,\n      \"SocketSpan::socket_stats: sk={}, {}:{} -> {}:{}, diff_bytes={},\"\n      \" diff_delivered={}, diff_retrans={}, max_srtt={}, is_rx={}\",\n      msg->sk,\n      local_addr_,\n      local_port_,\n      remote_addr_,\n      remote_port_,\n      msg->diff_bytes,\n      msg->diff_delivered,\n      msg->diff_retrans,\n      msg->max_srtt,\n      msg->is_rx);\n\n  if (msg->is_rx > 1)\n    return; /* ignore is_rx not in {0,1} */\n\n  // Correct small negative differences\n  u64 diff_bytes = msg->diff_bytes;\n  if (unlikely(diff_bytes > static_cast<u64>(1ULL << 56ULL))) {\n    // should not happen (after agent ~0.8.2390)\n    LOG::error(\"truncating negative diff_bytes: was 0x{:x}\", diff_bytes);\n    diff_bytes = 0;\n  }\n  u32 diff_retrans = msg->diff_retrans;\n  if (unlikely(diff_retrans > static_cast<u32>(1UL << 28UL))) {\n    // should not happen (after agent ~0.8.2390)\n    LOG::error(\"truncating negative diff_retrans: was 0x{:x}\", diff_retrans);\n    diff_retrans = 0;\n  }\n  u32 diff_delivered = msg->diff_delivered;\n  if (unlikely(diff_delivered > static_cast<u32>(1UL << 28UL))) {\n    // should not happen (after agent ~0.8.2390)\n    LOG::error(\"truncating negative diff_delivered: was 0x{:x}\", diff_delivered);\n    diff_delivered = 0;\n  }\n\n  ::ebpf_net::metrics::tcp_metrics_point stats = {\n      .active_sockets = 1,\n      .sum_retrans = diff_retrans,\n      .sum_bytes = diff_bytes,\n      .sum_srtt = msg->max_srtt,\n      .sum_delivered = diff_delivered,\n      .active_rtts = 1,\n      .syn_timeouts = 0,\n      .new_sockets = new_sockets_,\n      .tcp_resets = 0,\n  };\n  new_sockets_ = 0;\n\n  if (!remote_dns_.has_value()) {\n    // retry getting remote DNS name\n    if (auto dns = agent.find_dns_for_ip(original_remote_addr_)) {\n      remote_dns_.emplace(short_string_behavior::no_truncate, *dns);\n      get_flow(span_ref);\n    }\n  }\n\n  if (flow_updater_) {\n    flow_updater_->tcp_update(timestamp, stats, msg->is_rx);\n  }\n}\n\nvoid SocketSpan::syn_timeout(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__syn_timeout *msg)\n{\n  ::ebpf_net::metrics::tcp_metrics_point stats = {\n      .active_sockets = 1,\n      .sum_retrans = 0,\n      .sum_bytes = 0,\n      .sum_srtt = 0,\n      .sum_delivered = 0,\n      .active_rtts = 0,\n      .syn_timeouts = 1,\n      .new_sockets = 0,\n      .tcp_resets = 0,\n  };\n\n  if (flow_updater_) {\n    flow_updater_->tcp_update(\n        timestamp,\n        stats,\n        /* timeouts are always tx: is_rx is 0 */ 0);\n  }\n}\n\nvoid SocketSpan::tcp_reset(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__tcp_reset *msg)\n{\n  ::ebpf_net::metrics::tcp_metrics_point stats = {\n      .active_sockets = 1,\n      .sum_retrans = 0,\n      .sum_bytes = 0,\n      .sum_srtt = 0,\n      .sum_delivered = 0,\n      .active_rtts = 0,\n      .syn_timeouts = 0,\n      .new_sockets = 0,\n      .tcp_resets = 1,\n  };\n\n  if (flow_updater_) {\n    flow_updater_->tcp_update(timestamp, stats, msg->is_rx);\n  }\n}\n\nvoid SocketSpan::http_response(\n    ::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, struct jsrv_ingest__http_response *msg)\n{\n  const enum CLIENT_SERVER_TYPE client_server = (const enum CLIENT_SERVER_TYPE)msg->client_server;\n  ::ebpf_net::metrics::http_metrics_point stats = {\n      .active_sockets = 1,\n      .sum_code_200 = 0,\n      .sum_code_400 = 0,\n      .sum_code_500 = 0,\n      .sum_code_other = 0,\n      .sum_total_time_ns = (client_server == SC_CLIENT) ? msg->latency_ns : 0, // client latency contributes to total time\n      .sum_processing_time_ns =\n          (client_server == SC_SERVER) ? msg->latency_ns : 0 // server latency contributes to processing time\n  };\n\n  if (msg->code >= 200 && msg->code <= 299) {\n    stats.sum_code_200 = 1;\n  } else if (msg->code >= 400 && msg->code <= 499) {\n    stats.sum_code_400 = 1;\n  } else if (msg->code >= 500 && msg->code <= 599) {\n    stats.sum_code_500 = 1;\n  } else {\n    stats.sum_code_other = 1;\n  }\n\n  if (flow_updater_) {\n    flow_updater_->http_update(timestamp, stats, msg->client_server);\n  }\n}\n\nvoid SocketSpan::get_flow(::ebpf_net::ingest::weak_refs::socket span_ref)\n{\n  auto *conn = local_connection()->ingest_connection();\n  auto agent = conn->agent();\n  auto &addr_map = global_private_to_public_address_map();\n\n  auto local_addr = local_addr_;\n  auto remote_addr = remote_addr_;\n\n  // Using the globally-shared private-to-public address map, translate both\n  // local and remote addresses to their public equivalent, if such mapping\n  // exists.\n  //\n  if (auto public_addr = addr_map.get(local_addr); public_addr) {\n    local_addr = *public_addr;\n  }\n  if (auto public_addr = addr_map.get(remote_addr); public_addr) {\n    remote_addr = *public_addr;\n  }\n\n  if (local_addr.is_localhost() || (local_addr.is_ipv4() && (local_addr.to_ipv4()->is_localhost()))) {\n    LOG::trace_in(\n        Component::socket,\n        \"SocketSpan::get_flow: ignoring localhost traffic:\"\n        \" {}:{} -> {}:{}\",\n        local_addr,\n        local_port_,\n        remote_addr,\n        remote_port_);\n    flow_updater_.reset();\n    return;\n  }\n\n  if ((local_addr == remote_addr) && !agent.impl().is_host_address(local_addr)) {\n    LOG::trace_in(\n        Component::socket,\n        \"SocketSpan::get_flow: ignoring local traffic:\"\n        \" {}:{} -> {}:{}\",\n        local_addr,\n        local_port_,\n        remote_addr,\n        remote_port_);\n    flow_updater_.reset();\n    return;\n  }\n\n  LOG::trace_in(Component::socket, \"SocketSpan::get_flow: {}:{} -> {}:{}\", local_addr, local_port_, remote_addr, remote_port_);\n\n  flow_updater_ =\n      FlowUpdater(span_ref.process(), agent, local_addr, local_port_, remote_addr, remote_port_, is_connector_, remote_dns_);\n}\n\nvoid SocketSpan::nat_remapping(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__nat_remapping *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  auto nat_src = IPv4Address::from(msg->src);\n  auto nat_dst = IPv4Address::from(msg->dst);\n  // NOTE: ports reported in the nat_remapping message are in the network byte\n  // order, differing from other messages (e.g. set_state_ipv4).\n  u16 nat_sport = ntohs(msg->sport);\n  u16 nat_dport = ntohs(msg->dport);\n\n  LOG::trace_in(\n      Component::socket,\n      \"SocketSpan::nat_remapping: sk={}, {}:{} -> {}:{},\"\n      \" src={}:{}, dst={}:{}\",\n      msg->sk,\n      local_addr_,\n      local_port_,\n      remote_addr_,\n      remote_port_,\n      nat_src,\n      nat_sport,\n      nat_dst,\n      nat_dport);\n\n  // Wasn't sure if we only ever see DNAT, so did the \"safe\" thing and assumed\n  // both could change entirely.\n\n  if (agent.is_host_address(nat_src.to_ipv6())) {\n    // remap local address only if it maps to a known host address\n    local_addr_ = nat_src.to_ipv6();\n    local_port_ = nat_sport;\n  } else {\n    LOG::trace_in(Component::socket, \"SocketSpan::nat_remapping: not remapping local address\");\n  }\n\n  // save NAT-ed remote address and port\n  remote_addr_ = nat_dst.to_ipv6();\n  remote_port_ = nat_dport;\n\n  LOG::trace_in(\n      Component::socket,\n      \"SocketSpan::nat_remapping: sk={}, {}:{} -> {}:{}\",\n      msg->sk,\n      local_addr_,\n      local_port_,\n      remote_addr_,\n      remote_port_);\n\n  // recompute the flow of this socket\n  get_flow(span_ref);\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/socket_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/ingest/flow_updater.h>\n\n#include <reducer/dns_cache.h>\n\n#include <generated/ebpf_net/ingest/parsed_message.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n\n#include <platform/platform.h>\n#include <util/ip_address.h>\n\n#include <optional>\n\nnamespace reducer::ingest {\n\nclass SocketSpan : public ::ebpf_net::ingest::SocketSpanBase {\npublic:\n  ~SocketSpan();\n\n  void new_sock_info(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__new_sock_info *msg);\n  void set_state_ipv4(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__set_state_ipv4 *msg);\n  void set_state_ipv6(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__set_state_ipv6 *msg);\n  void socket_stats(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__socket_stats *msg);\n  void nat_remapping(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__nat_remapping *msg);\n  void syn_timeout(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__syn_timeout *msg);\n  void tcp_reset(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__tcp_reset *msg);\n  void http_response(::ebpf_net::ingest::weak_refs::socket span_ref, u64 timestamp, jsrv_ingest__http_response *msg);\n\nprivate:\n  IPv6Address local_addr_;\n  u16 local_port_ = 0;\n  IPv6Address remote_addr_;\n  u16 remote_port_ = 0;\n  u32 is_connector_ = 0; /* 0: unknown, 1: connector, 2: listener */\n  u32 new_sockets_ = 0;\n\n  // Original value or the remote address, before any translations.\n  IPv6Address original_remote_addr_;\n  // DNS record for the remote address, if any.\n  std::optional<dns::dns_record> remote_dns_;\n\n  std::optional<FlowUpdater> flow_updater_;\n\n  void get_flow(::ebpf_net::ingest::weak_refs::socket span_ref);\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/tcp_server.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"tcp_server.h\"\n\n#include <reducer/ingest/ingest_worker.h>\n\n#include <util/uv_helpers.h>\n\n#include <iomanip>\n#include <memory>\n#include <signal.h>\n#include <sstream>\n\n#define SERVER_LISTEN_BACKLOG 128\n\nusing std::placeholders::_1;\nusing std::placeholders::_2;\n\nnamespace reducer::ingest {\n\nvoid TcpServer::on_new_connection_cb(uv_stream_t *stream, int status)\n{\n  uv_tcp_t *server_socket = (uv_tcp_t *)stream;\n  TcpServer *server = (TcpServer *)server_socket->data;\n\n  if (status != 0) {\n    LOG::error(\"Error creating new connection: {}\", uv_strerror(status));\n    return;\n  }\n  server->on_new_connection();\n}\n\nTcpServer::TcpServer(uv_loop_t &loop, u32 telemetry_port, bool localhost, std::vector<std::unique_ptr<IngestWorker>> workers)\n    : loop_(loop), workers_(std::move(workers))\n{\n  // Initialize the workers.\n  std::vector<Worker *> worker_ptrs;\n  for (std::size_t i = 0; i < workers_.size(); ++i) {\n    IngestWorker *const worker_ptr = workers_[i].get();\n    worker_ptrs.push_back(worker_ptr);\n\n    worker_ptr->register_close_callback([this, worker_ptr] { on_connection_close(worker_ptr); });\n    worker_ptr->start(i);\n  }\n  worker_balancer_ = std::make_unique<LoadBalancer<Worker *>>(absl::MakeSpan(worker_ptrs));\n\n  /* Listen for telemetry connections */\n  CHECK_UV(uv_tcp_init(&loop_, &server_));\n  /* save this for callbacks from libuv */\n  server_.data = this;\n\n  /* bind + listen */\n  struct sockaddr_in addr;\n  CHECK_UV(uv_ip4_addr(localhost ? \"127.0.0.1\" : \"0.0.0.0\", telemetry_port, &addr));\n  CHECK_UV(uv_tcp_bind(&server_, (struct sockaddr *)&addr, 0));\n  CHECK_UV(uv_listen((uv_stream_t *)&server_, SERVER_LISTEN_BACKLOG, on_new_connection_cb));\n}\n\nTcpServer::~TcpServer()\n{\n  for (auto &worker : workers_) {\n    worker->stop();\n  }\n}\n\nTcpServer::Stats TcpServer::get_stats()\n{\n  absl::ReaderMutexLock l(&stats_mu_);\n  return stats_;\n}\n\nvoid TcpServer::visit_indexes(const IndexCb &cb, const bool block)\n{\n  visit_internal(\n      [&cb](const int worker_index, IngestWorker *const worker) {\n        IngestWorker::IndexCb worker_cb = std::bind(cb, worker_index, _1);\n        return worker->visit_index(std::move(worker_cb));\n      },\n      block);\n}\n\nvoid TcpServer::visit_connections(const ConnectionCb &cb, const bool block)\n{\n  visit_internal(\n      [&cb](const int worker_index, IngestWorker *const worker) {\n        IngestWorker::ConnectionCb worker_cb = std::bind(cb, worker_index, _1);\n        return worker->visit_connections(std::move(worker_cb));\n      },\n      block);\n}\n\nvoid TcpServer::visit_channels(const ChannelCb &cb, const bool block)\n{\n  visit_internal(\n      [&cb](const int worker_index, IngestWorker *const worker) {\n        IngestWorker::ChannelCb worker_cb = std::bind(cb, worker_index, _1, _2);\n        return worker->visit_channels(std::move(worker_cb));\n      },\n      block);\n}\n\nvoid TcpServer::visit_rpc_stats(const RpcStatsCb &cb, const bool block)\n{\n  visit_internal(\n      [&cb](const int worker_index, IngestWorker *const worker) {\n        IngestWorker::RpcStatsCb worker_cb = std::bind(cb, worker_index, _1);\n        return worker->visit_rpc_stats(std::move(worker_cb));\n      },\n      block);\n}\n\nTcpServer::Singleton *TcpServer::singleton()\n{\n  static auto *const value = new Singleton;\n  return value;\n}\n\nvoid TcpServer::on_new_connection()\n{\n  // Accept the new connection.\n  auto *const conn = reinterpret_cast<uv_tcp_t *>(std::malloc(sizeof(uv_tcp_t)));\n  CHECK_UV(uv_tcp_init(&loop_, conn));\n  CHECK_UV(uv_accept(reinterpret_cast<uv_stream_t *>(&server_), reinterpret_cast<uv_stream_t *>(conn)));\n\n  // Hand off connection to worker.\n  Worker *const worker = worker_balancer_->least_loaded();\n  worker_balancer_->increment_load(worker, 1);\n  worker->assign(*conn);\n\n  // Close the connnection.\n  uv_close(reinterpret_cast<uv_handle_t *>(conn), reinterpret_cast<uv_close_cb>(&std::free));\n\n  // Update the stats\n  {\n    absl::MutexLock l(&stats_mu_);\n    stats_.connection_counter++;\n  }\n}\n\nvoid TcpServer::on_connection_close(IngestWorker *const worker)\n{\n  // Update the load balancer.\n  worker_balancer_->increment_load(worker, -1);\n\n  // Update the connection stats.\n  {\n    absl::MutexLock l(&stats_mu_);\n    stats_.disconnect_counter++;\n  }\n}\n\nvoid TcpServer::visit_internal(const WorkerVisitCb &cb, const bool block)\n{\n  // Run all the callbacks in parallel.\n  std::vector<std::shared_ptr<absl::Notification>> notifications;\n  for (std::size_t worker_index = 0; worker_index < workers_.size(); worker_index++) {\n    notifications.push_back(cb(worker_index, workers_[worker_index].get()));\n  }\n\n  // Wait for them to finish.\n  if (block) {\n    for (const auto &notification : notifications) {\n      notification->WaitForNotification();\n    }\n  }\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/tcp_server.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"absl/base/thread_annotations.h\"\n#include <reducer/ingest/ingest_worker.h>\n#include <reducer/load_balancer.h>\n#include <reducer/prometheus_handler.h>\n\n#include <generated/ebpf_net/ingest/index.h>\n\n#include <uv.h>\n\n#include <cstddef>\n\nnamespace reducer::ingest {\n\n// TcpServer is responsible for opening a TCP server port, accepting\n// connecitons, and assigning them to workers for processing. It transitively\n// uses all the Index objects used in the first stage of the pipeline.\nclass TcpServer {\npublic:\n  struct Stats {\n    Stats() : connection_counter(0), disconnect_counter(0) {}\n\n    u64 connection_counter;\n    u64 disconnect_counter;\n  };\n\n  // Arguments:\n  // * loop - The loop on which the port will be opneed.\n  // * telemetry_port - The port the tcp connection will listen on.\n  // * localhsot - If true, connects to 127.0.0.1, otherwise uses 0.0.0.0\n  // * workers - The ingest workers owned by this class. This constructor\n  //    will overwrite the close callback used by these workers.\n  TcpServer(uv_loop_t &loop, u32 telemetry_port, bool localhost, std::vector<std::unique_ptr<IngestWorker>> workers);\n  ~TcpServer();\n\n  // Returns various stats related to connects/disconnects, etc.\n  Stats get_stats();\n\n  // Calls `cb` on the Index objects owned by the workers of this class (the\n  // int argument is the index of the worker).\n  // These will be executed in parallel. If `block` is true, this function will\n  // block until each callback has finished, otherwise it will return early\n  // while the callbacks run in the background.\n  using IndexCb = std::function<void(int, ::ebpf_net::ingest::Index *)>;\n  void visit_indexes(const IndexCb &cb, bool block);\n\n  // Same as above, but for NpmConnection instances. The int parameter\n  // is still like IngestWorker index, like above, not the NpmConnection\n  // index.\n  using ConnectionCb = std::function<void(int, NpmConnection *)>;\n  void visit_connections(const ConnectionCb &cb, bool block);\n\n  // Same as above, but for channels.\n  using ChannelCb = std::function<void(int, channel::TCPChannel *, std::chrono::nanoseconds)>;\n  void visit_channels(const ChannelCb &cb, bool block);\n\n  // Same as above, but for RpcSenderStats.\n  using RpcStatsCb = std::function<void(int, RpcSenderStats &)>;\n  void visit_rpc_stats(const RpcStatsCb &cb, bool block);\n\n  std::size_t workers_count() const { return workers_.size(); }\n\n  // Global accessor for the TcpSever. Used by classes who want use the\n  // `visit_*` functions but do not have direct access to a `TcpSever` instance\n  // for whatever reason (e.g. render-instantiated `*Span` classes).\n  // It is the caller's responsibility to make sure that `instance` has already\n  // been set to a valid value before using it.\n  struct Singleton {\n  private:\n    // Classes are given whitelisted access. Global variables are generally an\n    // antipattern, and therefore this list should only be extended if there is\n    // no other reasonable alternative.\n\n    // Classes that set `instance`.\n    friend class IngestCore;\n    friend class TestServer; // Used in agent_span_pod_test.cc\n\n    // Classes that read `instance`.\n    friend class AgentSpan;\n\n    TcpServer *instance = nullptr;\n  };\n\n  static Singleton *singleton();\n\nprivate:\n  // libuv callback invoked when a new conneciton arrives.\n  static void on_new_connection_cb(uv_stream_t *stream, int status);\n\n  // Basically same as above, but as member function.\n  void on_new_connection();\n\n  // Callback invoked when a connection on `worker` has been closed.\n  void on_connection_close(IngestWorker *worker);\n\n  // Internal visitor implementation.\n  using WorkerVisitCb = std::function<std::shared_ptr<absl::Notification>(int, IngestWorker *)>;\n  void visit_internal(const WorkerVisitCb &cb, bool block);\n\n  uv_loop_t &loop_;\n  uv_tcp_t server_;\n\n  std::vector<std::unique_ptr<IngestWorker>> workers_;\n  std::unique_ptr<LoadBalancer<Worker *>> worker_balancer_;\n\n  Stats stats_ ABSL_GUARDED_BY(stats_mu_);\n  mutable absl::Mutex stats_mu_;\n};\n\n} /* namespace reducer::ingest */\n"
  },
  {
    "path": "reducer/ingest/udp_socket_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"udp_socket_span.h\"\n\n#include <reducer/ingest/component.h>\n#include <reducer/ingest/shared_state.h>\n\n#include <generated/ebpf_net/ingest/modifiers.h>\n#include <generated/ebpf_net/metrics.h>\n\n#include <util/log.h>\n\n#include <cstring>\n\nnamespace reducer::ingest {\n\nnamespace {\n\n// Version in which the IPv6 address bug was fixed in the kernel collector.\n// That bug was causing spurious addresses to be reported for UDP sockets.\n// Temporary pipeline fix was to ignore UDP metrics.\nstatic constexpr VersionInfo UDP_ADDRESS_FIX_VERSION(0, 8, 2082);\n\n} // namespace\n\nUdpSocketSpan::~UdpSocketSpan() {}\n\nvoid UdpSocketSpan::udp_new_socket(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_new_socket *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  u32 *laddr = reinterpret_cast<u32 *>(msg->laddr);\n\n  if ((laddr[0] | laddr[1] | (laddr[2] ^ 0xffff0000)) == 0) {\n    /// IPv6-encoded IPv4 address\n    local_addr_ = IPv4Address::from(laddr[3]).to_ipv6();\n  } else {\n    local_addr_ = IPv6Address::from(msg->laddr);\n  }\n\n  local_port_ = msg->lport;\n\n  // get a reference to the process\n  if (auto process_ref = conn->get_process(msg->pid); process_ref.valid()) {\n    span_ref.modify().process(process_ref.get());\n  } else {\n    local_logger().udp_socket_failed_getting_process_reference(msg->pid);\n  }\n\n  if (agent.version() < UDP_ADDRESS_FIX_VERSION) {\n    ignore_udp_ = true;\n  }\n}\n\nvoid UdpSocketSpan::udp_stats_addr_unchanged(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_unchanged *msg)\n{\n  if (msg->is_rx > 1) {\n    return; /* TODO: log */\n  }\n\n  if (!ignore_udp_) {\n    update_udp_stats(timestamp, msg->is_rx, 0 /* addr_changed */, msg->packets, msg->bytes, 0 /* drops */);\n  }\n}\n\nvoid UdpSocketSpan::udp_stats_addr_changed_v4(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_changed_v4 *msg)\n{\n  if (msg->is_rx > 1) {\n    return; /* TODO: log */\n  }\n\n  auto remote_addr = IPv4Address::from(msg->raddr).to_ipv6();\n  auto remote_port = msg->rport;\n\n  remote_ip_[msg->is_rx] = AddrPort{remote_addr, remote_port};\n\n  if (!ignore_udp_ || is_dns_) {\n    // if UDP metrics are disabled, we only need the updater for DNS metrics\n    update_handle(span_ref, msg->is_rx);\n  }\n\n  if (!ignore_udp_) {\n    update_udp_stats(timestamp, msg->is_rx, 1 /* addr_changed */, msg->packets, msg->bytes, 0 /* drops */);\n  }\n}\n\nvoid UdpSocketSpan::udp_stats_addr_changed_v6(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_changed_v6 *msg)\n{\n  if (msg->is_rx > 1) {\n    return; /* TODO: log */\n  }\n\n  auto remote_addr = IPv6Address::from(msg->raddr);\n  auto remote_port = msg->rport;\n\n  remote_ip_[msg->is_rx] = AddrPort{remote_addr, remote_port};\n\n  if (!ignore_udp_ || is_dns_) {\n    // if UDP metrics are disabled, we only need the updater for DNS metrics\n    update_handle(span_ref, msg->is_rx);\n  }\n\n  if (!ignore_udp_) {\n    update_udp_stats(timestamp, msg->is_rx, 1 /* addr_changed */, msg->packets, msg->bytes, 0 /* drops */);\n  }\n}\n\nvoid UdpSocketSpan::dns_response_dep_b(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_response_dep_b *msg)\n{\n  // for older agents just put dns responses on the client side for now\n  jsrv_ingest__dns_response newmsg;\n  newmsg._rpc_id = jsrv_ingest__dns_response__rpc_id;\n  newmsg.total_dn_len = msg->total_dn_len;\n  newmsg.sk_id = msg->sk_id;\n  newmsg.domain_name = msg->domain_name;\n  newmsg.ipv4_addrs = msg->ipv4_addrs;\n  newmsg.ipv6_addrs = msg->ipv6_addrs;\n  newmsg.latency_ns = msg->latency_ns;\n\n  // for older agents, just key client_server off of port 53 (like old is_dns_rx)\n  newmsg.client_server = (u8)((local_port_ == kPortDNS) ? SC_SERVER : SC_CLIENT);\n\n  // pass new version of message through\n  dns_response(span_ref, timestamp, &newmsg);\n}\n\nvoid UdpSocketSpan::dns_response(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_response *msg)\n{\n  auto *conn = local_connection()->ingest_connection();\n  AgentSpan &agent = conn->agent().impl();\n\n  // copy because we're not sure of message alignment\n  int num_ipv4_addrs = msg->ipv4_addrs.len / sizeof(in_addr);\n  in_addr ipv4_addrs[num_ipv4_addrs];\n  memcpy(ipv4_addrs, msg->ipv4_addrs.buf, sizeof(in_addr) * num_ipv4_addrs);\n\n  int num_ipv6_addrs = msg->ipv6_addrs.len / sizeof(in6_addr);\n  in6_addr ipv6_addrs[num_ipv6_addrs];\n  memcpy(ipv6_addrs, msg->ipv6_addrs.buf, sizeof(in6_addr) * num_ipv6_addrs);\n\n  std::string_view domain_name{msg->domain_name.buf, msg->domain_name.len};\n\n  LOG::debug_in(\n      Component::dns,\n      \"UdpSocketSpan::dns_response: timestamp={}, sk_id={}, domain_name={}, \"\n      \"num_ipv4_addrs={}, num_ipv6_addrs={}, latency_ns={} \",\n      timestamp,\n      msg->sk_id,\n      domain_name,\n      num_ipv4_addrs,\n      num_ipv6_addrs,\n      msg->latency_ns);\n\n  agent.map_ips_to_domain(ipv4_addrs, num_ipv4_addrs, ipv6_addrs, num_ipv6_addrs, domain_name);\n\n  // Update DNS stats.\n  const enum CLIENT_SERVER_TYPE client_server = (const enum CLIENT_SERVER_TYPE)msg->client_server;\n  update_dns_stats(\n      span_ref,\n      timestamp,\n      client_server,\n      ::ebpf_net::metrics::dns_metrics_point{\n          .active_sockets = 1,\n          .requests_a = (num_ipv4_addrs > 0),\n          .requests_aaaa = (num_ipv6_addrs > 0),\n          .responses = 1,\n          .timeouts = 0,\n          .sum_total_time_ns = (client_server == SC_CLIENT) ? msg->latency_ns : 0, // client latency contributes to total time\n          .sum_processing_time_ns =\n              (client_server == SC_SERVER) ? msg->latency_ns : 0 // server latency contributes to processing time\n      },\n      false);\n}\n\nvoid UdpSocketSpan::dns_timeout(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_timeout *msg)\n{\n  LOG::debug_in(\n      Component::dns,\n      \"UdpSocketSpan::dns_timeout: timestamp={}, sk_id={}, \"\n      \"domain_name={}, timeout_ns={} \",\n      timestamp,\n      msg->sk_id,\n      std::string_view(msg->domain_name.buf, msg->domain_name.len),\n      msg->timeout_ns);\n\n  // Update DNS stats.\n  update_dns_stats(\n      span_ref,\n      timestamp,\n      SC_CLIENT, // dns stats for timeouts are always client side\n      ::ebpf_net::metrics::dns_metrics_point{\n          .active_sockets = 1,\n          .requests_a = 0,\n          .requests_aaaa = 0,\n          .responses = 0,\n          .timeouts = 1,\n          .sum_total_time_ns = 0,     // timeout duration does not contribute here\n          .sum_processing_time_ns = 0 // timeout duration does not contribute here\n      },\n      true);\n}\n\nvoid UdpSocketSpan::udp_stats_drops_changed(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_drops_changed *msg)\n{\n  LOG::debug_in(Component::udp_drops, \"UDP Drops: {}\", msg->drops);\n\n  // Always assume that drops are for received, not transmit.\n  update_udp_stats(timestamp, true /* is_rx */, 0 /* addr_changed */, 0 /* packets */, 0 /* bytes */, msg->drops);\n}\n\nvoid UdpSocketSpan::update_handle(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u8 is_rx)\n{\n  auto *conn = local_connection()->ingest_connection();\n  auto agent = conn->agent();\n  auto &addr_map = global_private_to_public_address_map();\n\n  auto local_addr = local_addr_;\n\n  auto remote_addr = remote_ip_[is_rx].addr;\n  auto remote_port = remote_ip_[is_rx].port;\n\n  // first, try to translate DNS. Do this before private-public mapping, as\n  // the address that DNS would have returned is the socket's remote address,\n  // not the mapped addresss.\n  auto remote_dns = agent.impl().find_dns_for_ip(remote_addr).value_or(std::string_view());\n\n  // Using the globally-shared private-to-public address map, translate both\n  // local and remote addresses to their public equivalent, if such mapping\n  // exists.\n  //\n  if (auto public_addr = addr_map.get(local_addr); public_addr) {\n    local_addr = *public_addr;\n  }\n  if (auto public_addr = addr_map.get(remote_addr); public_addr) {\n    remote_addr = *public_addr;\n  }\n\n  if (local_addr.is_localhost() || (local_addr.is_ipv4() && (local_addr.to_ipv4()->is_localhost()))) {\n    LOG::trace_in(\n        Component::udp,\n        \"UdpSocketSpan::update_handle: ignoring localhost traffic:\"\n        \" {}:{} -> {}:{}\",\n        local_addr,\n        local_port_,\n        remote_addr,\n        remote_port);\n    flow_updater_[is_rx].reset();\n    return;\n  }\n\n  if ((local_addr == remote_addr) && !agent.impl().is_host_address(local_addr)) {\n    LOG::trace_in(\n        Component::udp,\n        \"UdpSocketSpan::update_handle: ignoring local traffic:\"\n        \" {}:{} -> {}:{}\",\n        local_addr,\n        local_port_,\n        remote_addr,\n        remote_port);\n    flow_updater_[is_rx].reset();\n    return;\n  }\n\n  LOG::trace_in(\n      Component::udp, \"UdpSocketSpan::update_handle: {}:{} -> {}:{}\", local_addr, local_port_, remote_addr, remote_port);\n\n  flow_updater_[is_rx] = FlowUpdater(\n      span_ref.process(),\n      agent,\n      local_addr,\n      local_port_,\n      remote_addr,\n      remote_port,\n      0,\n      dns::dns_record{short_string_behavior::no_truncate, remote_dns});\n}\n\nvoid UdpSocketSpan::update_udp_stats(u64 timestamp, u8 is_rx, u32 addr_changed, u32 packets, u32 bytes, u32 drops)\n{\n  ::ebpf_net::metrics::udp_metrics_point stats = {\n      .active_sockets = 1,\n      .addr_changes = addr_changed,\n      .packets = packets,\n      .bytes = bytes,\n      .drops = drops,\n  };\n\n  auto &flow_updater = flow_updater_[is_rx];\n  if (flow_updater) {\n    flow_updater->udp_update(timestamp, stats, is_rx);\n  }\n}\n\nvoid UdpSocketSpan::update_dns_stats(\n    ::ebpf_net::ingest::weak_refs::udp_socket span_ref,\n    const u64 timestamp,\n    const CLIENT_SERVER_TYPE client_server,\n    const ::ebpf_net::metrics::dns_metrics_point &metrics,\n    bool is_timeout)\n{\n  // we -received- the dns response if we are the client\n  u8 is_rx = (client_server == SC_CLIENT) ? 1 : 0;\n\n  // use server side for timeouts since we may not have RX address\n  if (is_timeout)\n    is_rx = 0;\n\n  is_dns_ = true;\n\n  // if UDP stats are being ignored, the updater will not be created until the\n  // first time we get UDP stats\n  if (!flow_updater_[is_rx].has_value()) {\n    update_handle(span_ref, is_rx);\n  }\n\n  auto &flow_updater = flow_updater_[is_rx];\n  if (flow_updater) {\n    flow_updater->dns_update(timestamp, metrics, client_server);\n  }\n}\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/ingest/udp_socket_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/ingest/flow_updater.h>\n\n#include <generated/ebpf_net/ingest/parsed_message.h>\n#include <generated/ebpf_net/ingest/span_base.h>\n\n#include <common/client_server_type.h>\n#include <platform/platform.h>\n#include <util/ip_address.h>\n\n#include <array>\n#include <optional>\n\nnamespace reducer::ingest {\n\nclass UdpSocketSpan : public ::ebpf_net::ingest::UdpSocketSpanBase {\npublic:\n  ~UdpSocketSpan();\n\n  /** handlers */\n  void udp_new_socket(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_new_socket *msg);\n  void udp_stats_addr_unchanged(\n      ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_unchanged *msg);\n  void udp_stats_addr_changed_v4(\n      ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_changed_v4 *msg);\n  void udp_stats_addr_changed_v6(\n      ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_addr_changed_v6 *msg);\n  void\n  dns_response_dep_b(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_response_dep_b *msg);\n  void dns_response(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_response *msg);\n  void dns_timeout(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__dns_timeout *msg);\n  void udp_stats_drops_changed(\n      ::ebpf_net::ingest::weak_refs::udp_socket span_ref, u64 timestamp, jsrv_ingest__udp_stats_drops_changed *msg);\n\nprivate:\n  // Local endpoint address.\n  IPv6Address local_addr_;\n  // Local port.\n  u16 local_port_{0};\n\n  struct AddrPort {\n    IPv6Address addr;\n    u16 port;\n  };\n\n  std::array<AddrPort, 2> remote_ip_;\n\n  // Flag indicating whether UDP metrics should be ignored.\n  bool ignore_udp_{false};\n  // Flag indicating whether this UDP socket has received DNS stats.\n  bool is_dns_{false};\n\n  // Flow updaters, [0]:TX, [1]:RX\n  std::array<std::optional<FlowUpdater>, 2> flow_updater_;\n\n  /**\n   * Updates the flow object given address change.\n   */\n  void update_handle(::ebpf_net::ingest::weak_refs::udp_socket span_ref, u8 is_rx);\n\n  /**\n   * Updates statistics assuming the flow object is already initialized.\n   *\n   * Checks if the handle is valid.\n   */\n  void update_udp_stats(u64 timestamp, u8 is_rx, u32 addr_changed, u32 packets, u32 bytes, u32 drops);\n\n  // Same as above, but for dns stats.\n  void update_dns_stats(\n      ::ebpf_net::ingest::weak_refs::udp_socket span_ref,\n      u64 timestamp,\n      const CLIENT_SERVER_TYPE client_server,\n      const ::ebpf_net::metrics::dns_metrics_point &metrics,\n      bool is_timeout);\n};\n\n} // namespace reducer::ingest\n"
  },
  {
    "path": "reducer/internal_metrics_encoder.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"constants.h\"\n#include \"disabled_metrics.h\"\n#include \"metric_info.h\"\n#include \"publisher.h\"\n#include \"stat_info.h\"\n#include \"tsdb_formatter.h\"\n\nnamespace reducer {\nclass InternalMetricsEncoder {\npublic:\n  InternalMetricsEncoder(TsdbFormat format, Publisher::WriterPtr &writer, const DisabledMetrics &disabled_metrics)\n      : writer_(writer), disabled_metrics_(disabled_metrics)\n  {\n    formatter_ = TsdbFormatter::make(format, writer);\n  }\n\n  void write_metric(\n      const EbpfNetMetricInfo &metric,\n      TsdbFormatter::labels_t labels,\n      TsdbFormatter::value_t value,\n      TsdbFormatter::timestamp_t time_ns)\n  {\n    formatter_->set_labels(labels);\n    formatter_->assign_label(std::string_view(kProductIdDimName), std::string_view(kProductIdDimValue));\n    formatter_->set_timestamp(time_ns);\n    formatter_->write(metric, value, writer_);\n  }\n\n  void write_metric(const EbpfNetMetricInfo &metric, TsdbFormatter::value_t value, TsdbFormatter::timestamp_t time_ns)\n  {\n    formatter_->assign_label(std::string_view(kProductIdDimName), std::string_view(kProductIdDimValue));\n    formatter_->set_timestamp(time_ns);\n    formatter_->write(metric, value, writer_);\n  }\n\n  void add_label(std::string name, std::string value) { formatter_->assign_label(name, value); }\n\n  void remove_label(std::string_view name) { formatter_->remove_label(name); }\n\n  void clear_labels() { formatter_->clear_labels(); }\n\n  void flush() { formatter_->flush(); }\n\n  template <typename STATS> void write_internal_stats(STATS &stats, std::chrono::nanoseconds time_ns)\n  {\n    clear_labels();\n\n    stats.labels.foreach_label([&](std::string_view name, std::string value) { add_label(std::string(name), value); });\n\n    stats.metrics.foreach_metric([&](EbpfNetMetricInfo metric, std::variant<u32, u64, double> value) {\n      if (!disabled_metrics_.is_metric_disabled(metric.metric)) {\n        write_metric(metric, value, time_ns);\n      }\n    });\n  }\n\n  template <typename STATS> void write_internal_stats(STATS &stats, u64 time_ns)\n  {\n    write_internal_stats<STATS>(stats, std::chrono::nanoseconds(time_ns));\n  }\n\nprivate:\n  Publisher::WriterPtr &writer_;\n  std::unique_ptr<TsdbFormatter> formatter_;\n  const DisabledMetrics &disabled_metrics_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/internal_stats.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"metric_info.h\"\n\n// The intention of these macros is to create a struct of related metrics (collectively called \"Stats\").  They\n// are characterized by sharing the same set of labels.\n// The BEGIN_LABELS/ LABEL / END_LABELS macros create a struct called \"labels\", with a public member\n// for each LABEL macro invocation.  It also creates an iterative function called \"foreach_label()\" that can\n// be called to yield all label names and values.\n//\n// The BEGIN_METRICS / METRIC / END_METRICS macros do the same, but the struct is called \"metrics\".\n//\n// To use these macros, create a containing struct, such as \"SpanUtilizationStats\".\n// Inside the struct, begin defining the set of labels.\n// You must first begin the label list with the \"BEGIN_LABELS\" macro\n// Next, list each LABEL(), giving the label its name: e.g. LABEL(span).\n// The name is both the field that will contain the label's value, as well as the \"display\" name.\n// Finally, end the label section with \"END_LABELS\"\n//\n// Similarly, define the metrics.\n// Begin the metric list with BEGIN_METRICS\n// Add each metric with METRIC()  There are two arguments: the metric info, defined in metric_info.h\n// and the name of the field that will store the metric value.\n// End the metric section with END_METRICS.  For example:\n//\n// struct SomeStat {\n//  BEGIN_LABELS\n//  LABEL(field)\n//  LABEL(field2)\n//  END_LABELS\n//\n//  BEGIN_METRICS\n//  METRIC(EbpfNetMetricInfo::Metric1, metric1)\n//  METRIC(EbpfNetMetricInfo::Metric2, metric2)\n//  END_METRICS\n// };\n//\n// Lots of concrete examples follow after the macro declarations.\n//\n// The labels can be iterated through the \"foreach_label()\" function, which will callback\n// with the name and the field value of each label.\n// The metrics can be iterated through the \"foreach_metric()\" function in the same fashion.\n// An example of how this is done can be found in internal_stats.h.\n//\n// Finally, access to the labels or metrics can be done directly via the labels or metrics structs.\n//\n// Since internal metrics are a cross-cutting concern, you can see examples of how to fill out a \"Stat\" in many places, such as\n// agg_core.cc\n//\n// The common pattern is this:\n//  SomeStat stats;\n//  // fill the labels\n//  stats.labels.field = value;\n//  stats.labels.field2 = value2;\n//  // fill the metrics\n//  stats.metrics.value = metric_value1;\n//  stats.metrics.value2 = metric_value2;\n//  // write it out along the encoder\n//  encoder.write_internal_stats(\n\nnamespace reducer {\n\n#define BEGIN_LABELS                                                                                                           \\\n  struct Labels {                                                                                                              \\\n    using label_fn_t = std::function<void(std::string_view, std::string)>;                                                     \\\n    void foreach_label(label_fn_t func)                                                                                        \\\n    {                                                                                                                          \\\n      __foreach_label(_FirstLabelType(), func, *this);                                                                         \\\n    }                                                                                                                          \\\n    struct _FirstLabelType {};                                                                                                 \\\n    typedef _FirstLabelType\n\n#define LABEL(LABEL_NAME)                                                                                                      \\\n  _LabelType_##LABEL_NAME;                                                                                                     \\\n  std::string LABEL_NAME;                                                                                                      \\\n  struct _NextLabelType_##LABEL_NAME {};                                                                                       \\\n  static void __foreach_label(_LabelType_##LABEL_NAME, label_fn_t func, Labels &this_struct)                                   \\\n  {                                                                                                                            \\\n    func(#LABEL_NAME, this_struct.LABEL_NAME);                                                                                 \\\n    __foreach_label(_NextLabelType_##LABEL_NAME(), func, this_struct);                                                         \\\n  }                                                                                                                            \\\n  typedef _NextLabelType_##LABEL_NAME\n\n#define END_LABELS                                                                                                             \\\n  _LastLabelType;                                                                                                              \\\n  static void __foreach_label(_LastLabelType, label_fn_t func, Labels &this_struct) {}                                         \\\n  }                                                                                                                            \\\n  labels;\n\n#define BEGIN_METRICS                                                                                                          \\\n  struct Metrics {                                                                                                             \\\n    using value_t = std::variant<u32, u64, double>;                                                                            \\\n    using metric_fn_t = std::function<void(const EbpfNetMetricInfo &, value_t)>;                                               \\\n                                                                                                                               \\\n    void foreach_metric(metric_fn_t func)                                                                                      \\\n    {                                                                                                                          \\\n      __foreach_metric(_FirstMetricType(), func, *this);                                                                       \\\n    }                                                                                                                          \\\n    struct _FirstMetricType {};                                                                                                \\\n    typedef _FirstMetricType\n\n#define METRIC(METRIC_INFO, METRIC_NAME)                                                                                       \\\n  _MetricType_##METRIC_NAME;                                                                                                   \\\n  value_t METRIC_NAME;                                                                                                         \\\n  struct _NextMetricType_##METRIC_NAME {};                                                                                     \\\n  static void __foreach_metric(_MetricType_##METRIC_NAME, metric_fn_t func, Metrics &this_struct)                              \\\n  {                                                                                                                            \\\n    func(METRIC_INFO, this_struct.METRIC_NAME);                                                                                \\\n    __foreach_metric(_NextMetricType_##METRIC_NAME(), func, this_struct);                                                      \\\n  }                                                                                                                            \\\n  typedef _NextMetricType_##METRIC_NAME\n\n#define END_METRICS                                                                                                            \\\n  _LastMetricType;                                                                                                             \\\n  static void __foreach_metric(_LastMetricType, metric_fn_t func, Metrics &this_struct) {}                                     \\\n  }                                                                                                                            \\\n  metrics;\n\n#define COMMON_LABELS                                                                                                          \\\n  LABEL(module)                                                                                                                \\\n  LABEL(shard)\n\n///////////////////////////////////////////////////////////////////////////////\n// CoreBase\n///////////////////////////////////////////////////////////////////////////////\nstruct SpanUtilizationStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(span)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::span_utilization, utilization)\n  METRIC(EbpfNetMetricInfo::span_utilization_fraction, utilization_fraction)\n  METRIC(EbpfNetMetricInfo::span_utilization_max, utilization_max)\n  END_METRICS\n};\n\nstruct ConnectionMessageStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(connection)\n  LABEL(message)\n  LABEL(severity)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::message, count)\n  END_METRICS\n};\n\nstruct ConnectionMessageErrorStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(connection)\n  LABEL(message)\n  LABEL(error)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::pipeline_message_error, count)\n  END_METRICS\n};\n\nstruct StatusStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(program)\n  LABEL(version)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::up, status)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// AggCore\n///////////////////////////////////////////////////////////////////////////////\n\nstruct AggRootTruncationStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(field)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::agg_root_truncation, count)\n  END_METRICS\n};\n\nstruct AggPrometheusBytesStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::prometheus_bytes_written, prometheus_bytes_written)\n  METRIC(EbpfNetMetricInfo::prometheus_bytes_discarded, prometheus_bytes_discarded)\n  END_METRICS\n};\n\nstruct CodeTimingStats {\n  BEGIN_LABELS\n  LABEL(name)\n  LABEL(filename)\n  LABEL(line)\n  LABEL(index)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::codetiming_count, count)\n  METRIC(EbpfNetMetricInfo::codetiming_avg_ns, avg_ns)\n  METRIC(EbpfNetMetricInfo::codetiming_min_ns, min_ns)\n  METRIC(EbpfNetMetricInfo::codetiming_max_ns, max_ns)\n  METRIC(EbpfNetMetricInfo::codetiming_sum_ns, sum_ns)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// AgentSpan\n///////////////////////////////////////////////////////////////////////////////\n#define COMMON_AGENT_SPAN_LABELS                                                                                               \\\n  COMMON_LABELS                                                                                                                \\\n  LABEL(version)                                                                                                               \\\n  LABEL(cloud)                                                                                                                 \\\n  LABEL(env)                                                                                                                   \\\n  LABEL(role)                                                                                                                  \\\n  LABEL(az)                                                                                                                    \\\n  LABEL(id)                                                                                                                    \\\n  LABEL(kernel)                                                                                                                \\\n  LABEL(c_type)                                                                                                                \\\n  LABEL(c_host)                                                                                                                \\\n  LABEL(os)                                                                                                                    \\\n  LABEL(os_version)\n\nstruct CollectorHealthStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(status)\n  LABEL(detail)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::collector_health, health)\n  END_METRICS\n};\n\nstruct BpfLogStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(file)\n  LABEL(line)\n  LABEL(code)\n  LABEL(arg0)\n  LABEL(arg1)\n  LABEL(arg2)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::bpf_log, bpf_log)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// IngestCore\n///////////////////////////////////////////////////////////////////////////////\n\nstruct ClientHandlePoolStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(span)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::client_handle_pool, client_handle_pool)\n  METRIC(EbpfNetMetricInfo::client_handle_pool_fraction, client_handle_pool_fraction)\n  END_METRICS\n};\n\nstruct ConnectionStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::time_since_last_message_ns, time_since_last_message_ns)\n  METRIC(EbpfNetMetricInfo::clock_offset_ns, clock_offset_ns)\n  END_METRICS\n};\n\nstruct AgentConnectionMessageStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(message)\n  LABEL(severity)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::message, count)\n  END_METRICS\n};\n\nstruct AgentConnectionMessageErrorStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(message)\n  LABEL(error)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::pipeline_message_error, count)\n  END_METRICS\n};\n\nstruct CollectorLogStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(severity)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::collector_log_count, collector_log_count)\n  END_METRICS\n};\n\nstruct EntrypointStats {\n  BEGIN_LABELS\n  COMMON_AGENT_SPAN_LABELS\n  LABEL(kernel_headers_source)\n  LABEL(error)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::entrypoint_info, entrypoint_info)\n  END_METRICS\n};\n\nstruct ServerStats {\n  BEGIN_LABELS\n  LABEL(module)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::connections, connections)\n  METRIC(EbpfNetMetricInfo::disconnects, disconnects)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// LoggingCore\n///////////////////////////////////////////////////////////////////////////////\nstruct PipelineAgentStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(hostname)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::pipeline_agent_connections, connections)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// OtlpGrpcPublisher\n///////////////////////////////////////////////////////////////////////////////\nstruct OtlpGrpcStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(client_type)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::otlp_grpc_bytes_failed, bytes_failed)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_bytes_sent, bytes_sent)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_metrics_failed, metrics_failed)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_metrics_sent, metrics_sent)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_requests_failed, requests_failed)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_requests_sent, requests_sent)\n  METRIC(EbpfNetMetricInfo::otlp_grpc_unknown_response_tags, unknown_response_tags)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// PrometheusPublisher\n///////////////////////////////////////////////////////////////////////////////\nstruct PromStats {\n  BEGIN_LABELS\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::prometheus_bytes_ingested, bytes_ingested)\n  METRIC(EbpfNetMetricInfo::prometheus_failed_scrapes, failed_scrapes)\n  END_METRICS\n};\n\nstruct BigItemsDroppedStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::prometheus_big_items_dropped, prometheus_big_items_dropped)\n  END_METRICS\n};\n\n///////////////////////////////////////////////////////////////////////////////\n// RpcStats\n///////////////////////////////////////////////////////////////////////////////\nstruct RpcWriteStallsStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(peer)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::rpc_write_stalls, stalls)\n  END_METRICS\n};\n\nstruct RpcQueueUtilizationStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(peer)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::rpc_queue_buf_utilization, max_buf_used)\n  METRIC(EbpfNetMetricInfo::rpc_queue_buf_utilization_fraction, max_buf_util)\n  METRIC(EbpfNetMetricInfo::rpc_queue_elem_utilization_fraction, max_elem_util)\n  END_METRICS\n};\n\nstruct RpcLatencyStats {\n  BEGIN_LABELS\n  COMMON_LABELS\n  LABEL(peer)\n  END_LABELS\n\n  BEGIN_METRICS\n  METRIC(EbpfNetMetricInfo::rpc_latency_ns, max_latency_ns)\n  END_METRICS\n};\n\n#undef BEGIN_LABELS\n#undef END_LABELS\n#undef LABEL\n\n#undef BEGIN_METRICS\n#undef END_METRICS\n#undef METRIC\n\n#undef INTERNAL_METRIC_PREFIX\n\n#undef COMMON_LABELS\n#undef COMMON_AGENT_SPAN_LABELS\n} // namespace reducer\n"
  },
  {
    "path": "reducer/json_formatter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include \"json_formatter.h\"\n\n#include <util/code_timing.h>\n#include <util/time.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <ctime>\n#include <stdexcept>\n\nnamespace reducer {\nnamespace {\n\ntemplate <typename T>\nstd::string_view\njson_format_prefix(char *buf_ptr, size_t buf_size, std::string_view metric, std::string_view aggregation_label, T value)\n{\n  auto [end, len] =\n      fmt::format_to_n(buf_ptr, buf_size, \"{{\\\"aggregation\\\":\\\"{}\\\",\\\"{}\\\":{},\", aggregation_label, metric, value);\n  return std::string_view(buf_ptr, std::min(len, buf_size));\n}\n\nstd::string_view json_format_labels(char *buff_ptr, size_t buff_size, TsdbFormatter::labels_t const &labels)\n{\n  size_t written = 0;\n  auto write = [&written, buff_ptr, buff_size](void const *ptr, size_t len) {\n    size_t const n = std::min(len, (buff_size - written));\n    if (n > 0) {\n      memcpy(buff_ptr + written, ptr, n);\n      written += n;\n    }\n  };\n\n  auto write_str = [write](std::string_view str) { write(str.data(), str.size()); };\n\n  auto write_label = [write_str](std::string_view name, std::string_view value) {\n    if (value.empty()) {\n      return;\n    }\n    write_str(\"\\\"\");\n    write_str(name);\n    write_str(\"\\\":\\\"\");\n    write_str(value);\n    write_str(\"\\\",\");\n  };\n\n  for (auto const &[name, value] : labels) {\n    write_label(name, value);\n  }\n\n  return std::string_view(buff_ptr, written);\n}\n\nstd::string_view json_format_suffix(char *buf_ptr, size_t buf_size, TsdbFormatter::timestamp_t timestamp)\n{\n  using namespace std::chrono;\n\n  time_t time = system_clock::to_time_t(time_point<system_clock>(timestamp));\n\n  struct tm tm_buf;\n  char strftime_buf[64];\n  strftime(strftime_buf, sizeof(strftime_buf), \"%FT%T\", gmtime_r(&time, &tm_buf));\n\n  auto millis = integer_time<milliseconds>(timestamp) - integer_time<seconds>(timestamp) * 1000;\n\n  auto [end, len] = fmt::format_to_n(buf_ptr, buf_size, \"\\\"timestamp\\\":\\\"{}.{:03}Z\\\"}}\\n\", strftime_buf, millis);\n  return std::string_view(buf_ptr, std::min(len, buf_size));\n}\n\n} // namespace\n\nvoid JsonTsdbFormatter::format(\n    MetricInfo const &metric,\n    value_t value,\n    std::string_view aggregation,\n    bool aggregation_changed,\n    rollup_t rollup,\n    bool rollup_changed,\n    labels_t labels,\n    bool labels_changed,\n    timestamp_t timestamp,\n    bool timestamp_changed,\n    Publisher::WriterPtr const &writer)\n{\n  SCOPED_TIMING(JsonTsdbFormatterFormat);\n\n  auto prefix = std::visit(\n      [&](auto &&val) -> std::string_view {\n        return json_format_prefix(prefix_buf_, sizeof(prefix_buf_), metric.name, aggregation_label_, val);\n      },\n      value);\n\n  if (labels_changed || labels_.empty()) {\n    labels_ = json_format_labels(labels_buf_, sizeof(labels_buf_), labels);\n  }\n\n  if (timestamp_changed || suffix_.empty()) {\n    suffix_ = json_format_suffix(suffix_buf_, sizeof(suffix_buf_), timestamp);\n  }\n\n  SCOPED_TIMING(JsonTsdbFormatterFormatWriterWrite);\n  writer->write(prefix, labels_, suffix_);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/json_formatter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"tsdb_formatter.h\"\n\nnamespace reducer {\n// Formatter implementation for JSON time-series format.\n//\n// Output:\n// - prefix: `{\"aggregation\":\"<aggregation>][_<rollup>]\",\"<metric>\":<value>,`\n// - labels: `\"label1\"=\"xxx\",\"label2\"=\"yyy\",...,`\n// - suffix: `\"timestamp\":\"<timestamp>\"}`\n//\nclass JsonTsdbFormatter : public TsdbFormatter {\nprotected:\n  void format(\n      MetricInfo const &metric,\n      value_t value,\n      std::string_view aggregation,\n      bool aggregation_changed,\n      rollup_t rollup,\n      bool rollup_changed,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed,\n      Publisher::WriterPtr const &writer) override;\n\nprivate:\n  // Large enough for metric name, aggregation, rollup and value.\n  char prefix_buf_[512];\n  // Large enough for all labels.\n  char labels_buf_[8192];\n  // Large enough for ISO timestamp and then some.\n  char suffix_buf_[64];\n\n  // Cached labels string, points to labels_buf_ when initialized.\n  std::string_view labels_;\n  // Cached suffix string, points to suffix_buf_ when initialized.\n  std::string_view suffix_;\n  // Cached aggregation label, based on aggregation and rollup.\n  std::string aggregation_label_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/latency_accumulator.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/tdigest.h>\n\n#include <absl/container/flat_hash_map.h>\n\n#include <queue>\n#include <string>\n\nnamespace reducer {\n\ntemplate <typename Key> class LatencyAccumulator {\npublic:\n  struct PLatencies {\n    Key key;\n    double p90, p95, p99;\n  };\n\n  LatencyAccumulator() : queue_max_size_(30), delta_ns_(10'000'000'000) {}\n\n  const std::vector<PLatencies> &get_p_latencies() const { return latencies_; }\n\n  const absl::flat_hash_map<Key, double> &get_max_latencies() const { return max_latencies_; }\n\n  void add(u64 t, Key key, double latency);\n\nprivate:\n  struct QueueElem {\n    u64 t = 0;\n    absl::flat_hash_map<Key, util::TDigest> digests;\n    absl::flat_hash_map<Key, double> max_latencies;\n  };\n\n  void compute_latencies();\n  void rotate_window(u64 t);\n\n  const size_t queue_max_size_;\n  const u64 delta_ns_;\n  std::deque<QueueElem> queue_;\n  std::vector<PLatencies> latencies_;\n  absl::flat_hash_map<Key, double> max_latencies_;\n};\n\n} // namespace reducer\n\n#include \"latency_accumulator.inl\"\n"
  },
  {
    "path": "reducer/latency_accumulator.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include \"latency_accumulator.h\"\n\nnamespace reducer {\n\ntemplate <typename Key> void LatencyAccumulator<Key>::compute_latencies()\n{\n  latencies_.clear();\n  max_latencies_.clear();\n\n  absl::flat_hash_map<Key, util::TDigest> digests;\n\n  for (const auto &t : queue_) {\n    for (const auto &[k, v] : t.digests) {\n      digests[k].merge(v);\n    }\n    for (const auto &[k, v] : t.max_latencies) {\n      if (max_latencies_[k] < v)\n        max_latencies_[k] = v;\n    }\n  }\n\n  for (const auto &[k, v] : digests) {\n    latencies_.push_back(\n        {k, v.estimate_value_at_quantile(0.90), v.estimate_value_at_quantile(0.95), v.estimate_value_at_quantile(0.99)});\n  }\n}\n\ntemplate <typename Key> void LatencyAccumulator<Key>::rotate_window(u64 t)\n{\n  if (queue_.empty()) {\n    queue_.push_back({});\n    queue_.back().t = t;\n    return;\n  }\n  auto delta = t - queue_.back().t;\n  if (delta > delta_ns_) {\n    queue_.push_back({});\n    queue_.back().t = t;\n    if (queue_.size() > queue_max_size_) {\n      queue_.pop_front();\n    }\n    compute_latencies();\n  }\n}\n\ntemplate <typename Key> void LatencyAccumulator<Key>::add(u64 t, Key key, double latency)\n{\n  rotate_window(t);\n\n  auto &digests = queue_.back().digests;\n\n  util::TDigestAccumulator acc(digests[key]);\n  acc.add(latency); // millisec\n  acc.flush();\n\n  auto &max_latencies = queue_.back().max_latencies;\n\n  if (max_latencies[key] < latency) {\n    max_latencies[key] = latency;\n  }\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/load_balancer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <set>\n\n#include \"absl/base/thread_annotations.h\"\n#include <absl/container/flat_hash_map.h>\n#include <absl/synchronization/mutex.h>\n\n#include <absl/types/span.h>\n\n// LoadBalancer helps associate integer load values with a list of elements.\n// Each element is initialized to a load of zero, and its load can be\n// incremented or decremented as needed, with the least-loaded element being\n// queryable. This class works best if type T is inexpensive to copy.\n// This class is thread-safe.\ntemplate <typename T> class LoadBalancer {\npublic:\n  // `elems` is the list of elements across which this class will load\n  // balance. For best performance, T should be cheap to copy.\n  explicit LoadBalancer(absl::Span<const T> elems);\n\n  // Returns a copy to the least loaded element. If there is a tie, one\n  // of the elements is returned, but it is undefined which one will be.\n  // Runs in O(1) time.\n  T least_loaded() const;\n\n  // Updates the load of `elem` by `load_delta`. `elem` must have been\n  // provided in the constructor. Runs in O(log n) time.\n  void increment_load(const T &elem, int load_delta);\n\nprivate:\n  struct Entry {\n    T elem;\n    int load = 0;\n    bool operator<(const Entry &rhs) const;\n  };\n\n  using EntrySet = std::multiset<Entry>;\n  using EntryIterator = typename EntrySet::const_iterator;\n\n  EntrySet entries_ ABSL_GUARDED_BY(mu_);\n  absl::flat_hash_map<T, EntryIterator> elem_to_entry_it_ ABSL_GUARDED_BY(mu_);\n  mutable absl::Mutex mu_;\n};\n\n// Implementation below.\n\ntemplate <typename T> LoadBalancer<T>::LoadBalancer(const absl::Span<const T> elems)\n{\n  assert(!elems.empty());\n  absl::MutexLock l(&mu_);\n  for (const T &elem : elems) {\n    auto entry_it = entries_.insert(Entry{.elem = elem});\n    elem_to_entry_it_[elem] = entry_it;\n  }\n}\n\ntemplate <typename T> T LoadBalancer<T>::least_loaded() const\n{\n  absl::ReaderMutexLock l(&mu_);\n  return entries_.begin()->elem;\n}\n\ntemplate <typename T> void LoadBalancer<T>::increment_load(const T &elem, const int load_delta)\n{\n  absl::MutexLock l(&mu_);\n\n  // Get the iterator in `entries_` corresponding to `elem` (which must have\n  // been provided to the constructor).\n  auto entry_it_it = elem_to_entry_it_.find(elem);\n  assert(entry_it_it != elem_to_entry_it_.end());\n  const EntryIterator entry_it = entry_it_it->second;\n\n  // Create the new entry with the updated load.\n  Entry new_entry{\n      .elem = elem,\n      .load = entry_it->load + load_delta,\n  };\n\n  // Remove the old entry and add the new one.\n  entries_.erase(entry_it);\n  const EntryIterator new_entry_it = entries_.insert(std::move(new_entry));\n\n  // Re-associate `elem` to the new entry.\n  entry_it_it->second = new_entry_it;\n}\n\ntemplate <typename T> bool LoadBalancer<T>::Entry::operator<(const Entry &rhs) const\n{\n  return load < rhs.load;\n}\n"
  },
  {
    "path": "reducer/logging/agg_core_stats_span.cc",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"agg_core_stats_span.h\"\n\n#include \"logging_core.h\"\n\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/logging/component.h>\n\n#include <reducer/internal_stats.h>\n#include <reducer/tsdb_format.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\nnamespace reducer::logging {\n\nAggCoreStatsSpan::AggCoreStatsSpan() {}\n\nAggCoreStatsSpan::~AggCoreStatsSpan() {}\n\nvoid AggCoreStatsSpan::agg_root_truncation_stats(\n    ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_root_truncation_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  AggRootTruncationStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.field = msg->field;\n  stats.metrics.count = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"AggCoreStatsSpan::agg_root_truncation_stats module={} shard={} field={}  count={} timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->field,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid AggCoreStatsSpan::agg_prometheus_bytes_stats(\n    ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_prometheus_bytes_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  AggPrometheusBytesStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.metrics.prometheus_bytes_written = msg->prometheus_bytes_written;\n  stats.metrics.prometheus_bytes_discarded = msg->prometheus_bytes_discarded;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"AggCoreStatsSpan::agg_prometheus_bytes_stats module={} shard={} metrics_bytes_written={} metrics_bytes_discarded={} timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->prometheus_bytes_written,\n      msg->prometheus_bytes_discarded,\n      msg->time_ns);\n}\n\nvoid AggCoreStatsSpan::agg_otlp_grpc_stats(\n    ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_otlp_grpc_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  OtlpGrpcStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.client_type = msg->client_type;\n  stats.metrics.bytes_failed = msg->bytes_failed;\n  stats.metrics.bytes_sent = msg->bytes_sent;\n  stats.metrics.metrics_failed = msg->data_points_failed;\n  stats.metrics.metrics_sent = msg->data_points_sent;\n  stats.metrics.requests_failed = msg->requests_failed;\n  stats.metrics.requests_sent = msg->requests_sent;\n  stats.metrics.unknown_response_tags = msg->unknown_response_tags;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"AggCoreStatsSpan::agg_otlp_grpc_stats module={} shard={} client_type={} bytes_failed={} bytes_sent={} metrics_failed={} metrics_sent={} requests_failed={} requests_sent={} unknown_response_tags={} timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->client_type,\n      msg->bytes_failed,\n      msg->bytes_sent,\n      msg->data_points_failed,\n      msg->data_points_sent,\n      msg->requests_failed,\n      msg->requests_sent,\n      msg->unknown_response_tags,\n      msg->time_ns);\n}\n\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/agg_core_stats_span.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <generated/ebpf_net/logging/span_base.h>\n\nnamespace reducer::logging {\n\nclass AggCoreStatsSpan : public ::ebpf_net::logging::AggCoreStatsSpanBase {\npublic:\n  AggCoreStatsSpan();\n  ~AggCoreStatsSpan();\n  void agg_root_truncation_stats(\n      ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_root_truncation_stats *msg);\n  void agg_prometheus_bytes_stats(\n      ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_prometheus_bytes_stats *msg);\n  void agg_otlp_grpc_stats(\n      ::ebpf_net::logging::weak_refs::agg_core_stats span_ref, u64 timestamp, jsrv_logging__agg_otlp_grpc_stats *msg);\n};\n\n}; // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/component.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE reducer::logging\n#define ENUM_NAME Component\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(internal_metrics, 1, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "reducer/logging/connection_metrics.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\nnamespace reducer::logging {\n\nstruct ConnectionMetrics {\n  u64 num_connections;\n\n  ConnectionMetrics() : num_connections(0) {}\n\n  ConnectionMetrics const &operator+=(ConnectionMetrics const &other)\n  {\n    num_connections += other.num_connections;\n    return *this;\n  }\n};\n\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/core_stats_span.cc",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"core_stats_span.h\"\n\n#include \"logging_core.h\"\n\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/logging/component.h>\n\n#include <reducer/internal_stats.h>\n#include <reducer/tsdb_format.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\nnamespace reducer::logging {\n\nCoreStatsSpan::CoreStatsSpan() {}\n\nCoreStatsSpan::~CoreStatsSpan() {}\n\nvoid CoreStatsSpan::span_utilization_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__span_utilization_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  SpanUtilizationStats stats;\n  stats.labels.span = msg->span_name;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.metrics.utilization = std::size_t(msg->allocated);\n  stats.metrics.utilization_fraction = (double)msg->allocated / msg->pool_size_;\n  stats.metrics.utilization_max = std::size_t(msg->max_allocated);\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::span_utilization_stats: module={} span_name={} allocated={} max_allocated={} pool_size_={} timestamp={} \",\n      msg->module,\n      msg->span_name,\n      msg->allocated,\n      msg->max_allocated,\n      msg->pool_size_,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan::connection_message_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__connection_message_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  ConnectionMessageStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.connection = std::to_string(msg->conn);\n  stats.labels.message = msg->msg_;\n  stats.labels.severity = std::to_string(msg->severity_);\n  stats.metrics.count = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::connection_message_stats module={} msg_={} shard={} severity_={} conn={} count={} timestamp={}\",\n      msg->module,\n      msg->msg_,\n      msg->shard,\n      msg->severity_,\n      msg->conn,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan ::connection_message_error_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__connection_message_error_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  ConnectionMessageErrorStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.connection = std::to_string(msg->conn);\n  stats.labels.message = msg->msg_;\n  stats.labels.error = msg->error;\n  stats.metrics.count = msg->count;\n  stats.labels.module = msg->module;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan ::connection_message_error_stats: module={} msg_={} shard={} conn={} count={} error={} timestamp={}\",\n      msg->module,\n      msg->msg_,\n      msg->shard,\n      msg->conn,\n      msg->count,\n      msg->error,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan::status_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__status_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  StatusStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.program = msg->program;\n  stats.labels.version = msg->version;\n  stats.metrics.status = std::size_t(msg->status);\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::status_stats module={} shard={} program={} version={} status={} timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->program,\n      msg->version,\n      msg->status,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan ::rpc_receive_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_receive_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  RpcLatencyStats stats;\n  stats.labels.module = msg->receiver_app;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.peer = msg->sender_app;\n  stats.metrics.max_latency_ns = msg->max_latency_ns;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::rpc_receive_stats receiver_app={} shard={} sender_app={} max_latency_ns={} timestamp={}\",\n      msg->receiver_app,\n      msg->shard,\n      msg->sender_app,\n      msg->max_latency_ns,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan ::rpc_write_stalls_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_write_stalls_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  RpcWriteStallsStats stats;\n  stats.labels.module = msg->sender_app;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.peer = msg->receiver_app;\n  stats.metrics.stalls = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::rpc_write_stalls_stats sender_app={} shard={} receiver_app={}  count={} timestamp={}\",\n      msg->sender_app,\n      msg->shard,\n      msg->receiver_app,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan ::rpc_write_utilization_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_write_utilization_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  RpcQueueUtilizationStats stats;\n  stats.labels.module = msg->sender_app;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.peer = msg->receiver_app;\n  stats.metrics.max_buf_used = msg->max_buf_used;\n  stats.metrics.max_buf_util = msg->max_buf_util;\n  stats.metrics.max_elem_util = msg->max_elem_util;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::rpc_write_utilization_stats sender_app={} shard={} receiver_app={}  max_buf_used={} max_buf_util={} max_elem_util={} timestamp={}\",\n      msg->sender_app,\n      msg->shard,\n      msg->receiver_app,\n      msg->max_buf_used,\n      msg->max_buf_util,\n      msg->max_elem_util,\n      msg->time_ns);\n}\n\nvoid CoreStatsSpan::code_timing_stats(\n    ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__code_timing_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  CodeTimingStats stats;\n  stats.labels.name = msg->name;\n  stats.labels.filename = msg->filename;\n  stats.labels.line = std::to_string(msg->line);\n  stats.labels.index = std::to_string(msg->index_string);\n\n  stats.metrics.count = msg->count;\n  stats.metrics.avg_ns = msg->avg_ns;\n  stats.metrics.min_ns = msg->min_ns;\n  stats.metrics.max_ns = msg->max_ns;\n  stats.metrics.sum_ns = msg->sum_ns;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"CoreStatsSpan::code_timing_stats name={} filename={} line={}  index={} count={} avg_ns={} min_ns={} max_ns={} sum_ns={}\",\n      msg->name,\n      msg->filename,\n      msg->line,\n      msg->index_string,\n      msg->count,\n      msg->avg_ns,\n      msg->min_ns,\n      msg->max_ns,\n      msg->sum_ns,\n      msg->time_ns);\n}\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/core_stats_span.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <generated/ebpf_net/logging/span_base.h>\n\nnamespace reducer::logging {\n\nclass CoreStatsSpan : public ::ebpf_net::logging::CoreStatsSpanBase {\npublic:\n  CoreStatsSpan();\n  ~CoreStatsSpan();\n  void span_utilization_stats(\n      ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__span_utilization_stats *msg);\n  void connection_message_stats(\n      ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__connection_message_stats *msg);\n  void connection_message_error_stats(\n      ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__connection_message_error_stats *msg);\n  void status_stats(::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__status_stats *msg);\n  void\n  rpc_receive_stats(::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_receive_stats *msg);\n  void rpc_write_stalls_stats(\n      ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_write_stalls_stats *msg);\n  void rpc_write_utilization_stats(\n      ::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__rpc_write_utilization_stats *msg);\n  void\n  code_timing_stats(::ebpf_net::logging::weak_refs::core_stats span_ref, u64 timestamp, jsrv_logging__code_timing_stats *msg);\n};\n\n}; // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/ingest_core_stats_span.cc",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"ingest_core_stats_span.h\"\n\n#include \"logging_core.h\"\n\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/logging/component.h>\n\n#include <reducer/internal_stats.h>\n#include <reducer/tsdb_format.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\nnamespace reducer::logging {\n\nIngestCoreStatsSpan::IngestCoreStatsSpan() {}\n\nIngestCoreStatsSpan::~IngestCoreStatsSpan() {}\n\nvoid IngestCoreStatsSpan::client_handle_pool_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__client_handle_pool_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  ClientHandlePoolStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.span = msg->span_name;\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.metrics.client_handle_pool = msg->client_handle_pool;\n  stats.metrics.client_handle_pool_fraction = msg->client_handle_pool_fraction;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::client_handle_pool_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} client_handle_pool={} client_handle_pool_fraction ={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->client_handle_pool,\n      msg->client_handle_pool_fraction,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::agent_connection_message_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref,\n    u64 timestamp,\n    jsrv_logging__agent_connection_message_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  AgentConnectionMessageStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.message = msg->message;\n  stats.labels.severity = std::to_string(msg->severity_);\n  stats.metrics.count = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::agent_connection_message_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} client_handle_pool={} client_handle_pool_fraction={} message={} severity_={} count={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->message,\n      msg->severity_,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::agent_connection_message_error_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref,\n    u64 timestamp,\n    jsrv_logging__agent_connection_message_error_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  AgentConnectionMessageErrorStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.message = msg->message;\n  stats.labels.error = msg->error;\n  stats.metrics.count = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::agent_connection_message_error_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} client_handle_pool={} client_handle_pool_fraction={} message={} error={} count={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->message,\n      msg->error,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::connection_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__connection_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  ConnectionStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.metrics.time_since_last_message_ns = msg->time_since_last_message_ns;\n  stats.metrics.clock_offset_ns = msg->clock_offset_ns;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::connection_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} client_handle_pool={} client_handle_pool_fraction={} time_since_last_message_ns={} clock_offset_ns={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->time_since_last_message_ns,\n      msg->clock_offset_ns,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::collector_log_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__collector_log_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  CollectorLogStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.severity = msg->severity_;\n  stats.metrics.collector_log_count = msg->count;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::collector_log_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} severity_={} count={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->severity_,\n      msg->count,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::entry_point_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__entry_point_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  EntrypointStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->agent_hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.kernel_headers_source = msg->kernel_headers_source;\n  stats.labels.error = msg->entrypoint_error;\n  stats.metrics.entrypoint_info = 1u;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::entry_point_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} kernel_headers_source={} entrypoint_error={} entrypoint_info={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->agent_hostname,\n      msg->os,\n      msg->os_version,\n      msg->kernel_headers_source,\n      msg->entrypoint_error,\n      msg->entrypoint_info,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::collector_health_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__collector_health_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  CollectorHealthStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.status = msg->status;\n  stats.labels.detail = msg->status_detail;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::collector_health_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} status={} status_detail={}  timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->hostname,\n      msg->os,\n      msg->os_version,\n      msg->status,\n      msg->status_detail,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::bpf_log_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__bpf_log_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  BpfLogStats stats;\n  stats.labels.module = msg->module;\n  stats.labels.shard = std::to_string(msg->shard);\n  stats.labels.version = msg->version;\n  stats.labels.cloud = msg->cloud;\n  stats.labels.env = msg->env;\n  stats.labels.role = msg->role;\n  stats.labels.az = msg->az;\n  stats.labels.id = msg->node_id;\n  stats.labels.kernel = msg->kernel_version;\n  stats.labels.c_type = std::to_string(msg->client_type);\n  stats.labels.c_host = msg->hostname;\n  stats.labels.os = msg->os;\n  stats.labels.os_version = msg->os_version;\n  stats.labels.file = msg->filename;\n  stats.labels.line = msg->line;\n  stats.labels.code = msg->code;\n  stats.labels.arg0 = msg->arg0;\n  stats.labels.arg1 = msg->arg1;\n  stats.labels.arg2 = msg->arg2;\n  stats.metrics.bpf_log = 1u;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::bpf_log_stats: module={} shard={}  version={} cloud={} env={} role={} az={} node_id={} kernel_version={} client_type={} agent_hostname={} os={} os_version={} filename={} line={} code={} arg0={} arg1={} arg2={} timestamp={}\",\n      msg->module,\n      msg->shard,\n      msg->version,\n      msg->cloud,\n      msg->env,\n      msg->role,\n      msg->az,\n      msg->node_id,\n      msg->kernel_version,\n      msg->client_type,\n      msg->hostname,\n      msg->os,\n      msg->os_version,\n      msg->filename,\n      msg->line,\n      msg->code,\n      msg->arg0,\n      msg->arg1,\n      msg->arg2,\n      msg->time_ns);\n}\n\nvoid IngestCoreStatsSpan::server_stats(\n    ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__server_stats *msg)\n{\n  auto &encoder = local_core<LoggingCore>().encoder_;\n\n  ServerStats stats;\n  stats.labels.module = msg->module;\n  stats.metrics.connections = msg->connection_counter;\n  stats.metrics.disconnects = msg->disconnect_counter;\n\n  encoder.write_internal_stats(stats, msg->time_ns);\n\n  LOG::debug_in(\n      reducer::logging::Component::internal_metrics,\n      \"IngestCoreStatsSpan::server_stats: module={} connection_counter={} disconnect_counter={}  timestamp={}\",\n      msg->module,\n      msg->connection_counter,\n      msg->disconnect_counter,\n      msg->time_ns);\n}\n\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/ingest_core_stats_span.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <generated/ebpf_net/logging/span_base.h>\n\nnamespace reducer::logging {\n\nclass IngestCoreStatsSpan : public ::ebpf_net::logging::IngestCoreStatsSpanBase {\npublic:\n  IngestCoreStatsSpan();\n  ~IngestCoreStatsSpan();\n  void client_handle_pool_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__client_handle_pool_stats *msg);\n  void agent_connection_message_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref,\n      u64 timestamp,\n      jsrv_logging__agent_connection_message_stats *msg);\n  void agent_connection_message_error_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref,\n      u64 timestamp,\n      jsrv_logging__agent_connection_message_error_stats *msg);\n  void connection_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__connection_stats *msg);\n  void collector_log_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__collector_log_stats *msg);\n  void entry_point_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__entry_point_stats *msg);\n  void server_stats(::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__server_stats *msg);\n  void collector_health_stats(\n      ::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__collector_health_stats *msg);\n  void\n  bpf_log_stats(::ebpf_net::logging::weak_refs::ingest_core_stats span_ref, u64 timestamp, jsrv_logging__bpf_log_stats *msg);\n};\n\n}; // namespace reducer::logging"
  },
  {
    "path": "reducer/logging/logger_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"logger_span.h\"\n#include \"connection_metrics.h\"\n\n#include <common/client_type.h>\n#include <reducer/constants.h>\n\n#include <util/log.h>\n\nnamespace reducer::logging {\n\nnamespace {\n\n// Ingest errors should be written to the log only for valid clients -- those that have at least successfully sent the\n// version information. Otherwise the log can be spammed by spurious connections. Errors triggered by non-valid clients will be\n// sent to the debug log, for debugging purposes.\n//\ntemplate <typename Format, typename... Args> void log_ingest_error(ClientType client_type, Format &&format, Args &&...args)\n{\n  if (client_type == ClientType::unknown) {\n    LOG::debug(std::forward<Format>(format), std::forward<Args>(args)...);\n  } else {\n    LOG::error(std::forward<Format>(format), std::forward<Args>(args)...);\n  }\n}\n\n} // namespace\n\nLoggerSpan::LoggerSpan() {}\n\nLoggerSpan::~LoggerSpan() {}\n\nvoid LoggerSpan::agent_lost_events(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__agent_lost_events *msg)\n{\n  LOG::warn(\"({}) lost events ({}) from agent at '{}'\", msg->_rpc_id, msg->count, msg->client_hostname);\n}\n\nvoid LoggerSpan::pod_not_found(::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__pod_not_found *msg)\n{\n  LOG::error(\"({}) pod with uid={} not found\", msg->_rpc_id, msg->uid);\n}\n\nvoid LoggerSpan::cgroup_not_found(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__cgroup_not_found *msg)\n{\n  LOG::error(\"({}) cgroup with id={} not found\", msg->_rpc_id, msg->cgroup);\n}\n\nvoid LoggerSpan::rewriting_private_to_public_ip_mapping(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__rewriting_private_to_public_ip_mapping *msg)\n{\n  LOG::warn(\n      \"({}) rewriting existing private-to-public IP address mapping:\"\n      \" private={}, existing_public={}, new_public={}\",\n      msg->_rpc_id,\n      msg->private_addr,\n      msg->existing_public_addr,\n      msg->new_public_addr);\n}\n\nvoid LoggerSpan::private_ip_in_private_to_public_ip_mapping(\n    ::ebpf_net::logging::weak_refs::logger span_ref,\n    u64 timestamp,\n    jsrv_logging__private_ip_in_private_to_public_ip_mapping *msg)\n{\n  LOG::warn(\n      \"({}) private-only address exists in private-to-public IP address\"\n      \" mapping: private={}, existing_public={}\",\n      msg->_rpc_id,\n      msg->private_addr,\n      msg->existing_public_addr);\n}\n\nvoid LoggerSpan::failed_to_insert_dns_record(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__failed_to_insert_dns_record *msg)\n{\n  LOG::error(\"({}) failed to insert dns record\", msg->_rpc_id);\n}\n\nvoid LoggerSpan::tcp_socket_failed_getting_process_reference(\n    ::ebpf_net::logging::weak_refs::logger span_ref,\n    u64 timestamp,\n    jsrv_logging__tcp_socket_failed_getting_process_reference *msg)\n{\n  LOG::error(\n      \"({}) TCP socket span failed to get process span reference\"\n      \" for pid={}\",\n      msg->_rpc_id,\n      msg->pid);\n}\n\nvoid LoggerSpan::udp_socket_failed_getting_process_reference(\n    ::ebpf_net::logging::weak_refs::logger span_ref,\n    u64 timestamp,\n    jsrv_logging__udp_socket_failed_getting_process_reference *msg)\n{\n  LOG::error(\n      \"({}) UDP socket span failed to get process span reference\"\n      \" for pid={}\",\n      msg->_rpc_id,\n      msg->pid);\n}\n\nvoid LoggerSpan::socket_address_already_assigned(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__socket_address_already_assigned *msg)\n{\n  LOG::error(\"({}) attempt to assign socket address multiple times\", msg->_rpc_id);\n}\n\nvoid LoggerSpan::ingest_decompression_error(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_decompression_error *msg)\n{\n  auto client_type = static_cast<ClientType>(msg->client_type);\n\n  log_ingest_error(\n      client_type,\n      \"({}) ingest decompression error from {} at '{}': {}\",\n      msg->_rpc_id,\n      msg->client_type,\n      msg->client_hostname,\n      msg->error);\n}\n\nvoid LoggerSpan::ingest_processing_error(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_processing_error *msg)\n{\n  auto client_type = static_cast<ClientType>(msg->client_type);\n\n  log_ingest_error(\n      client_type,\n      \"({}) error processing data from {} at '{}': {}\",\n      msg->_rpc_id,\n      client_type,\n      msg->client_hostname,\n      msg->error);\n}\n\nvoid LoggerSpan::ingest_connection_error(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_connection_error *msg)\n{\n  auto client_type = static_cast<ClientType>(msg->client_type);\n\n  log_ingest_error(\n      client_type,\n      \"({}) connection error from {} collector at '{}' encountered: {}\",\n      msg->_rpc_id,\n      msg->client_type,\n      msg->client_hostname,\n      msg->error);\n}\n\nvoid LoggerSpan::k8s_container_pod_not_found(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__k8s_container_pod_not_found *msg)\n{\n  LOG::error(\"({}) k8s_container failed to reference a pod\", msg->_rpc_id);\n}\n\nvoid LoggerSpan::agent_connect_success(\n    ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__agent_connect_success *msg)\n{\n  std::string_view client_hostname{msg->client_hostname.buf, msg->client_hostname.len};\n\n  LOG::info(\n      \"({}) {} collector at '{}' successfully connected\",\n      msg->_rpc_id,\n      static_cast<ClientType>(msg->client_type),\n      client_hostname);\n}\n\n}; // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/logger_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/logging/span_base.h>\n\n#include <absl/container/flat_hash_map.h>\n\n#include <string>\n\nnamespace reducer::logging {\n\nstruct ConnectionMetrics;\n\nclass LoggerSpan : public ::ebpf_net::logging::LoggerSpanBase {\npublic:\n  LoggerSpan();\n  ~LoggerSpan();\n\n  void connection_metrics(std::function<void(std::string_view, ConnectionMetrics const &)> const &f);\n\n  void agent_lost_events(::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__agent_lost_events *msg);\n\n  void pod_not_found(::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__pod_not_found *msg);\n\n  void cgroup_not_found(::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__cgroup_not_found *msg);\n\n  void rewriting_private_to_public_ip_mapping(\n      ::ebpf_net::logging::weak_refs::logger span_ref,\n      u64 timestamp,\n      jsrv_logging__rewriting_private_to_public_ip_mapping *msg);\n\n  void private_ip_in_private_to_public_ip_mapping(\n      ::ebpf_net::logging::weak_refs::logger span_ref,\n      u64 timestamp,\n      jsrv_logging__private_ip_in_private_to_public_ip_mapping *msg);\n\n  void failed_to_insert_dns_record(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__failed_to_insert_dns_record *msg);\n\n  void tcp_socket_failed_getting_process_reference(\n      ::ebpf_net::logging::weak_refs::logger span_ref,\n      u64 timestamp,\n      jsrv_logging__tcp_socket_failed_getting_process_reference *msg);\n\n  void udp_socket_failed_getting_process_reference(\n      ::ebpf_net::logging::weak_refs::logger span_ref,\n      u64 timestamp,\n      jsrv_logging__udp_socket_failed_getting_process_reference *msg);\n\n  void socket_address_already_assigned(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__socket_address_already_assigned *msg);\n\n  void ingest_decompression_error(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_decompression_error *msg);\n\n  void ingest_processing_error(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_processing_error *msg);\n\n  void ingest_connection_error(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__ingest_connection_error *msg);\n\n  void k8s_container_pod_not_found(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__k8s_container_pod_not_found *msg);\n\n  void agent_connect_success(\n      ::ebpf_net::logging::weak_refs::logger span_ref, u64 timestamp, jsrv_logging__agent_connect_success *msg);\n};\n}; // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/logging_core.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include <reducer/logging/connection_metrics.h>\n#include <reducer/logging/logging_core.h>\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <common/constants.h>\n\n#include <platform/userspace-time.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <absl/container/flat_hash_map.h>\n\nnamespace reducer::logging {\n\nbool LoggingCore::otlp_formatted_internal_metrics_enabled_ = false;\n\nvoid LoggingCore::set_otlp_formatted_internal_metrics_enabled(bool enabled)\n{\n  otlp_formatted_internal_metrics_enabled_ = enabled;\n}\n\nTsdbFormat LoggingCore::get_tsdb_format()\n{\n  TsdbFormat internal_format =\n      otlp_formatted_internal_metrics_enabled_ == true ? TsdbFormat::otlp_grpc : TsdbFormat::prometheus;\n  return internal_format;\n}\n\nLoggingCore::LoggingCore(\n    RpcQueueMatrix &ingest_to_logging_queues,\n    RpcQueueMatrix &matching_to_logging_queues,\n    RpcQueueMatrix &aggregation_to_logging_queues,\n    Publisher::WriterPtr stats_writer,\n    const DisabledMetrics &disabled_metrics,\n    size_t shard_num,\n    u64 initial_timestamp)\n    : CoreBase(\"logging\", shard_num, initial_timestamp),\n      stats_writer_(std::move(stats_writer)),\n      encoder_(get_tsdb_format(), stats_writer_, disabled_metrics),\n      ingest_to_logging_stats_(shard_num, \"ingest\", \"logging\"),\n      matching_to_logging_stats_(shard_num, \"matching\", \"logging\"),\n      aggregation_to_logging_stats_(shard_num, \"aggregation\", \"logging\"),\n      logger_(index_.logger.alloc())\n{\n  // ingest->this\n  add_rpc_clients(ingest_to_logging_queues.make_readers(shard_num), ClientType::ingest, ingest_to_logging_stats_);\n\n  // matching->this\n  add_rpc_clients(matching_to_logging_queues.make_readers(shard_num), ClientType::matching, matching_to_logging_stats_);\n\n  // aggregation->this\n  add_rpc_clients(\n      aggregation_to_logging_queues.make_readers(shard_num), ClientType::aggregation, aggregation_to_logging_stats_);\n}\n\nvoid LoggingCore::write_internal_stats()\n{\n  auto bytes_failed_to_write = stats_writer_->bytes_failed_to_write();\n  u64 time_ns = fp_get_time_ns();\n\n  write_common_stats(encoder_, time_ns);\n\n  int const shard = shard_num();\n  std::string_view module = \"logging\";\n\n  ingest_to_logging_stats_.write_internal_stats(encoder_, time_ns);\n  matching_to_logging_stats_.write_internal_stats(encoder_, time_ns);\n  aggregation_to_logging_stats_.write_internal_stats(encoder_, time_ns);\n\n  stats_writer_->write_internal_stats(encoder_, time_ns, shard, module);\n\n  encoder_.flush();\n  stats_writer_->flush();\n\n  if (stats_writer_->bytes_failed_to_write() > bytes_failed_to_write) {\n    LOG::error(\"Logging core failed to publish internal metrics writer stats\");\n  }\n}\n\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/logging/logging_core.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/core_base.h>\n\n#include <reducer/disabled_metrics.h>\n#include <reducer/publisher.h>\n#include <reducer/rpc_stats.h>\n\n#include <generated/ebpf_net/logging/connection.h>\n#include <generated/ebpf_net/logging/index.h>\n#include <generated/ebpf_net/logging/protocol.h>\n#include <generated/ebpf_net/logging/transform_builder.h>\n\nnamespace reducer {\nclass RpcQueueMatrix;\n}\n\nnamespace reducer::logging {\n\n// This class implements the 'logging' app (see render definition file).\n//\n// It receives messages that are signaling error conditions, writes out their\n// textual representation to stderr, and publishes their count to a TSDB.\n//\nclass LoggingCore : public CoreBase<\n                        ebpf_net::logging::Index,\n                        ebpf_net::logging::Protocol,\n                        ebpf_net::logging::Connection,\n                        ebpf_net::logging::TransformBuilder> {\npublic:\n  LoggingCore(\n      RpcQueueMatrix &ingest_to_logging_queues,\n      RpcQueueMatrix &matching_to_logging_queues,\n      RpcQueueMatrix &aggregation_to_logging_queues,\n      Publisher::WriterPtr stats_writer,\n      const DisabledMetrics &disabled_metrics,\n      size_t shard_num,\n      u64 initial_timestamp);\n\n  // For writing internal stats.\n  Publisher::WriterPtr stats_writer_;\n\n  InternalMetricsEncoder encoder_;\n\n  // Enables otlp formatted internal metrics.\n  static void set_otlp_formatted_internal_metrics_enabled(bool enabled);\n\n  // Get tsdb format for Logging core.\n  static TsdbFormat get_tsdb_format();\n\nprivate:\n  // Keeper of ingest->this RPC stats.\n  RpcReceiverStats ingest_to_logging_stats_;\n  // Keeper of matching->this RPC stats.\n  RpcReceiverStats matching_to_logging_stats_;\n  // Keeper of aggregation->this RPC stats.\n  RpcReceiverStats aggregation_to_logging_stats_;\n\n  ::ebpf_net::logging::auto_handles::logger logger_;\n\n  // Internal metrics using otlp formatting.\n  static bool otlp_formatted_internal_metrics_enabled_;\n\n  // Outputs internal stats to be scraped by a time-series DB.\n  void write_internal_stats() override;\n};\n\n} // namespace reducer::logging\n"
  },
  {
    "path": "reducer/matching/aws_enrichment_info.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n\nstruct AwsEnrichmentInfo {\n  std::string role;\n  std::string az;\n  std::string id;\n};\n"
  },
  {
    "path": "reducer/matching/aws_enrichment_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/matching/aws_enrichment_span.h>\n\n#include <reducer/constants.h>\n\n#include <util/log.h>\n\nnamespace reducer::matching {\n\nAwsEnrichmentSpan::AwsEnrichmentSpan() {}\n\nAwsEnrichmentSpan::~AwsEnrichmentSpan() {}\n\nvoid AwsEnrichmentSpan::aws_enrichment(\n    ::ebpf_net::matching::weak_refs::aws_enrichment span_ref, u64 timestamp, jsrv_matching__aws_enrichment *msg)\n{\n  LOG::trace_in(\n      std::make_tuple(NodeResolutionType::AWS, ClientType::cloud),\n      \"matching::AwsEnrichmentSpan::aws_enrichment:\"\n      \" role='{}' az='{}' id='{}'\",\n      msg->role,\n      msg->az,\n      msg->id);\n\n  if (!info_) {\n    info_.emplace();\n  }\n\n  assign_jb(info_->role, msg->role);\n  assign_jb(info_->az, msg->az);\n  assign_jb(info_->id, msg->id);\n}\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/aws_enrichment_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/matching/span_base.h>\n\n#include <reducer/matching/aws_enrichment_info.h>\n\n#include <optional>\n\nnamespace reducer::matching {\n\nstruct AwsEnrichmentSpan : ebpf_net::matching::AwsEnrichmentSpanBase {\n  AwsEnrichmentSpan();\n  ~AwsEnrichmentSpan();\n\n  void\n  aws_enrichment(::ebpf_net::matching::weak_refs::aws_enrichment span_ref, u64 timestamp, jsrv_matching__aws_enrichment *msg);\n\n  AwsEnrichmentInfo const *info() const { return info_ ? &info_.value() : nullptr; }\n\nprivate:\n  std::optional<AwsEnrichmentInfo> info_;\n};\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/component.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE reducer::matching\n#define ENUM_NAME Component\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(flow, 1, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "reducer/matching/flow_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/matching/component.h>\n#include <reducer/matching/flow_span.h>\n#include <reducer/matching/matching_core.h>\n\n#include <reducer/constants.h>\n#include <reducer/copy_metrics.h>\n#include <reducer/uid_key.h>\n\n#include <generated/ebpf_net/matching/containers.inl>\n#include <generated/ebpf_net/matching/index.h>\n#include <generated/ebpf_net/matching/modifiers.h>\n#include <generated/ebpf_net/matching/spans.h>\n#include <generated/ebpf_net/metrics.h>\n\n#include <common/client_server_type.h>\n\n#include <util/cgroup_parser.h>\n#include <util/error_handling.h>\n#include <util/ip_address.h>\n#include <util/log.h>\n#include <util/string_view.h>\n\n#include <cassert>\n#include <optional>\n#include <string>\n\nnamespace reducer::matching {\n\nnamespace {\n\nconstexpr std::string_view kNoAgentEnvironmentName = \"(no agent)\";\n\nstatic const IPv6Address kAddrInstanceMetadata = IPv6Address::from_host_hextets({0, 0, 0, 0, 0, 0xffff, 0xa9fe, 0xa9fe});\n\n// Returns the underlying value if it is set, otherwise returns a\n// default-constructed object.\ntemplate <typename T> const T &get_or_default(const std::optional<T> &value)\n{\n  static const auto *const kDefault = new T;\n  return value.has_value() ? *value : *kDefault;\n}\n\n} // namespace\n\nbool FlowSpan::aws_enrichment_enabled_ = false;\n\nvoid FlowSpan::enable_aws_enrichment(bool enabled)\n{\n  aws_enrichment_enabled_ = enabled;\n}\n\nFlowSpan::FlowSpan() {}\n\nFlowSpan::~FlowSpan() {}\n\nvoid FlowSpan::agent_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__agent_info *msg)\n{\n  std::string id{msg->id.buf, msg->id.len};\n  std::string az{msg->az.buf, msg->az.len};\n  std::string env{msg->env.buf, msg->env.len};\n  std::string role{msg->role.buf, msg->role.len};\n  std::string ns{msg->ns.buf, msg->ns.len};\n\n  LOG::trace_in(\n      Component::flow, \"matching::FlowSpan::agent_info: side={} id={} az={} env={} ns={}\", msg->side, id, az, env, ns);\n\n  auto const side = u8_to_side(msg->side);\n\n  agent_info_[+side] = {\n      .id = std::move(id),\n      .az = std::move(az),\n      .env = std::move(env),\n      .role = std::move(role),\n      .ns = std::move(ns),\n  };\n\n  n_received_info_messages_++;\n}\n\nvoid FlowSpan::task_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__task_info *msg)\n{\n  std::string comm{msg->comm.buf, msg->comm.len};\n  std::string cgroup_name{msg->cgroup_name.buf, msg->cgroup_name.len};\n\n  LOG::trace_in(Component::flow, \"matching::FlowSpan::task_info: side={} comm='{}' cgroup='{}'\", msg->side, comm, cgroup_name);\n\n  auto const side = u8_to_side(msg->side);\n\n  task_info_[+side] = {\n      .comm = comm,\n      .cgroup_name = cgroup_name,\n  };\n\n  n_received_info_messages_++;\n}\n\nvoid FlowSpan::socket_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__socket_info *msg)\n{\n  auto local_addr = IPv6Address::from(msg->local_addr);\n  auto remote_addr = IPv6Address::from(msg->remote_addr);\n  std::string remote_dns_name{msg->remote_dns_name.buf, msg->remote_dns_name.len};\n\n  LOG::trace_in(\n      Component::flow,\n      \"matching::FlowSpan::socket_info: side={}\"\n      \" local_ip={}:{} remote_ip={}:{}\",\n      msg->side,\n      local_addr,\n      msg->local_port,\n      remote_addr,\n      msg->remote_port);\n\n  auto const side = u8_to_side(msg->side);\n\n  socket_info_[+side] = SocketInfo{\n      .local_addr = local_addr,\n      .local_port = msg->local_port,\n      .remote_addr = remote_addr,\n      .remote_port = msg->remote_port,\n      .is_connector = msg->is_connector,\n      .remote_dns_name = std::move(remote_dns_name),\n  };\n\n  n_received_info_messages_++;\n}\n\nvoid FlowSpan::k8s_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__k8s_info *msg)\n{\n  auto const side = u8_to_side(msg->side);\n\n  std::array<u8, 64> pod_uid_suffix;\n  std::copy_n(msg->pod_uid_suffix, std::min(sizeof(msg->pod_uid_suffix), pod_uid_suffix.max_size()), pod_uid_suffix.begin());\n\n  LOG::trace_in(\n      Component::flow,\n      \"matching::FlowSpan::k8s_info: side={} pod_uid_suffix='{}'\"\n      \" pod_uid_hash={}\",\n      msg->side,\n      std::string_view((char *)pod_uid_suffix.data(), pod_uid_suffix.size()),\n      msg->pod_uid_hash);\n\n  k8s_info_[+side] = K8sInfo{\n      .pod_uid_suffix = std::move(pod_uid_suffix),\n      .pod_uid_hash = msg->pod_uid_hash,\n  };\n\n  n_received_info_messages_++;\n}\n\nvoid FlowSpan::container_info(\n    ::ebpf_net::matching::weak_refs::flow span_ref, const u64 timestamp, jsrv_matching__container_info *const msg)\n{\n  auto const side = u8_to_side(msg->side);\n  std::string name{msg->name.buf, msg->name.len};\n  std::string pod{msg->pod.buf, msg->pod.len};\n  std::string role{msg->role.buf, msg->role.len};\n  std::string version{msg->version.buf, msg->version.len};\n  std::string ns{msg->ns.buf, msg->ns.len};\n  auto const type = static_cast<NodeResolutionType>(msg->node_type);\n\n  container_info_[+side] = ContainerInfo{\n      .name = std::move(name),\n      .pod = std::move(pod),\n      .role = std::move(role),\n      .version = std::move(version),\n      .ns = std::move(ns),\n      .type = (sanitize_enum(type) == type ? type : NodeResolutionType::CONTAINER)};\n\n  n_received_info_messages_++;\n}\n\nvoid FlowSpan::service_info(\n    ::ebpf_net::matching::weak_refs::flow span_ref, const u64 timestamp, jsrv_matching__service_info *const msg)\n{\n  auto const side = u8_to_side(msg->side);\n  std::string name{msg->name.buf, msg->name.len};\n\n  service_info_[+side] = ServiceInfo{\n      .name = std::move(name),\n  };\n\n  n_received_info_messages_++;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvoid FlowSpan::update_nodes_if_required(::ebpf_net::matching::weak_refs::flow flow)\n{\n  bool got_messages = (n_received_info_messages_ != message_count_on_last_update_);\n\n  bool update_needed = got_messages || should_attempt_k8s_enrichment(flow, FlowSide::SIDE_A) ||\n                       should_attempt_k8s_enrichment(flow, FlowSide::SIDE_B);\n\n  if (!update_needed) {\n    return;\n  }\n\n  update_nodes(flow, resolve_node(flow, FlowSide::SIDE_A), resolve_node(flow, FlowSide::SIDE_B));\n\n  // set the last update value to avoid refreshing if there are no messages\n  message_count_on_last_update_ = n_received_info_messages_;\n}\n\nvoid FlowSpan::update_nodes(::ebpf_net::matching::weak_refs::flow flow, NodeData const &node_a, NodeData const &node_b)\n{\n  create_agg_root(flow, node_a, node_b);\n\n  if (!flow.agg_root().valid()) {\n    return;\n  }\n\n  update_node(flow.agg_root(), FlowSide::SIDE_A, node_a);\n  update_node(flow.agg_root(), FlowSide::SIDE_B, node_b);\n}\n\nvoid FlowSpan::update_node(::ebpf_net::matching::weak_refs::agg_root agg_root, FlowSide side, NodeData const &n)\n{\n  agg_root.update_node(\n      static_cast<u8>(side),\n      jb_blob(n.id),\n      jb_blob(n.az),\n      jb_blob(n.role),\n      jb_blob(n.version),\n      jb_blob(n.env),\n      jb_blob(n.ns),\n      static_cast<u8>(n.node_type),\n      jb_blob(n.address),\n      jb_blob(n.comm),\n      jb_blob(n.container_name),\n      jb_blob(n.pod_name),\n      jb_blob(n.role_uid));\n}\n\nvoid FlowSpan::create_agg_root(::ebpf_net::matching::weak_refs::flow flow, NodeData const &node_a, NodeData const &node_b)\n{\n  using agg_root = ::ebpf_net::matching::spans::agg_root;\n\n  if (node_a.role.empty() || node_b.role.empty()) {\n    // can't do anything yet\n    return;\n  }\n\n  std::string_view role_a = node_a.role;\n  std::string_view role_b = node_b.role;\n  std::string_view az_a;\n  std::string_view az_b;\n\n  // HACK ALERT!\n  // We will only shard by (role,az,role,az) if one of the nodes is an IP node,\n  // which means that its role is (unknown) or (internet). Not a good thing to\n  // shard on. If neither of nodes is IP, we shard only on (role,role).\n  //\n  if ((node_a.node_type == NodeResolutionType::IP) || (node_b.node_type == NodeResolutionType::IP)) {\n    az_a = node_a.az;\n    az_b = node_b.az;\n  }\n\n  agg_root::role1_t role1;\n  agg_root::role2_t role2;\n  agg_root::az1_t az1;\n  agg_root::az2_t az2;\n\n  if (std::tie(role_a, az_a) <= std::tie(role_b, az_b)) {\n    role1 = agg_root::role1_t::truncate(role_a);\n    role2 = agg_root::role2_t::truncate(role_b);\n    az1 = agg_root::az1_t::truncate(az_a);\n    az2 = agg_root::az2_t::truncate(az_b);\n  } else {\n    role1 = agg_root::role1_t::truncate(role_b);\n    role2 = agg_root::role2_t::truncate(role_a);\n    az1 = agg_root::az1_t::truncate(az_b);\n    az2 = agg_root::az2_t::truncate(az_a);\n  }\n\n  if ((flow.agg_root().valid() == false) || (role1 != flow.agg_root().role1()) || (role2 != flow.agg_root().role2()) ||\n      (az1 != flow.agg_root().az1()) || (az2 != flow.agg_root().az2())) {\n    flow.modify().agg_root(flow.index().agg_root.alloc(role1, az1, role2, az2));\n  }\n}\n\nbool FlowSpan::should_attempt_k8s_enrichment(::ebpf_net::matching::weak_refs::flow flow, FlowSide side) const\n{\n  // should we retry to enrich using kubernetes pods?\n  //\n  // this can happen for a side if:\n  //   1. we haven't already resolved kubernetes\n  //   2. the side has k8s info\n\n  // get the k8s pod info\n  auto k8s_pod = (side == FlowSide::SIDE_A) ? flow.k8s_pod1() : flow.k8s_pod2();\n\n  if (k8s_pod.valid()) {\n    // already resolved and enriched as kubernetes\n    return false;\n  } else {\n    // continue attempting if we have k8s info\n    return k8s_info_[+side].has_value();\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nFlowSpan::NodeData FlowSpan::resolve_node(::ebpf_net::matching::weak_refs::flow span_ref, FlowSide side)\n{\n  const std::optional<AddrPort> addr_port = get_addr_port(side);\n  if (unlikely(!addr_port.has_value())) {\n    debug_state(\"address/port missing\");\n    return NodeData{};\n  }\n\n  // Textual representation of the IP address.\n  std::string address = addr_port->addr.tidy_string();\n\n  auto [id, az, is_autonomous_system] = get_id_az(side);\n\n  std::string role;\n  std::string role_uid;\n  std::string version;\n  std::string env;\n  std::string ns;\n  std::string container_name;\n  auto node_type = NodeResolutionType::NONE;\n\n  if (auto &agent_info = agent_info_[+side]; agent_info.has_value()) {\n    env = agent_info->env;\n    ns = agent_info->ns;\n  } else {\n    env = kNoAgentEnvironmentName;\n  }\n\n  const std::string &pod_name = get_or_default(container_info_[+side]).pod;\n\n  // sanity checks on messages:\n  // we get agent_info_ if and only if task_info and socket info\n  if (unlikely(agent_info_[+side].has_value() != task_info_[+side].has_value())) {\n    debug_state(\"agent_info not iff task_info\");\n  };\n  if (unlikely(agent_info_[+side].has_value() != socket_info_[+side].has_value())) {\n    debug_state(\"agent info not iff socket_info\");\n  }\n\n  // Enrich, in order of data richness.\n  //\n  // First, try Kubernetes\n  if (auto pod = get_k8s_pod(side, span_ref.index()); pod.valid()) {\n    // got a k8s pod!\n    LOG::trace_in(\n        NodeResolutionType::K8S_CONTAINER,\n        \"matching::FlowSpan::update_node: enriching k8s\"\n        \" pod_uid_suffix='{}' pod_uid_hash={} pod_owner='{}' ns='{}'\",\n        std::string_view((char *)pod.uid_suffix().data(), pod.uid_suffix().size()),\n        pod.uid_hash(),\n        pod.owner_name(),\n        pod.ns());\n\n    // set our state that we're enriched\n    if (side == FlowSide::SIDE_A) {\n      span_ref.modify().k8s_pod1(pod.get());\n    } else {\n      span_ref.modify().k8s_pod2(pod.get());\n    }\n\n    // enrich\n    node_type = NodeResolutionType::K8S_CONTAINER;\n    role = pod.owner_name();\n    role_uid = pod.owner_uid();\n    version = pod.version();\n    ns = pod.ns();\n\n    if (auto &task_info = task_info_[+side]; task_info.has_value()) {\n      auto info = CGroupParser{task_info->cgroup_name}.get();\n      auto container_id = info.container_id;\n\n      if (!container_id.empty()) {\n        auto container_key = make_uid_key<ebpf_net::matching::keys::k8s_container>(container_id);\n        auto k8s_container = span_ref.index().k8s_container.by_key(container_key);\n\n        if (k8s_container.valid()) {\n          container_name = k8s_container.name();\n\n          if (!k8s_container.version().empty()) {\n            version = k8s_container.version();\n          }\n        }\n      }\n    }\n  } else if (auto const &container = container_info_[+side]; container.has_value()) {\n    // if we have container info, use that\n    node_type = sanitize_enum(container->type) == container->type ? container->type : NodeResolutionType::CONTAINER;\n    role = container->role;\n    version = container->version;\n    ns = container->ns;\n  } else if (agent_info_[+side].has_value()) {\n    // if we at least have an agent, use that\n    node_type = NodeResolutionType::PROCESS;\n    if (service_info_[+side].has_value()) {\n      role = service_info_[+side]->name;\n    } else if (task_info_[+side].has_value()) {\n      role = task_info_[+side]->comm;\n    } else {\n      // Shouldn't happen :( need to fix\n      role = agent_info_[+side]->role;\n    }\n    ns = agent_info_[+side]->ns;\n  } else if (auto const &flipside_socket_info = socket_info_[+(~side)]; flipside_socket_info.has_value()) {\n    // no agent: try AWS -> DNS -> IP\n    auto const &ipv6 = flipside_socket_info->remote_addr;\n\n    AwsEnrichmentInfo const *aws_info = nullptr;\n    if (aws_enrichment_enabled_) {\n      auto aws_enrichment_span = span_ref.index().aws_enrichment.by_key({ipv6.as_int()}, false);\n\n      if (aws_enrichment_span.valid()) {\n        aws_info = aws_enrichment_span.impl().info();\n      }\n\n      if (aws_info) {\n        LOG::trace_in(\n            NodeResolutionType::AWS,\n            \"matching::FlowSpan::update_node: found AWS metadata\"\n            \" for: ipv6={} role={} az={} id={}\",\n            ipv6,\n            aws_info->role,\n            aws_info->az,\n            aws_info->id);\n      } else {\n        LOG::trace_in(\n            NodeResolutionType::AWS,\n            \"matching::FlowSpan::update_node: found no AWS metadata\"\n            \" for: ipv6={}\",\n            ipv6);\n      }\n    }\n\n    if (aws_info && !aws_info->role.empty() && !aws_info->az.empty()) {\n      // AWS enrichment exists\n      node_type = NodeResolutionType::AWS;\n      role = aws_info->role;\n      az = aws_info->az;\n      if (!aws_info->id.empty()) {\n        id = aws_info->id + \"/\" + id;\n      }\n    } else {\n      if (!flipside_socket_info->remote_dns_name.empty()) {\n        node_type = NodeResolutionType::DNS;\n        role = flipside_socket_info->remote_dns_name;\n      } else {\n        // no agent and no DNS, fall back on the IP address\n        node_type = NodeResolutionType::IP;\n        role = is_autonomous_system ? \"(internet)\" : \"(unknown)\";\n      }\n    }\n  }\n\n  if (get_comm(side) == kCommKubelet) {\n    role = \"kubelet\";\n  } else if (addr_port->port == kPortDNS) {\n    role = \"DNS\";\n  } else if (addr_port->addr == kAddrInstanceMetadata) {\n    role = \"instance metadata\";\n    node_type = NodeResolutionType::INSTANCE_METADATA;\n    if (auto &agent_info = agent_info_[+(~side)]; agent_info.has_value()) {\n      id = agent_info->id;\n      az = agent_info->az;\n    }\n  }\n\n  if (is_autonomous_system && (node_type == NodeResolutionType::IP) && !MatchingCore::autonomous_system_ip_enabled()) {\n    id = address = \"AS\";\n  }\n\n  if (container_name.empty()) {\n    container_name = get_or_default(container_info_[+side]).name;\n  }\n\n  return NodeData{\n      .id = id,\n      .az = az,\n      .role = role,\n      .role_uid = role_uid,\n      .version = version,\n      .env = env,\n      .ns = ns,\n      .node_type = node_type,\n      .address = address,\n      .comm = get_comm(side),\n      .container_name = container_name,\n      .pod_name = pod_name,\n  };\n}\n\nstd::string FlowSpan::get_comm(FlowSide side) const\n{\n  return get_or_default(task_info_[+side]).comm;\n}\n\nstd::optional<FlowSpan::AddrPort> FlowSpan::get_addr_port(FlowSide side) const\n{\n  if (auto &local_info = socket_info_[+side]; local_info.has_value()) {\n    return AddrPort{local_info->local_addr, local_info->local_port};\n  }\n\n  FlowSide other_side = ~side;\n  if (auto &remote_info = socket_info_[+other_side]; remote_info.has_value()) {\n    return AddrPort{remote_info->remote_addr, remote_info->remote_port};\n  }\n\n  return std::nullopt;\n}\n\nstd::tuple<std::string, std::string, bool> FlowSpan::get_id_az(FlowSide side) const\n{\n  if (auto &agent_info = agent_info_[+side]; agent_info.has_value()) {\n    // use ID and AZ obtained from this side's agent info\n    return std::make_tuple(agent_info->id, agent_info->az, false);\n  }\n\n  std::string id;\n  std::string az = kUnknown;\n  bool is_autonomous_system = false;\n\n  FlowSide other_side = ~side;\n  if (auto &socket_info = socket_info_[+other_side]; socket_info.has_value()) {\n    // use the remote IP address from other side's socket info for ID\n    id = socket_info->remote_addr.tidy_string();\n\n    if (auto &an_db = local_core<MatchingCore>().an_db; an_db) {\n      auto addr = reinterpret_cast<in6_addr const *>(&socket_info->remote_addr);\n      if (auto entry = an_db.lookup(addr)) {\n        is_autonomous_system = geoip::well_known_data::try_autonomous_system_organization(az, entry);\n      }\n    }\n  }\n\n  return std::make_tuple(id, az, is_autonomous_system);\n}\n\n::ebpf_net::matching::auto_handles::k8s_pod FlowSpan::get_k8s_pod(FlowSide side, ::ebpf_net::matching::Index &index)\n{\n  if (auto &k8s_info = k8s_info_[+side]; k8s_info.has_value()) {\n    ::ebpf_net::matching::keys::k8s_pod pod_key{k8s_info->pod_uid_suffix, k8s_info->pod_uid_hash};\n\n    LOG::trace_in(\n        NodeResolutionType::K8S_CONTAINER,\n        \"matching::FlowSpan::get_k8s_pod: trying to find pod\"\n        \" uid_suffix='{}' uid_hash={}\",\n        std::string_view((char *)pod_key.uid_suffix.data(), pod_key.uid_suffix.size()),\n        pod_key.uid_hash);\n\n    auto pod = index.k8s_pod.by_key(pod_key);\n\n    if (pod.valid() && !pod.owner_name().empty()) {\n      return pod;\n    }\n  }\n\n  if (auto &task_info = task_info_[+side]; task_info.has_value()) {\n    auto info = CGroupParser{task_info->cgroup_name}.get();\n    auto container_id = info.container_id;\n\n    if (!container_id.empty()) {\n      auto container_key = make_uid_key<ebpf_net::matching::keys::k8s_container>(container_id);\n\n      LOG::trace_in(\n          NodeResolutionType::K8S_CONTAINER,\n          \"matching::FlowSpan::update_node: trying to find container\"\n          \" id='{}' uid_suffix='{}' uid_hash={}\",\n          container_id,\n          std::string_view((char *)container_key.uid_suffix.data(), container_key.uid_suffix.size()),\n          container_key.uid_hash);\n\n      auto k8s_container = index.k8s_container.by_key(container_key);\n\n      if (k8s_container.valid() && k8s_container.pod().valid()) {\n        return k8s_container.pod().get();\n      }\n    }\n  }\n\n  return ::ebpf_net::matching::auto_handles::k8s_pod(index);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nUpdateDirection FlowSpan::metrics_update_direction(FlowSide side, int is_rx, bool force_both_sides)\n{\n  if (!force_both_sides) {\n    if (!metrics_update_side_.has_value()) {\n      // first side to try gets to send metric updates\n      metrics_update_side_ = side;\n    } else if (*metrics_update_side_ != side) {\n      // the other side is sending metric updates\n      return UpdateDirection::NONE;\n    }\n  }\n\n  if (side == FlowSide::SIDE_A) {\n    return (is_rx == 0) ? UpdateDirection::A_TO_B : UpdateDirection::B_TO_A;\n  } else { // side == FlowSide::SIDE_B\n    return (is_rx == 0) ? UpdateDirection::B_TO_A : UpdateDirection::A_TO_B;\n  }\n}\n\nvoid FlowSpan::tcp_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__tcp_update *msg)\n{\n  auto const side = u8_to_side(msg->side);\n  auto const direction = metrics_update_direction(side, msg->is_rx);\n\n  ::ebpf_net::metrics::tcp_metrics_point metrics_point;\n  copy_tcp_metrics(metrics_point, *msg);\n\n  // skip RX RTT measurements\n  bool skip_rtts = msg->is_rx;\n\n  if (skip_rtts) {\n    metrics_point.sum_srtt = 0;\n    metrics_point.active_rtts = 0;\n  }\n\n  if (direction == UpdateDirection::A_TO_B) {\n    span_ref.tcp_a_to_b_update(timestamp, metrics_point);\n  } else if (direction == UpdateDirection::B_TO_A) {\n    span_ref.tcp_b_to_a_update(timestamp, metrics_point);\n  }\n\n  if (!skip_rtts) {\n    // Apply RTT-only metrics to side(s) that full metrics have not\n    // been applied above.\n    ::ebpf_net::metrics::tcp_metrics_point rtt_metrics = {\n        .sum_srtt = metrics_point.sum_srtt,\n        .active_rtts = metrics_point.active_rtts,\n    };\n    if (direction == UpdateDirection::A_TO_B) {\n      span_ref.tcp_b_to_a_update(timestamp, rtt_metrics);\n    } else if (direction == UpdateDirection::B_TO_A) {\n      span_ref.tcp_a_to_b_update(timestamp, rtt_metrics);\n    } else {\n      span_ref.tcp_a_to_b_update(timestamp, rtt_metrics);\n      span_ref.tcp_b_to_a_update(timestamp, rtt_metrics);\n    }\n  }\n}\n\nvoid FlowSpan::udp_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__udp_update *msg)\n{\n  auto const side = u8_to_side(msg->side);\n  auto const direction = metrics_update_direction(side, msg->is_rx);\n\n  ::ebpf_net::metrics::udp_metrics_point metrics_point;\n  copy_udp_metrics(metrics_point, *msg);\n\n  if (direction == UpdateDirection::A_TO_B) {\n    span_ref.udp_a_to_b_update(timestamp, metrics_point);\n  } else if (direction == UpdateDirection::B_TO_A) {\n    span_ref.udp_b_to_a_update(timestamp, metrics_point);\n  }\n}\n\nvoid FlowSpan::http_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__http_update *msg)\n{\n  auto const side = u8_to_side(msg->side);\n  auto const is_rx = msg->client_server == (u8)SC_SERVER; // client(0) is 'tx', server(1) is 'rx'\n  auto direction = metrics_update_direction(side, is_rx);\n\n  ::ebpf_net::metrics::http_metrics_point metrics_point;\n  copy_http_metrics(metrics_point, *msg);\n\n  // Ensure metrics are always reported on the correct side\n  if (!is_rx) {\n    // Client never gets processing time\n    metrics_point.sum_processing_time_ns = 0;\n  } else {\n    // Server never gets total time\n    metrics_point.sum_total_time_ns = 0;\n  }\n\n  // If we would duplicate metrics, still send a 'single-sided' update\n  // but leave out metrics that would contribute twice\n  if (direction == UpdateDirection::NONE) {\n    // Leave only fields that are not duplicates (this excludes active_sockets)\n    metrics_point = {\n        .sum_total_time_ns = metrics_point.sum_total_time_ns,\n        .sum_processing_time_ns = metrics_point.sum_processing_time_ns,\n    };\n\n    // And allow the update regardless of if there's another agent on the other side\n    direction = metrics_update_direction(side, is_rx, true);\n  }\n\n  if (direction == UpdateDirection::A_TO_B) {\n    span_ref.http_a_to_b_update(timestamp, metrics_point);\n  } else if (direction == UpdateDirection::B_TO_A) {\n    span_ref.http_b_to_a_update(timestamp, metrics_point);\n  }\n}\n\nvoid FlowSpan::dns_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__dns_update *msg)\n{\n  ASSUME(msg->side <= 1).else_log(\"side must be either 0 or 1, instead got {}\", msg->side);\n\n  auto const side = u8_to_side(msg->side);\n  auto const is_rx = msg->client_server == (u8)SC_SERVER; // client(0) is 'tx', server(1) is 'rx'\n  auto direction = metrics_update_direction(side, is_rx);\n\n  ::ebpf_net::metrics::dns_metrics_point metrics_point;\n  copy_dns_metrics(metrics_point, *msg);\n\n  // Ensure metrics are always reported on the correct side\n  if (!is_rx) {\n    // Client never gets processing time\n    metrics_point.sum_processing_time_ns = 0;\n  } else {\n    // Server never gets total time\n    metrics_point.sum_total_time_ns = 0;\n  }\n\n  // If we would duplicate metrics, still send a 'single-sided' update\n  // but leave out metrics that would contribute twice\n  if (direction == UpdateDirection::NONE) {\n    // Leave only fields that are not duplicates (this excludes active_sockets)\n    metrics_point = {\n        .sum_total_time_ns = metrics_point.sum_total_time_ns,\n        .sum_processing_time_ns = metrics_point.sum_processing_time_ns,\n    };\n\n    // And allow the update regardless of if there's another agent on the other side\n    direction = metrics_update_direction(side, is_rx, true);\n  }\n\n  if (direction == UpdateDirection::A_TO_B) {\n    span_ref.dns_a_to_b_update(timestamp, metrics_point);\n  } else if (direction == UpdateDirection::B_TO_A) {\n    span_ref.dns_b_to_a_update(timestamp, metrics_point);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nnamespace {\n\ntemplate <UpdateDirection Dir>\nvoid send_tcp_metrics(\n    u64 t, ::ebpf_net::matching::weak_refs::flow span_ref, ::ebpf_net::metrics::tcp_metrics const &m, u64 interval)\n{\n  span_ref.impl().update_nodes_if_required(span_ref);\n\n  auto agg_root = span_ref.agg_root();\n  if (!agg_root.valid()) {\n    return;\n  }\n\n  agg_root.update_tcp_metrics(\n      (u8)Dir,\n      m.active_sockets,\n      m.sum_retrans,\n      m.sum_bytes,\n      m.sum_srtt,\n      m.sum_delivered,\n      m.active_rtts,\n      m.syn_timeouts,\n      m.new_sockets,\n      m.tcp_resets);\n}\n\ntemplate <UpdateDirection Dir>\nvoid send_udp_metrics(\n    u64 t, ::ebpf_net::matching::weak_refs::flow span_ref, ::ebpf_net::metrics::udp_metrics const &m, u64 interval)\n{\n  span_ref.impl().update_nodes_if_required(span_ref);\n\n  auto agg_root = span_ref.agg_root();\n  if (!agg_root.valid()) {\n    return;\n  }\n\n  agg_root.update_udp_metrics((u8)Dir, m.active_sockets, m.addr_changes, m.packets, m.bytes, m.drops);\n}\n\ntemplate <UpdateDirection Dir>\nvoid send_dns_metrics(\n    u64 t, ::ebpf_net::matching::weak_refs::flow span_ref, ::ebpf_net::metrics::dns_metrics const &m, u64 interval)\n{\n  span_ref.impl().update_nodes_if_required(span_ref);\n\n  auto agg_root = span_ref.agg_root();\n  if (!agg_root.valid()) {\n    return;\n  }\n\n  agg_root.update_dns_metrics(\n      (u8)Dir,\n      m.active_sockets,\n      m.requests_a,\n      m.requests_aaaa,\n      m.responses,\n      m.timeouts,\n      m.sum_total_time_ns,\n      m.sum_processing_time_ns);\n}\n\ntemplate <UpdateDirection Dir>\nvoid send_http_metrics(\n    u64 t, ::ebpf_net::matching::weak_refs::flow span_ref, ::ebpf_net::metrics::http_metrics const &m, u64 interval)\n{\n  span_ref.impl().update_nodes_if_required(span_ref);\n\n  auto agg_root = span_ref.agg_root();\n  if (!agg_root.valid()) {\n    return;\n  }\n\n  agg_root.update_http_metrics(\n      (u8)Dir,\n      m.active_sockets,\n      m.sum_code_200,\n      m.sum_code_400,\n      m.sum_code_500,\n      m.sum_code_other,\n      m.sum_total_time_ns,\n      m.sum_processing_time_ns);\n}\n\n} // namespace\n\nvoid FlowSpan::send_metrics_to_aggregation(::ebpf_net::matching::containers::flow &flows, u64 ts)\n{\n  flows.tcp_a_to_b_foreach(ts, send_tcp_metrics<UpdateDirection::A_TO_B>);\n  flows.tcp_b_to_a_foreach(ts, send_tcp_metrics<UpdateDirection::B_TO_A>);\n\n  flows.udp_a_to_b_foreach(ts, send_udp_metrics<UpdateDirection::A_TO_B>);\n  flows.udp_b_to_a_foreach(ts, send_udp_metrics<UpdateDirection::B_TO_A>);\n\n  flows.dns_a_to_b_foreach(ts, send_dns_metrics<UpdateDirection::A_TO_B>);\n  flows.dns_b_to_a_foreach(ts, send_dns_metrics<UpdateDirection::B_TO_A>);\n\n  flows.http_a_to_b_foreach(ts, send_http_metrics<UpdateDirection::A_TO_B>);\n  flows.http_b_to_a_foreach(ts, send_http_metrics<UpdateDirection::B_TO_A>);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nvoid FlowSpan::debug_state(const std::string_view &reason)\n{\n  LOG::debug_in(\n      NodeResolutionType::NONE,\n      \"FlowSpan - n_messages={} last_update={} - '{}'\",\n      n_received_info_messages_,\n      message_count_on_last_update_,\n      reason);\n\n  for (int i = 0; i < 2; i++) {\n    LOG::debug_in(NodeResolutionType::NONE, \"side[{}]:\", i);\n\n    if (agent_info_[i].has_value()) {\n      LOG::debug_in(\n          NodeResolutionType::NONE,\n          \"  agent:     id='{}' az='{}' env='{}'\",\n          agent_info_[i]->id,\n          agent_info_[i]->az,\n          agent_info_[i]->env);\n    } else {\n      LOG::debug_in(NodeResolutionType::NONE, \"  agent:     null\");\n    }\n\n    if (task_info_[i].has_value()) {\n      LOG::debug_in(NodeResolutionType::NONE, \"  task:      comm='{}'\", task_info_[i]->comm);\n    } else {\n      LOG::debug_in(NodeResolutionType::NONE, \"  task:      null\");\n    }\n\n    if (socket_info_[i].has_value()) {\n      LOG::debug_in(\n          NodeResolutionType::NONE,\n          \"  socket:    local_addr='{}' local_port={} remote_addr='{}' \"\n          \"remote_port={}\",\n          socket_info_[i]->local_addr,\n          socket_info_[i]->local_port,\n          socket_info_[i]->remote_addr,\n          socket_info_[i]->remote_port);\n    } else {\n      LOG::debug_in(NodeResolutionType::NONE, \"  socket:    null\");\n    }\n\n    if (container_info_[i].has_value()) {\n      LOG::debug_in(\n          NodeResolutionType::NONE, \"  container: name='{}' pod='{}'\", container_info_[i]->name, container_info_[i]->pod);\n    } else {\n      LOG::debug_in(NodeResolutionType::NONE, \"  container: null\");\n    }\n  }\n}\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/flow_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/constants.h>\n\n#include <generated/ebpf_net/matching/modifiers.h>\n#include <generated/ebpf_net/matching/span_base.h>\n\n#include <util/ip_address.h>\n\n#include <array>\n#include <functional>\n#include <optional>\n#include <string>\n#include <tuple>\n\nnamespace reducer::matching {\n\nclass FlowSpan : public ::ebpf_net::matching::FlowSpanBase {\npublic:\n  FlowSpan();\n  ~FlowSpan();\n\n  void agent_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__agent_info *msg);\n  void task_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__task_info *msg);\n  void socket_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__socket_info *msg);\n  void k8s_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__k8s_info *msg);\n  void container_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__container_info *msg);\n  void service_info(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__service_info *msg);\n\n  void tcp_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__tcp_update *msg);\n  void udp_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__udp_update *msg);\n  void http_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__http_update *msg);\n  void dns_update(::ebpf_net::matching::weak_refs::flow span_ref, u64 timestamp, jsrv_matching__dns_update *msg);\n\n  // Updates nodes if new messages have arrived.\n  void update_nodes_if_required(::ebpf_net::matching::weak_refs::flow flow);\n\n  static void send_metrics_to_aggregation(::ebpf_net::matching::containers::flow &flows, u64 timestamp);\n\n  // NOTE: must be called on startup, from main, before any flow spans are\n  // created\n  static void enable_aws_enrichment(bool enabled);\n\nprivate:\n  struct AgentInfo {\n    std::string id;\n    std::string az;\n    std::string env;\n    std::string role;\n    std::string ns;\n  };\n\n  struct TaskInfo {\n    std::string comm;\n    std::string cgroup_name;\n  };\n\n  struct SocketInfo {\n    IPv6Address local_addr;\n    u16 local_port = 0;\n    IPv6Address remote_addr;\n    u16 remote_port = 0;\n    u8 is_connector = 0;\n    std::string remote_dns_name;\n  };\n\n  struct K8sInfo {\n    std::array<u8, 64> pod_uid_suffix;\n    u64 pod_uid_hash;\n  };\n\n  struct ContainerInfo {\n    std::string name;\n    std::string pod;\n    std::string role;\n    std::string version;\n    std::string ns;\n    NodeResolutionType type = NodeResolutionType::CONTAINER;\n  };\n\n  struct ServiceInfo {\n    std::string name;\n  };\n\n  struct AddrPort {\n    IPv6Address addr;\n    u16 port;\n  };\n\n  // Fully resolved node information.\n  struct NodeData {\n    std::string id;\n    std::string az;\n    std::string role;\n    std::string role_uid;\n    std::string version;\n    std::string env;\n    std::string ns;\n    NodeResolutionType node_type;\n    std::string address;\n    std::string comm;\n    std::string container_name;\n    std::string pod_name;\n  };\n\n  std::array<std::optional<AgentInfo>, 2> agent_info_;\n  std::array<std::optional<TaskInfo>, 2> task_info_;\n  std::array<std::optional<SocketInfo>, 2> socket_info_;\n  std::array<std::optional<K8sInfo>, 2> k8s_info_;\n  std::array<std::optional<ContainerInfo>, 2> container_info_;\n  std::array<std::optional<ServiceInfo>, 2> service_info_;\n\n  // Updates nodes using the provided full-resolved node data.\n  void update_nodes(::ebpf_net::matching::weak_refs::flow flow, NodeData const &node_a, NodeData const &node_b);\n\n  //! Sends the provided node data to the aggregation root.\n  void update_node(::ebpf_net::matching::weak_refs::agg_root agg_root, FlowSide side, NodeData const &n);\n\n  // Creates the appropriate agg_root proxy and assignes it to the span.\n  void create_agg_root(::ebpf_net::matching::weak_refs::flow flow, NodeData const &node_a, NodeData const &role_b);\n\n  // Returns true if we should try to re-enrich kubernetes information for the\n  // side\n  bool should_attempt_k8s_enrichment(::ebpf_net::matching::weak_refs::flow flow, FlowSide side) const;\n\n  // Returns the direction of metrics updates.\n  UpdateDirection metrics_update_direction(FlowSide side, int is_rx, bool force_both_sides = false);\n\n  // Resolves all available information into full node data.\n  NodeData resolve_node(::ebpf_net::matching::weak_refs::flow span_ref, FlowSide side);\n\n  std::string get_comm(FlowSide side) const;\n  std::optional<AddrPort> get_addr_port(FlowSide side) const;\n\n  // Write out the entire state of this flow, preceded with the `reason` string.\n  // Debug is gated by NodeResolutionType::NONE\n  void debug_state(const std::string_view &reason);\n\n  // Returns the ID and AZ information for the specified side.\n  // The third tuple element indicates whether this is an autonomous system.\n  std::tuple<std::string, std::string, bool> get_id_az(FlowSide side) const;\n\n  // Returns the k8s_pod span of the process for the specified side, if any.\n  ::ebpf_net::matching::auto_handles::k8s_pod get_k8s_pod(FlowSide side, ::ebpf_net::matching::Index &index);\n\n  // Side of the flow that will update metrics.\n  // Only one side will do that, to avoid double counting.\n  std::optional<FlowSide> metrics_update_side_;\n\n  // update_node caching: counter of how many messages had been received since\n  // the span was created. Can overflow as long as update_node() is called\n  // before overflow (highly likely)\n  u32 n_received_info_messages_ = 0;\n\n  // the value of n_received_info_messages_ when update_node() was last called\n  u32 message_count_on_last_update_ = ~0u;\n\n  static bool aws_enrichment_enabled_;\n};\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/k8s_container_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/matching/k8s_container_span.h>\n\n#include <reducer/ingest/agent_span.h>\n#include <reducer/matching/matching_core.h>\n#include <reducer/util/docker_image.h>\n#include <util/log.h>\n\n#include <generated/ebpf_net/matching/containers.inl>\n#include <generated/ebpf_net/matching/index.h>\n#include <generated/ebpf_net/matching/keys.h>\n#include <generated/ebpf_net/matching/modifiers.h>\n#include <generated/ebpf_net/matching/spans.h>\n#include <generated/ebpf_net/matching/weak_refs.h>\n#include <generated/ebpf_net/matching/weak_refs.inl>\n\n#include <string>\n\nnamespace reducer::matching {\n\nK8sContainerSpan::K8sContainerSpan() {}\n\nK8sContainerSpan::~K8sContainerSpan() {}\n\nvoid K8sContainerSpan::set_container_pod(\n    ::ebpf_net::matching::weak_refs::k8s_container span_ref, u64 timestamp, jsrv_matching__set_container_pod *msg)\n{\n  auto &index = span_ref.index();\n\n  ::ebpf_net::matching::keys::k8s_pod k8s_pod_key;\n  k8s_pod_key.uid_hash = msg->pod_uid_hash;\n  static_assert(sizeof(k8s_pod_key.uid_suffix) == sizeof(msg->pod_uid_suffix));\n  std::copy_n(std::begin(msg->pod_uid_suffix), k8s_pod_key.uid_suffix.max_size(), std::begin(k8s_pod_key.uid_suffix));\n\n  LOG::trace_in(\n      NodeResolutionType::K8S_CONTAINER,\n      \"matching::K8sContainerSpan::set_container_pod: looking up pod_u64={} \"\n      \"pod_id_suffix='{}' container_u64={} container_id_suffix='{}'\",\n      k8s_pod_key.uid_hash,\n      std::string_view((char *)k8s_pod_key.uid_suffix.data(), k8s_pod_key.uid_suffix.size()),\n      span_ref.uid_hash(),\n      std::string_view((char *)span_ref.uid_suffix().data(), span_ref.uid_suffix().size()));\n\n  // get the owning k8s_pod\n  auto k8s_pod = index.k8s_pod.by_key(k8s_pod_key);\n  if (!k8s_pod.valid()) {\n    LOG::trace_in(\n        NodeResolutionType::K8S_CONTAINER,\n        \"matching::K8sContainerSpan::set_container_pod: failed to\"\n        \" reference a pod: uid_suffix='{}'\",\n        std::string_view((char *)k8s_pod_key.uid_suffix.data(), k8s_pod_key.uid_suffix.size()));\n    local_core<MatchingCore>().logger().k8s_container_pod_not_found(msg->pod_uid_suffix, msg->pod_uid_hash);\n    return;\n  }\n\n  using name_t = ::ebpf_net::matching::modifiers::k8s_container::name_t;\n  using version_t = ::ebpf_net::matching::modifiers::k8s_container::version_t;\n\n  name_t const name{msg->name.buf, std::min((size_t)msg->name.len, name_t::max_len)};\n  DockerImageMetadata const image_metadata{msg->image.string_view()};\n  version_t const version{image_metadata.version().data(), std::min(image_metadata.version().size(), version_t::max_len)};\n\n  span_ref.modify().pod(std::move(k8s_pod)).name(name).version(version);\n}\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/k8s_container_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/constants.h>\n\n#include <generated/ebpf_net/matching/span_base.h>\n\nnamespace reducer::matching {\n\nclass K8sContainerSpan : public ::ebpf_net::matching::K8sContainerSpanBase {\npublic:\n  K8sContainerSpan();\n  ~K8sContainerSpan();\n\n  void set_container_pod(\n      ::ebpf_net::matching::weak_refs::k8s_container span_ref, u64 timestamp, jsrv_matching__set_container_pod *msg);\n};\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/k8s_pod_span.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/matching/k8s_pod_span.h>\n\n#include <generated/ebpf_net/matching/modifiers.h>\n#include <generated/ebpf_net/matching/spans.h>\n#include <util/log.h>\n\n#include <string>\n\nnamespace reducer::matching {\n\nK8sPodSpan::K8sPodSpan() {}\n\nK8sPodSpan::~K8sPodSpan() {}\n\nvoid K8sPodSpan::set_pod_detail(\n    ::ebpf_net::matching::weak_refs::k8s_pod span_ref, u64 timestamp, jsrv_matching__set_pod_detail *msg)\n{\n  span_ref.modify()\n      .owner_name({short_string_behavior::truncate, msg->owner_name})\n      .owner_uid({short_string_behavior::truncate, msg->owner_uid})\n      .pod_name({short_string_behavior::truncate, msg->pod_name})\n      .ns({short_string_behavior::truncate, msg->ns})\n      .version({short_string_behavior::truncate, msg->version});\n}\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/k8s_pod_span.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/constants.h>\n\n#include <generated/ebpf_net/matching/modifiers.h>\n#include <generated/ebpf_net/matching/span_base.h>\n\n#include <util/ip_address.h>\n\n#include <array>\n#include <functional>\n#include <optional>\n#include <string>\n#include <tuple>\n\nnamespace reducer::matching {\n\nclass K8sPodSpan : public ::ebpf_net::matching::K8sPodSpanBase {\npublic:\n  K8sPodSpan();\n  ~K8sPodSpan();\n\n  // store pod detail into the span\n  void set_pod_detail(::ebpf_net::matching::weak_refs::k8s_pod span_ref, u64 timestamp, jsrv_matching__set_pod_detail *msg);\n};\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/matching_core.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n#include <config.h>\n\n#include <reducer/matching/component.h>\n#include <reducer/matching/matching_core.h>\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <common/constants.h>\n\n#include <generated/ebpf_net/matching/containers.h>\n#include <generated/ebpf_net/matching/containers.inl>\n\n#include <platform/userspace-time.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/log_modifiers.h>\n#include <util/time.h>\n\n#include <functional>\n#include <stdexcept>\n\nnamespace reducer::matching {\n\nnamespace {\n\ngeoip::database make_geoip_db(std::optional<std::string> const &path)\n{\n  if (!path) {\n    // Return a no-op database object.\n    return geoip::database(std::nothrow, {});\n  }\n\n  return geoip::database(std::nothrow, path->c_str());\n}\n\n} // namespace\n\nbool MatchingCore::autonomous_system_ip_enabled_ = false;\n\nbool MatchingCore::autonomous_system_ip_enabled()\n{\n  return autonomous_system_ip_enabled_;\n}\n\nvoid MatchingCore::set_autonomous_system_ip_enabled(bool enabled)\n{\n  autonomous_system_ip_enabled_ = enabled;\n}\n\nvoid MatchingCore::enable_aws_enrichment(bool enabled)\n{\n  FlowSpan::enable_aws_enrichment(enabled);\n}\n\nMatchingCore::MatchingCore(\n    RpcQueueMatrix &ingest_to_matching_queues,\n    RpcQueueMatrix &matching_to_aggregation_queues,\n    RpcQueueMatrix &matching_to_logging_queues,\n    std::optional<std::string> geoip_path,\n    size_t shard_num,\n    u64 initial_timestamp)\n    : CoreBase(\n          \"matching\",\n          shard_num,\n          initial_timestamp,\n          matching_to_aggregation_queues.make_writers<ebpf_net::aggregation::Writer>(\n              shard_num, std::bind(&Core::current_timestamp, this)),\n          matching_to_logging_queues.make_writers<ebpf_net::logging::Writer>(\n              shard_num, std::bind(&Core::current_timestamp, this))),\n      an_db(make_geoip_db(geoip_path)),\n      ingest_to_matching_stats_(shard_num, \"ingest\", \"matching\"),\n      matching_to_aggregation_stats_(shard_num, \"matching\", \"aggregation\", matching_to_aggregation_queues),\n      matching_to_logging_stats_(shard_num, \"matching\", \"logging\", matching_to_logging_queues),\n      core_stats_(index_.core_stats.alloc()),\n      logger_(index_.logger.alloc())\n{\n  add_rpc_clients(ingest_to_matching_queues.make_readers(shard_num), ClientType::ingest, ingest_to_matching_stats_);\n}\n\nebpf_net::matching::weak_refs::logger MatchingCore::logger()\n{\n  return logger_;\n}\n\nvoid MatchingCore::on_timeslot_complete()\n{\n  send_metrics_to_aggregation();\n\n  matching_to_aggregation_stats_.check_utilization();\n  matching_to_logging_stats_.check_utilization();\n\n  index_.send_pulse();\n}\n\nvoid MatchingCore::send_metrics_to_aggregation()\n{\n  // use one timeslot before the current one\n  u64 slot_timestamp = current_timestamp() - (u64)timeslot_duration();\n\n  if (index_.flow.tcp_a_to_b_ready(slot_timestamp)) {\n    FlowSpan::send_metrics_to_aggregation(index_.flow, slot_timestamp);\n  }\n}\n\nvoid MatchingCore::write_internal_stats()\n{\n  u64 time_ns = fp_get_time_ns();\n  write_common_stats_to_logging_core(core_stats_, time_ns);\n  ingest_to_matching_stats_.write_internal_metrics_to_logging_core(core_stats_, time_ns);\n  matching_to_aggregation_stats_.write_internal_metrics_to_logging_core(core_stats_, time_ns);\n  matching_to_logging_stats_.write_internal_metrics_to_logging_core(core_stats_, time_ns);\n\n  dump_internal_state(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds{time_ns}));\n}\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/matching/matching_core.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/core_base.h>\n\n#include <geoip/geoip.h>\n#include <reducer/publisher.h>\n#include <reducer/rpc_stats.h>\n#include <reducer/tsdb_format.h>\n\n#include <generated/ebpf_net/logging/writer.h>\n#include <generated/ebpf_net/matching/connection.h>\n#include <generated/ebpf_net/matching/index.h>\n#include <generated/ebpf_net/matching/protocol.h>\n#include <generated/ebpf_net/matching/span_base.h>\n#include <generated/ebpf_net/matching/transform_builder.h>\n\n#include <memory>\n\nnamespace reducer {\nclass RpcQueueMatrix;\n}\n\nnamespace reducer::matching {\n\n// This class implements the 'matching' app (see render definition file).\n//\n// It receives messages from one or more ingest core(s), matches received\n// information to 'flow' spans, sends enriched flows to aggregation core(s) for\n// aggreation and output to TSDB.\n//\nclass MatchingCore : public CoreBase<\n                         ebpf_net::matching::Index,\n                         ebpf_net::matching::Protocol,\n                         ebpf_net::matching::Connection,\n                         ebpf_net::matching::TransformBuilder> {\npublic:\n  // NOTE: must be called on startup, from main, before any matching cores are\n  // created\n  static void enable_aws_enrichment(bool enabled);\n\n  // Enables using IP address for autonomous systems.\n  static void set_autonomous_system_ip_enabled(bool enabled);\n  // Returns whether using IP addresses for autonomous systems is enabled.\n  static bool autonomous_system_ip_enabled();\n\n  geoip::database an_db;\n\n  MatchingCore(\n      RpcQueueMatrix &ingest_to_matching_queues,\n      RpcQueueMatrix &matching_to_aggregation_queues,\n      RpcQueueMatrix &matching_to_logging_queues,\n      std::optional<std::string> geoip_path,\n      size_t shard_num,\n      u64 initial_timestamp);\n\n  // Logger instance.\n  ::ebpf_net::matching::weak_refs::logger logger();\n\nprivate:\n  // Flag indicating whether IP addresses should be used for autonomous systems.\n  static bool autonomous_system_ip_enabled_;\n\n  // Keeper of ingest->this RPC stats.\n  RpcReceiverStats ingest_to_matching_stats_;\n  // Keeper of this->aggregation RPC stats.\n  RpcSenderStats matching_to_aggregation_stats_;\n  // Keeper of this->logging RPC stats.\n  RpcSenderStats matching_to_logging_stats_;\n\n  // accessor handle for core_worker_internal_metrics span\n  ::ebpf_net::matching::auto_handles::core_stats core_stats_;\n\n  // For writing logs to the logging core.\n  ::ebpf_net::matching::auto_handles::logger logger_;\n\n  void on_timeslot_complete() override;\n\n  // Sends metrics from the metrics store to the aggregation core.\n  void send_metrics_to_aggregation();\n\n  // Outputs internal stats to be scraped by a time-series DB.\n  void write_internal_stats() override;\n};\n\n} // namespace reducer::matching\n"
  },
  {
    "path": "reducer/metric_info.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"metric_info.h\"\n\nnamespace {\n\nstatic constexpr std::string_view UNIT_BYTES = \"By\";\nstatic constexpr std::string_view UNIT_MICROSECONDS = \"us\";\nstatic constexpr std::string_view UNIT_DIMENSIONLESS = \"1\";\n\n} // namespace\n\nnamespace reducer {\n\n////////////////////////////////////////////////////////////////////////////////\n// TCP\n//\n\nTcpMetricInfo TcpMetricInfo::bytes{\n    TcpMetrics::bytes,\n    \"The total number of TCP bytes between the source and destination measured for the prior thirty seconds.\",\n    UNIT_BYTES,\n    MetricTypeSum};\n\nTcpMetricInfo TcpMetricInfo::rtt_num_measurements{\n    TcpMetrics::rtt_num_measurements,\n    \"The number of measurements made in calculating the current RTT average value.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeGauge};\n\nTcpMetricInfo TcpMetricInfo::active{\n    TcpMetrics::active,\n    \"The number of TCP connections considered to be open and alive between the source and destination at the point the measurement was taken.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeGauge};\n\nTcpMetricInfo TcpMetricInfo::rtt_average{\n    TcpMetrics::rtt_average,\n    \"The computed average round trip time between the source and destination as measured in microseconds.\",\n    UNIT_MICROSECONDS,\n    MetricTypeGauge};\n\nTcpMetricInfo TcpMetricInfo::packets{\n    TcpMetrics::packets,\n    \"The total number of TCP packets between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nTcpMetricInfo TcpMetricInfo::retrans{\n    TcpMetrics::retrans,\n    \"The total number of TCP retransmission requests between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nTcpMetricInfo TcpMetricInfo::syn_timeouts{\n    TcpMetrics::syn_timeouts,\n    \"The total number of TCP SYN timeouts between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nTcpMetricInfo TcpMetricInfo::new_sockets{\n    TcpMetrics::new_sockets,\n    \"The total number of new TCP sockets opened between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nTcpMetricInfo TcpMetricInfo::resets{\n    TcpMetrics::resets,\n    \"The total number of TCP resets sent between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\n////////////////////////////////////////////////////////////////////////////////\n// UDP\n//\n\nUdpMetricInfo UdpMetricInfo::bytes{\n    UdpMetrics::bytes,\n    \"The total number of UDP bytes between the source and destination measured for the prior thirty seconds.\",\n    UNIT_BYTES,\n    MetricTypeSum};\n\nUdpMetricInfo UdpMetricInfo::packets{\n    UdpMetrics::packets,\n    \"The total number of UDP packets between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nUdpMetricInfo UdpMetricInfo::active{\n    UdpMetrics::active,\n    \"The number of UDP connections considered to be open and alive between the source and destination at the point the measurement was taken.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeGauge};\n\nUdpMetricInfo UdpMetricInfo::drops{\n    UdpMetrics::drops,\n    \"The total number of UDP connections dropped between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\n////////////////////////////////////////////////////////////////////////////////\n// DNS\n//\n\nDnsMetricInfo DnsMetricInfo::client_duration_average{\n    DnsMetrics::client_duration_average,\n    \"This metric is the average duration in microseconds from when the client sends a DNS request, until the response is received back from the server.\"\n    \" As such, it includes the communication round-trip times, plus the server processing latency.\"\n    \" Computed by the summation of all times, divided by dns.responses.\",\n    UNIT_MICROSECONDS,\n    MetricTypeGauge};\n\nDnsMetricInfo DnsMetricInfo::server_duration_average{\n    DnsMetrics::server_duration_average,\n    \"This metric is the average duration in microseconds for the server to respond to a request received locally. \"\n    \" Thus, it does not include the network latency from or to the client.\"\n    \" Computed by the summation of all times, divided by dns.responses.\",\n    UNIT_MICROSECONDS,\n    MetricTypeGauge};\n\nDnsMetricInfo DnsMetricInfo::active_sockets{\n    DnsMetrics::active_sockets,\n    \"The number of DNS connections for which measurements were taken in the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeGauge};\n\nDnsMetricInfo DnsMetricInfo::responses{\n    DnsMetrics::responses,\n    \"The total number of DNS responses sent between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\nDnsMetricInfo DnsMetricInfo::timeouts{\n    DnsMetrics::timeouts,\n    \"The total number of DNS timeouts between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n\n////////////////////////////////////////////////////////////////////////////////\n// HTTP\n//\n\nHttpMetricInfo HttpMetricInfo::client_duration_average{\n    HttpMetrics::client_duration_average,\n    \"This metric is the average duration in microseconds from when the client sends an HTTP request, until the response is received back from the server.\"\n    \" As such, it includes the communication round-trip times, plus the server processing latency.\"\n    \" Computed by summation of all times, divided by http.active_sockets.\",\n    UNIT_MICROSECONDS,\n    MetricTypeGauge};\n\nHttpMetricInfo HttpMetricInfo::server_duration_average{\n    HttpMetrics::server_duration_average,\n    \"This metric is the average duration in microseconds for the server to respond to a request received locally.\"\n    \" Thus, it does not include the network latency from or to the client.\"\n    \" Computed by summation of all times, divided by http.active_sockets.\",\n    UNIT_MICROSECONDS,\n    MetricTypeGauge};\n\nHttpMetricInfo HttpMetricInfo::active_sockets{\n    HttpMetrics::active_sockets,\n    \"The number of unencrypted HTTPv1 connections for which measurements were taken in the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeGauge};\n\nHttpMetricInfo HttpMetricInfo::status_code{\n    HttpMetrics::status_code,\n    \"For a given class of response code (see 'response_code' dimension), the number of times an unencrypted server sent an\"\n    \" HTTPv1 status code between the source and destination measured for the prior thirty seconds.\",\n    UNIT_DIMENSIONLESS,\n    MetricTypeSum};\n} // namespace reducer\n"
  },
  {
    "path": "reducer/metric_info.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include \"outbound_metrics.h\"\n\n#include <string>\n#include <string_view>\n\nnamespace reducer {\n// Metric Types\n//\nenum MetricType {\n  // OTLP Metric Data Sum\n  MetricTypeSum,\n  // OTLP Metric Data Gauge\n  MetricTypeGauge,\n};\n\n// Information associated with a metric.\n//\nstruct MetricInfo {\n  // Canonical metric name (e.g. `tcp.bytes`).\n  const std::string name;\n  // Description of the metric.\n  const std::string description;\n  // Unit in which the metric value is reported (format described in http://unitsofmeasure.org/ucum.html).\n  const std::string unit;\n  // Metric Type\n  MetricType type;\n\n  explicit MetricInfo(\n      std::string_view name_, std::string_view description_ = \"\", std::string_view unit_ = \"\", MetricType type_ = MetricTypeSum)\n      : name(name_), description(description_), unit(unit_), type(type_)\n  {}\n};\n\n// Used for information on outbound metrics.\n// The template parameter is one of enums defined in `outbound_metrics.h`.\n//\ntemplate <typename T> struct OutboundMetricInfo : public MetricInfo {\n  typedef T metric_group_t;\n  const metric_group_t metric;\n\n  OutboundMetricInfo(T metric_, std::string_view description_, std::string_view unit_, MetricType type_ = MetricTypeSum)\n      : MetricInfo(to_string(metric_), description_, unit_, type_), metric(metric_)\n  {}\n};\n\n// Information on TCP outbound metrics.\n//\nstruct TcpMetricInfo : public OutboundMetricInfo<TcpMetrics> {\n  using OutboundMetricInfo<TcpMetrics>::OutboundMetricInfo;\n\n  static TcpMetricInfo bytes;\n  static TcpMetricInfo rtt_num_measurements;\n  static TcpMetricInfo active;\n  static TcpMetricInfo rtt_average;\n  static TcpMetricInfo packets;\n  static TcpMetricInfo retrans;\n  static TcpMetricInfo syn_timeouts;\n  static TcpMetricInfo new_sockets;\n  static TcpMetricInfo resets;\n};\n\n// Information on UDP outbound metrics.\n//\nstruct UdpMetricInfo : public OutboundMetricInfo<UdpMetrics> {\n  using OutboundMetricInfo<UdpMetrics>::OutboundMetricInfo;\n\n  static UdpMetricInfo bytes;\n  static UdpMetricInfo packets;\n  static UdpMetricInfo active;\n  static UdpMetricInfo drops;\n};\n\n// Information on DNS outbound metrics.\n//\nstruct DnsMetricInfo : public OutboundMetricInfo<DnsMetrics> {\n  using OutboundMetricInfo<DnsMetrics>::OutboundMetricInfo;\n\n  static DnsMetricInfo client_duration_average;\n  static DnsMetricInfo server_duration_average;\n  static DnsMetricInfo active_sockets;\n  static DnsMetricInfo responses;\n  static DnsMetricInfo timeouts;\n};\n\n// Information on HTTP outbound metrics.\n//\nstruct HttpMetricInfo : public OutboundMetricInfo<HttpMetrics> {\n  using OutboundMetricInfo<HttpMetrics>::OutboundMetricInfo;\n\n  static HttpMetricInfo client_duration_average;\n  static HttpMetricInfo server_duration_average;\n  static HttpMetricInfo active_sockets;\n  static HttpMetricInfo status_code;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/null_publisher.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include <reducer/null_publisher.h>\n#include <util/time.h>\n\nnamespace reducer {\n\nNullPublisher::NullPublisher() {}\n\nNullPublisher::~NullPublisher() {}\n\nPublisher::WriterPtr NullPublisher::make_writer(size_t thread_num)\n{\n  return std::make_unique<Writer>(thread_num, server_address_and_port_);\n}\n\nvoid NullPublisher::write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const {}\n\n////////////////////////////////////////////////////////////////////////////////\n// Writer\n//\n\nNullPublisher::Writer::Writer(size_t thread_num, std::string const &server_address_and_port)\n    : thread_num_(thread_num), server_address_and_port_(server_address_and_port)\n{}\n\nNullPublisher::Writer::~Writer() {}\n\nvoid NullPublisher::Writer::write(std::stringstream &ss) {}\n\nvoid NullPublisher::Writer::write(std::string_view prefix, std::string_view labels, std::string_view suffix) {}\n\nvoid NullPublisher::Writer::flush() {}\n\nvoid NullPublisher::Writer::write_internal_stats(\n    InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const\n{}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/null_publisher.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config.h>\n\n#include \"publisher.h\"\n\n#include <optional>\n#include <string>\n#include <vector>\n\nnamespace reducer {\nclass InternalMetricsEncoder;\n\n// Dummy class to stop publishing internal metrics.\n\nclass NullPublisher : public Publisher {\npublic:\n  class Writer;\n\n  NullPublisher();\n\n  virtual ~NullPublisher();\n\n  // Creates a writer object for the specified thread.\n  virtual WriterPtr make_writer(size_t thread_num) override;\n\n  // Gets this writer's stats encoded for TSDB output.\n  virtual void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const override;\n\nprivate:\n  std::string server_address_and_port_;\n};\n\n// Writer for NullPublisher.\n//\nclass NullPublisher::Writer : public Publisher::Writer {\npublic:\n  Writer(size_t thread_num, std::string const &server_address_and_port);\n\n  Writer(Writer const &) = delete;\n  Writer(Writer &&) = default;\n\n  ~Writer();\n\n  // Writes provided stream's content.\n  void write(std::stringstream &ss) override;\n\n  // Writes prefix, followed by labels, finished with suffix.\n  void write(std::string_view prefix, std::string_view labels, std::string_view suffix) override;\n\n  void flush() override;\n\n  // Gets this writer's stats encoded for TSDB output.\n  void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const override;\n\nprivate:\n  size_t thread_num_;\n  std::string server_address_and_port_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/otlp_grpc_formatter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include \"otlp_grpc_formatter.h\"\n\n#include <generated/ebpf_net/metrics.h>\n#include <util/overloaded_visitor.h>\n\nnamespace reducer {\n\nbool OtlpGrpcFormatter::metric_description_field_enabled_ = false;\n\nvoid OtlpGrpcFormatter::set_metric_description_field_enabled(bool enabled)\n{\n  metric_description_field_enabled_ = enabled;\n}\n\nbool OtlpGrpcFormatter::metric_description_field_enabled()\n{\n  return metric_description_field_enabled_;\n}\n\nOtlpGrpcFormatter::OtlpGrpcFormatter(Publisher::WriterPtr const &writer)\n{\n  writer_ = dynamic_cast<OtlpGrpcPublisher::Writer *>(writer.get());\n}\n\nOtlpGrpcFormatter::~OtlpGrpcFormatter()\n{\n  flush();\n}\n\nvoid OtlpGrpcFormatter::flush()\n{\n  if (writer_) {\n    writer_->flush();\n  }\n}\n\nvoid OtlpGrpcFormatter::rebuild_labels(labels_t const &labels)\n{\n  labels_cache_.clear();\n  for (auto const &kv : labels) {\n    ::Label l;\n    l.key = ::rust::String(kv.first);\n    l.value = ::rust::String(kv.second);\n    labels_cache_.push_back(l);\n  }\n}\n\nvoid OtlpGrpcFormatter::format(\n    MetricInfo const &metric_info,\n    value_t val,\n    std::string_view /*aggregation*/, // included in labels\n    bool /*aggregation_changed*/,\n    rollup_t /*rollup*/,\n    bool /*rollup_changed*/,\n    labels_t labels,\n    bool labels_changed,\n    timestamp_t timestamp,\n    bool /*timestamp_changed*/,\n    Publisher::WriterPtr const & /*unused_writer*/)\n{\n  if (!writer_)\n    return;\n\n  if (labels_changed) {\n    rebuild_labels(labels);\n  }\n\n  auto &rp = writer_->rust_publisher();\n\n  ::MetricKind kind = metric_info.type == MetricTypeSum ? ::MetricKind::Sum : ::MetricKind::Gauge;\n  ::rust::Str name(metric_info.name);\n  ::rust::Str unit(metric_info.unit);\n  ::rust::Str description(OtlpGrpcFormatter::metric_description_field_enabled() ? metric_info.description : std::string(\"\"));\n  auto ts_ns = static_cast<int64_t>(timestamp.count());\n\n  std::visit(\n      overloaded_visitor{\n          [&](u32 v) { rp.publish_metric_u64(name, unit, description, kind, labels_cache_, ts_ns, static_cast<uint64_t>(v)); },\n          [&](u64 v) { rp.publish_metric_u64(name, unit, description, kind, labels_cache_, ts_ns, v); },\n          [&](double v) { rp.publish_metric_f64(name, unit, description, kind, labels_cache_, ts_ns, v); },\n      },\n      val);\n}\n\nvoid OtlpGrpcFormatter::format_flow_log(\n    ebpf_net::metrics::tcp_metrics const &tcp_metrics,\n    labels_t labels,\n    bool labels_changed,\n    timestamp_t timestamp,\n    bool /*timestamp_changed*/)\n{\n  if (!writer_)\n    return;\n  if (labels_changed) {\n    rebuild_labels(labels);\n  }\n  auto &rp = writer_->rust_publisher();\n  auto ts_ns = static_cast<int64_t>(timestamp.count());\n\n  double sum_srtt = double(tcp_metrics.sum_srtt) / 8 / 1'000'000; // RTTs are measured in units of 1/8 microseconds.\n\n  rp.publish_flow_log(\n      labels_cache_,\n      ts_ns,\n      tcp_metrics.sum_bytes,\n      tcp_metrics.active_rtts,\n      tcp_metrics.active_sockets,\n      tcp_metrics.active_rtts ? sum_srtt / tcp_metrics.active_rtts : 0.0,\n      tcp_metrics.sum_delivered,\n      tcp_metrics.sum_retrans,\n      tcp_metrics.syn_timeouts,\n      tcp_metrics.new_sockets,\n      tcp_metrics.tcp_resets);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/otlp_grpc_formatter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"otlp_grpc_publisher.h\"\n#include \"tsdb_formatter.h\"\n\n#include <otlp_export_cxxbridge.h>\n\n// Retained for config compatibility; not used by Rust exporter.\nextern int global_otlp_grpc_batch_size;\n\nnamespace reducer {\n\n// TsdbFormatter that calls into Rust OTLP exporter via cxx bridge\nclass OtlpGrpcFormatter : public TsdbFormatter {\npublic:\n  static void set_metric_description_field_enabled(bool enabled);\n  static bool metric_description_field_enabled();\n\n  explicit OtlpGrpcFormatter(Publisher::WriterPtr const &writer);\n  virtual ~OtlpGrpcFormatter() override;\n\n  virtual void flush() override;\n\nprotected:\n  void format(\n      MetricInfo const &metric_info,\n      value_t value,\n      std::string_view aggregation,\n      bool aggregation_changed,\n      rollup_t rollup,\n      bool rollup_changed,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed,\n      Publisher::WriterPtr const &unused_writer) override;\n\n  void format_flow_log(\n      ebpf_net::metrics::tcp_metrics const &tcp_metrics,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed) override;\n\nprivate:\n  // Cached labels to avoid rebuilding when unchanged\n  ::rust::Vec<::Label> labels_cache_;\n\n  // Reference to the concrete writer that owns the Rust publisher\n  OtlpGrpcPublisher::Writer *writer_;\n\n  static bool metric_description_field_enabled_;\n\n  void rebuild_labels(labels_t const &labels);\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/otlp_grpc_publisher.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include \"otlp_grpc_publisher.h\"\n\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n\nnamespace reducer {\n\nOtlpGrpcPublisher::OtlpGrpcPublisher(size_t /*num_writer_threads*/, const std::string &endpoint) : endpoint_(endpoint) {}\n\nOtlpGrpcPublisher::~OtlpGrpcPublisher() {}\n\nPublisher::WriterPtr OtlpGrpcPublisher::make_writer(size_t thread_num)\n{\n  return std::make_unique<Writer>(thread_num, endpoint_);\n}\n\nvoid OtlpGrpcPublisher::write_internal_stats(InternalMetricsEncoder & /*encoder*/, u64 /*time_ns*/) const {}\n\n////////////////////////////////////////////////////////////////////////////////\n// Writer\n\nOtlpGrpcPublisher::Writer::Writer(size_t thread_num, std::string const &endpoint)\n    : thread_num_(thread_num), endpoint_(endpoint), publisher_(otlp_publisher_new(::rust::Str(endpoint_)))\n{}\n\nOtlpGrpcPublisher::Writer::~Writer() {}\n\nvoid OtlpGrpcPublisher::Writer::flush()\n{\n  publisher_->flush();\n}\n\nu64 OtlpGrpcPublisher::Writer::bytes_written() const\n{\n  auto s = publisher_->stats();\n  return s.bytes_sent - s.bytes_failed;\n}\n\nu64 OtlpGrpcPublisher::Writer::bytes_failed_to_write() const\n{\n  auto s = publisher_->stats();\n  return s.bytes_failed;\n}\n\nvoid OtlpGrpcPublisher::Writer::write_internal_stats(\n    InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const\n{\n  auto s = publisher_->stats();\n\n  OtlpGrpcStats stats;\n  stats.labels.shard = std::to_string(shard);\n  stats.labels.module = module;\n\n  // Report as metrics client\n  stats.labels.client_type = \"metrics\";\n  stats.metrics.bytes_failed = s.bytes_failed;\n  stats.metrics.bytes_sent = s.bytes_sent;\n  stats.metrics.metrics_failed = s.data_points_failed;\n  stats.metrics.metrics_sent = s.data_points_sent;\n  stats.metrics.requests_failed = s.requests_failed;\n  stats.metrics.requests_sent = s.requests_sent;\n  stats.metrics.unknown_response_tags = s.unknown_response_tags;\n  encoder.write_internal_stats(stats, time_ns);\n\n  // Report as logs client (mirror metrics for now)\n  stats.labels.client_type = \"logs\";\n  encoder.write_internal_stats(stats, time_ns);\n}\n\nvoid OtlpGrpcPublisher::Writer::write_internal_stats_to_logging_core(\n    ::ebpf_net::aggregation::auto_handles::agg_core_stats &agg_core_stats,\n    u64 time_ns,\n    int shard,\n    std::string_view module) const\n{\n  auto s = publisher_->stats();\n\n  agg_core_stats.agg_otlp_grpc_stats(\n      jb_blob(module),\n      shard,\n      jb_blob(std::string_view(\"metrics\")),\n      s.bytes_failed,\n      s.bytes_sent,\n      s.data_points_failed,\n      s.data_points_sent,\n      s.requests_failed,\n      s.requests_sent,\n      s.unknown_response_tags,\n      time_ns);\n\n  agg_core_stats.agg_otlp_grpc_stats(\n      jb_blob(module),\n      shard,\n      jb_blob(std::string_view(\"logs\")),\n      s.bytes_failed,\n      s.bytes_sent,\n      s.data_points_failed,\n      s.data_points_sent,\n      s.requests_failed,\n      s.requests_sent,\n      s.unknown_response_tags,\n      time_ns);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/otlp_grpc_publisher.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config.h>\n\n#include \"publisher.h\"\n\n#include <otlp_export_cxxbridge.h>\n\n#include <optional>\n#include <string>\n\nnamespace reducer {\n\n// Rust-backed OTLP publisher (gRPC exporter via cxx bridge)\nclass OtlpGrpcPublisher : public Publisher {\npublic:\n  class Writer;\n\n  // Constructs with number of writer threads and endpoint (host:port or full URL)\n  OtlpGrpcPublisher(size_t num_writer_threads, const std::string &endpoint);\n  virtual ~OtlpGrpcPublisher();\n\n  virtual WriterPtr make_writer(size_t thread_num) override;\n\n  virtual void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const override;\n\nprivate:\n  std::string endpoint_;\n};\n\nclass OtlpGrpcPublisher::Writer : public Publisher::Writer {\npublic:\n  Writer(size_t thread_num, std::string const &endpoint);\n  Writer(Writer const &) = delete;\n  Writer(Writer &&) = default;\n  ~Writer();\n\n  void flush() override;\n\n  u64 bytes_written() const override;\n  u64 bytes_failed_to_write() const override;\n\n  void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const override;\n  void write_internal_stats_to_logging_core(\n      ::ebpf_net::aggregation::auto_handles::agg_core_stats &agg_core_stats,\n      u64 time_ns,\n      int shard,\n      std::string_view module) const override;\n\n  // Access to underlying Rust publisher for formatting layer\n  ::Publisher &rust_publisher() { return *publisher_; }\n  const ::Publisher &rust_publisher() const { return *publisher_; }\n\n  const std::string &endpoint() const { return endpoint_; }\n\nprivate:\n  size_t thread_num_;\n  std::string endpoint_;\n  ::rust::Box<::Publisher> publisher_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/outbound_metrics.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <util/enum.h>\n\n// This file describes our outgoing metrics that are either hosted for\n// prometheus to scrape, or sent over OTLP.\n//\n// They are broken up into enum \"groups\" (tcp, udp, dns, http, ebpf_net),\n// and given a bit mask value.  The enum list ends with \"all\"\n// with all bits turned on, representing every metric.\n// A value of unknown is used mainly for string to enum conversions\n// and denotes the conversion failed.\n//\n// These enums are used in \"write_metrics.h\" to send them out of the reducer\n// to prometheus / OTLP.\n// They are also used in \"disable_metrics.h\" to make the metric group\n// participate in that code - that is to allow the metrics or group to be\n// disabled.\n//\n// To add a new collection of metrics, follow how the other metric groups were\n// added:\n//\n// #define ENUM_NAMESPACE reducer\n// #define ENUM_NAME <GroupName>Metrics\n// #define ENUM_TYPE std::uint32_t\n// #define ENUM_ELEMENTS(X)\n//   X(unknown, 0, \"\")                      \\.\n//   X(<enum-name1>, 0x001, <output-name1>) \\.\n//   X(<enum-name2>, 0x002, <output-name2>) \\.\n//   ...                                    \\.\n//   X(all, 0xFFFFFFFF, \"<prefix>.all\")\n// #define ENUM_DEFAULT unknown\n// #include <util/enum_operators.inl>\n//\n// If these metrics are to be disable-able, be sure to update disabled_metrics.h\n// adding your group prefix there.\n\n#define TCP_PREFIX \"tcp.\"\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME TcpMetrics\n#define ENUM_TYPE std::uint32_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(bytes, 0x001, TCP_PREFIX \"bytes\")                                                                                          \\\n  X(rtt_num_measurements, 0x002, TCP_PREFIX \"rtt.num_measurements\")                                                            \\\n  X(active, 0x004, TCP_PREFIX \"active\")                                                                                        \\\n  X(rtt_average, 0x008, TCP_PREFIX \"rtt.average\")                                                                              \\\n  X(packets, 0x010, TCP_PREFIX \"packets\")                                                                                      \\\n  X(retrans, 0x020, TCP_PREFIX \"retrans\")                                                                                      \\\n  X(syn_timeouts, 0x040, TCP_PREFIX \"syn_timeouts\")                                                                            \\\n  X(new_sockets, 0x080, TCP_PREFIX \"new_sockets\")                                                                              \\\n  X(resets, 0x100, TCP_PREFIX \"resets\")                                                                                        \\\n  X(all, 0xFFFFFFFF, TCP_PREFIX \"all\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n#undef TCP_PREFIX\n\n#define UDP_PREFIX \"udp.\"\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME UdpMetrics\n#define ENUM_TYPE std::uint32_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(bytes, 0x001, UDP_PREFIX \"bytes\")                                                                                          \\\n  X(packets, 0x002, UDP_PREFIX \"packets\")                                                                                      \\\n  X(active, 0x004, UDP_PREFIX \"active\")                                                                                        \\\n  X(drops, 0x008, UDP_PREFIX \"drops\")                                                                                          \\\n  X(all, 0xFFFFFFFF, UDP_PREFIX \"all\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n#undef UDP_PREFIX\n\n#define DNS_PREFIX \"dns.\"\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME DnsMetrics\n#define ENUM_TYPE std::uint32_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(client_duration_average, 0x001, DNS_PREFIX \"client.duration.average\")                                                      \\\n  X(server_duration_average, 0x002, DNS_PREFIX \"server.duration.average\")                                                      \\\n  X(active_sockets, 0x004, DNS_PREFIX \"active_sockets\")                                                                        \\\n  X(responses, 0x008, DNS_PREFIX \"responses\")                                                                                  \\\n  X(timeouts, 0x010, DNS_PREFIX \"timeouts\")                                                                                    \\\n  X(all, 0xFFFFFFFF, DNS_PREFIX \"all\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n#undef DNS_PREFIX\n\n#define HTTP_PREFIX \"http.\"\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME HttpMetrics\n#define ENUM_TYPE std::uint32_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(client_duration_average, 0x001, HTTP_PREFIX \"client.duration.average\")                                                     \\\n  X(server_duration_average, 0x002, HTTP_PREFIX \"server.duration.average\")                                                     \\\n  X(active_sockets, 0x004, HTTP_PREFIX \"active_sockets\")                                                                       \\\n  X(status_code, 0x008, HTTP_PREFIX \"status_code\")                                                                             \\\n  X(all, 0xFFFFFFFF, HTTP_PREFIX \"all\")\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n#undef HTTP_PREFIX\n"
  },
  {
    "path": "reducer/outbound_stats.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <util/enum.h>\n\n// This file describes our outgoing internal stats that are either hosted for\n// prometheus to scrape, or sent over OTLP.\n//\n\n// A value of unknown is used mainly for string to enum conversions\n// and denotes the conversion failed.\n//\n\n#define INTERNAL_PREFIX \"ebpf_net.\"\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME EbpfNetMetrics\n#define ENUM_TYPE std::uint64_t\n// clang-format off\n#define ENUM_ELEMENTS(X)                                                \\\n  X(unknown, 0, \"\")                                                     \\\n  X(agg_root_truncation,                 0x0000'0000'0000'0001, INTERNAL_PREFIX \"agg_root_truncation\") \\\n  X(bpf_log,                             0x0000'0000'0000'0002, INTERNAL_PREFIX \"bpf_log\") \\\n  X(client_handle_pool,                  0x0000'0000'0000'0004, INTERNAL_PREFIX \"client_handle_pool\") \\\n  X(client_handle_pool_fraction,         0x0000'0000'0000'0008, INTERNAL_PREFIX \"client_handle_pool_fraction\") \\\n  X(clock_offset_ns,                     0x0000'0000'0000'0010, INTERNAL_PREFIX \"clock_offset_ns\") \\\n  X(codetiming_avg_ns,                   0x0000'0000'0000'0020, INTERNAL_PREFIX \"codetiming_avg_ns\") \\\n  X(codetiming_count,                    0x0000'0000'0000'0040, INTERNAL_PREFIX \"codetiming_count\") \\\n  X(codetiming_max_ns,                   0x0000'0000'0000'0080, INTERNAL_PREFIX \"codetiming_max_ns\") \\\n  X(codetiming_min_ns,                   0x0000'0000'0000'0100, INTERNAL_PREFIX \"codetiming_min_ns\") \\\n  X(codetiming_sum_ns,                   0x0000'0000'0000'0200, INTERNAL_PREFIX \"codetiming_sum_ns\") \\\n  X(collector_health,                    0x0000'0000'0000'0400, INTERNAL_PREFIX \"collector_health\") \\\n  X(collector_log_count,                 0x0000'0000'0000'0800, INTERNAL_PREFIX \"collector_log_count\") \\\n  X(connections,                         0x0000'0000'0000'1000, INTERNAL_PREFIX \"connections\") \\\n  X(disconnects,                         0x0000'0000'0000'2000, INTERNAL_PREFIX \"disconnects\") \\\n  X(entrypoint_info,                     0x0000'0000'0000'4000, INTERNAL_PREFIX \"entrypoint_info\") \\\n  X(message,                             0x0000'0000'0000'8000, INTERNAL_PREFIX \"message\") \\\n  X(otlp_grpc_bytes_failed,              0x0000'0000'0001'0000, INTERNAL_PREFIX \"otlp_grpc.bytes_failed\") \\\n  X(otlp_grpc_bytes_sent,                0x0000'0000'0002'0000, INTERNAL_PREFIX \"otlp_grpc.bytes_sent\") \\\n  X(otlp_grpc_metrics_failed,            0x0000'0000'0004'0000, INTERNAL_PREFIX \"otlp_grpc.metrics_failed\") \\\n  X(otlp_grpc_metrics_sent,              0x0000'0000'0008'0000, INTERNAL_PREFIX \"otlp_grpc.metrics_sent\") \\\n  X(otlp_grpc_requests_failed,           0x0000'0000'0010'0000, INTERNAL_PREFIX \"otlp_grpc.requests_failed\") \\\n  X(otlp_grpc_requests_sent,             0x0000'0000'0020'0000, INTERNAL_PREFIX \"otlp_grpc.requests_sent\") \\\n  X(otlp_grpc_unknown_response_tags,     0x0000'0000'0040'0000, INTERNAL_PREFIX \"otlp_grpc.unknown_response_tags\") \\\n  X(pipeline_agent_connections,          0x0000'0000'0080'0000, INTERNAL_PREFIX \"pipeline_agent_connections\") \\\n  X(pipeline_message_error,              0x0000'0000'0100'0000, INTERNAL_PREFIX \"pipeline_message_error\") \\\n  X(prometheus_big_items_dropped,        0x0000'0000'0200'0000, INTERNAL_PREFIX \"prometheus.big_items_dropped\") \\\n  X(prometheus_bytes_discarded,          0x0000'0000'0400'0000, INTERNAL_PREFIX \"prometheus.bytes_discarded\") \\\n  X(prometheus_bytes_ingested,           0x0000'0000'0800'0000, INTERNAL_PREFIX \"prometheus.bytes_ingested\") \\\n  X(prometheus_bytes_written,            0x0000'0000'1000'0000, INTERNAL_PREFIX \"prometheus.bytes_written\") \\\n  X(prometheus_failed_scrapes,           0x0000'0000'2000'0000, INTERNAL_PREFIX \"prometheus.failed_scrapes\") \\\n  X(rpc_latency_ns,                      0x0000'0000'4000'0000, INTERNAL_PREFIX \"rpc_latency_ns\") \\\n  X(rpc_queue_buf_utilization,           0x0000'0000'8000'0000, INTERNAL_PREFIX \"rpc_queue_buf_utilization\") \\\n  X(rpc_queue_buf_utilization_fraction,  0x0000'0001'0000'0000, INTERNAL_PREFIX \"rpc_queue_buf_utilization_fraction\") \\\n  X(rpc_queue_elem_utilization_fraction, 0x0000'0002'0000'0000, INTERNAL_PREFIX \"rpc_queue_elem_utilization_fraction\") \\\n  X(rpc_write_stalls,                    0x0000'0004'0000'0000, INTERNAL_PREFIX \"rpc_write_stalls\") \\\n  X(span_utilization,                    0x0000'0008'0000'0000, INTERNAL_PREFIX \"span_utilization\") \\\n  X(span_utilization_fraction,           0x0000'0010'0000'0000, INTERNAL_PREFIX \"span_utilization_fraction\") \\\n  X(span_utilization_max,                0x0000'0020'0000'0000, INTERNAL_PREFIX \"span_utilization_max\") \\\n  X(time_since_last_message_ns,          0x0000'0040'0000'0000, INTERNAL_PREFIX \"time_since_last_message_ns\") \\\n  X(up,                                  0x0000'0080'0000'0000, INTERNAL_PREFIX \"up\") \\\n  X(all,                                 0xFFFF'FFFF'FFFF'FFFF, INTERNAL_PREFIX \"all\")\n// clang-format on\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n#undef INTERNAL_PREFIX\n"
  },
  {
    "path": "reducer/prometheus_formatter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"prometheus_formatter.h\"\n\n#include <util/code_timing.h>\n#include <util/time.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <ctime>\n#include <stdexcept>\n\nnamespace reducer {\nnamespace {\n\nstd::string_view prom_format_labels(char *buff_ptr, size_t buff_size, TsdbFormatter::labels_t const &labels)\n{\n  size_t written = 0;\n  auto write = [&written, buff_ptr, buff_size](void const *ptr, size_t len) {\n    size_t const n = std::min(len, (buff_size - written));\n    if (n > 0) {\n      memcpy(buff_ptr + written, ptr, n);\n      written += n;\n    }\n  };\n\n  auto write_str = [write](std::string_view str) { write(str.data(), str.size()); };\n\n  size_t num_labels = 0;\n  auto write_label = [&num_labels, write_str](std::string_view name, std::string_view value) {\n    if (num_labels++ > 0) {\n      write_str(\",\");\n    }\n    write_str(name);\n    write_str(\"=\\\"\");\n    write_str(value);\n    write_str(\"\\\"\");\n  };\n\n  write_str(\"{\");\n\n  for (auto const &[name, value] : labels) {\n    std::string label_name_sanitized;\n    std::transform(name.begin(), name.end(), std::back_inserter(label_name_sanitized), [](unsigned char c) -> unsigned char {\n      return c == '.' ? '_' : c;\n    });\n\n    write_label(label_name_sanitized, value);\n  }\n\n  write_str(\"}\");\n\n  return std::string_view(buff_ptr, written);\n}\n\ntemplate <typename T> std::string_view prom_format_suffix(char *buf_ptr, size_t buf_size, T value, std::string_view timestamp)\n{\n  auto [end, len] = fmt::format_to_n(buf_ptr, buf_size, \" {} {}\\n\", value, timestamp);\n  return std::string_view(buf_ptr, std::min(len, buf_size));\n}\n\n} // namespace\n\nvoid PrometheusFormatter::format(\n    MetricInfo const &metric,\n    value_t value,\n    std::string_view aggregation,\n    bool aggregation_changed,\n    rollup_t rollup,\n    bool rollup_changed,\n    labels_t labels,\n    bool labels_changed,\n    timestamp_t timestamp,\n    bool timestamp_changed,\n    Publisher::WriterPtr const &writer)\n{\n  START_TIMING(PrometheusFormatterFormat);\n  if (timestamp_changed || timestamp_str_.empty()) {\n    timestamp_str_ = std::to_string(integer_time<std::chrono::milliseconds>(timestamp));\n  }\n\n  if (aggregation_changed || rollup_changed || labels_changed || labels_.empty()) {\n    labels_ = prom_format_labels(labels_buf_, sizeof(labels_buf_), labels);\n  }\n\n  auto suffix = std::visit(\n      [&](auto &&val) -> std::string_view { return prom_format_suffix(suffix_buf_, sizeof(suffix_buf_), val, timestamp_str_); },\n      value);\n\n  std::string metric_name_sanitized;\n  std::transform(\n      metric.name.begin(), metric.name.end(), std::back_inserter(metric_name_sanitized), [](unsigned char c) -> unsigned char {\n        return c == '.' ? '_' : c;\n      });\n  STOP_TIMING(PrometheusFormatterFormat);\n\n  SCOPED_TIMING(PrometheusFormatterFormatWriterWrite);\n  writer->write(metric_name_sanitized, labels_, suffix);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/prometheus_formatter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"tsdb_formatter.h\"\n\nnamespace reducer {\n\n// Formatter implementation for Prometheus time-series format.\n//\n// Output:\n// - prefix: `<metric_name>`\n// - labels: `{label1=\"xxx\",label2=\"yyy\",...}`\n// - suffix: ` <value> <milliseconds>`\n//\nclass PrometheusFormatter : public TsdbFormatter {\nprotected:\n  void format(\n      MetricInfo const &metric,\n      value_t value,\n      std::string_view aggregation,\n      bool aggregation_changed,\n      rollup_t rollup,\n      bool rollup_changed,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed,\n      Publisher::WriterPtr const &writer) override;\n\nprivate:\n  // Large enough for all labels.\n  char labels_buf_[8192];\n  // Large enough for value and timestamp.\n  char suffix_buf_[64];\n\n  // Cached labels string, points to labels_buf_ when initialized.\n  std::string_view labels_;\n  // Cached textual representation of timestamp.\n  std::string timestamp_str_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/prometheus_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"prometheus_handler.h\"\n\n#include <util/log.h>\n\n#include <charconv>\n#include <optional>\n#include <string>\n\nnamespace reducer {\n\nnamespace {\n\nstatic constexpr size_t chunk_buffer_size = (1 << 20);\n\nstatic constexpr char const *response_content_type = \"text/plain;version=0.0.4\";\n\nstatic const auto default_timeout = std::chrono::seconds(5);\n\n// Returns number of bytes read and an indication whether there is more\n// content in the queue to be read.\nstd::pair<u32, bool> read_from_queue(ElementQueue &queue, char *buffer, u32 buffer_size)\n{\n  u32 len = 0;\n  bool more = false;\n\n  queue.start_read_batch();\n\n  while ((more = (queue.peek() > 0)) == true) {\n    if ((len + queue.peek()) > buffer_size) {\n      break; /* we've filled the buffer */\n    }\n\n    char *elem_buf = nullptr;\n    int elem_len = queue.read(elem_buf);\n    assert(elem_len >= 0);\n\n    memcpy(buffer + len, elem_buf, elem_len);\n    len += elem_len;\n  }\n\n  queue.finish_read_batch();\n\n  return std::make_pair(len, more);\n}\n\n} // namespace\n\nPrometheusHandler::PrometheusHandler(\n    std::vector<ElementQueueStoragePtr> const &queues, std::optional<u64> scrape_size_limit_bytes)\n    : queue_mutex_(queues.size()), scrape_size_limit_bytes_(scrape_size_limit_bytes)\n{\n  queues_.reserve(queues.size());\n  for (auto &queue_storage : queues) {\n    queues_.emplace_back(queue_storage);\n  }\n}\n\nstd::optional<PrometheusHandler::timeout_t> PrometheusHandler::get_scrape_timeout(CivetServer *server, mg_connection *conn)\n{\n  char const *hdr = server->getHeader(conn, \"X-Prometheus-Scrape-Timeout-Seconds\");\n\n  if (hdr == nullptr) {\n    // not specified\n    return std::nullopt;\n  }\n\n  double seconds = atof(hdr);\n\n  if (seconds <= 0) {\n    // invalid value\n    return std::nullopt;\n  }\n\n  return std::chrono::milliseconds(static_cast<std::chrono::milliseconds::rep>(seconds * 1000));\n}\n\nvoid PrometheusHandler::write_content_from_queues(CivetServer *server, mg_connection *conn)\n{\n  // max duration of the request\n  timeout_t timeout = default_timeout;\n\n  if (auto scrape_timeout = get_scrape_timeout(server, conn); scrape_timeout) {\n    timeout = *scrape_timeout;\n\n    // leave some time for network\n    //\n    if (timeout > 1s) {\n      // trim half a second\n      timeout -= 500ms;\n    } else {\n      // halve the value\n      timeout /= 2;\n    }\n  }\n\n  // absolute time at which to finish sending\n  u64 timeout_ns = monotonic() + std::chrono::nanoseconds(timeout).count();\n\n  char chunk_buffer[chunk_buffer_size];\n  u32 chunk_len = 0;\n  u64 bytes_sent = 0;\n  bool error = false;\n\n  auto send_chunk = [&]() {\n    if (auto nsent = mg_send_chunk(conn, chunk_buffer, chunk_len); nsent > 0) {\n      bytes_sent += nsent;\n      chunk_len = 0;\n    } else {\n      error = true;\n    }\n  };\n\n  std::lock_guard<std::mutex> lock(handler_mutex_);\n\n  // start the response\n  if (mg_send_http_ok(conn, response_content_type, -1) == -1) {\n    ++num_failed_scrapes_;\n    return;\n  }\n\n  u64 scrape_limit = scrape_size_limit_bytes_.value_or(std::numeric_limits<u64>::max());\n\n  // This will get set when a read from a queue can't be performed because\n  // the scrape size limit would be exceeded. In that case we will complete\n  // the request even if a read some other queue would succeed.\n  bool scrape_size_limited = false;\n\n  for (size_t i = 0; i < queues_.size(); ++i) {\n    if (error) {\n      break;\n    }\n\n    if (monotonic() >= timeout_ns) {\n      break;\n    }\n\n    if (scrape_size_limited) {\n      break;\n    }\n\n    if (bytes_sent >= scrape_limit) {\n      break;\n    }\n\n    auto &queue = queues_[next_queue_];\n    next_queue_ = (next_queue_ + 1) % queues_.size();\n\n    bool more;\n    u32 repeat = 0;\n    do {\n      u64 scrape_remaining = scrape_limit - bytes_sent;\n      u64 chunk_remaining = chunk_buffer_size - chunk_len;\n\n      u32 nread;\n      std::tie(nread, more) = read_from_queue(queue, chunk_buffer + chunk_len, std::min(chunk_remaining, scrape_remaining));\n\n      if (nread > 0) {\n        // something was read and added to the chunk buffer\n        chunk_len += nread;\n      }\n\n      if (more) {\n        if (chunk_remaining < scrape_remaining) {\n          // There was more to read but didn't fit in the chunk buffer.\n          // We will flush the chunk buffer and try again.\n          send_chunk();\n        } else {\n          // There was more to read but it would go over the scrape size limit.\n          // Setting this flag will conclude this request.\n          scrape_size_limited = true;\n          break;\n        }\n      }\n\n      // If there is more to read it is because there was not enough space in\n      // the chunk buffer. We will repeat the read -- only once, otherwise we\n      // can get into a race with the producer.\n    } while (more && !error && (bytes_sent < scrape_limit) && (repeat++ < 2));\n  }\n\n  if (!error && (chunk_len > 0)) {\n    // send what is left to send\n    send_chunk();\n  }\n\n  // terminating chunk\n  mg_send_chunk(conn, nullptr, 0);\n\n  bytes_served_ += bytes_sent;\n  if (error) {\n    ++num_failed_scrapes_;\n  }\n}\n\nvoid PrometheusHandler::write_content_from_queue(CivetServer *server, mg_connection *conn, size_t queue_num)\n{\n  // max duration of the request\n  timeout_t timeout = default_timeout;\n\n  if (auto scrape_timeout = get_scrape_timeout(server, conn); scrape_timeout) {\n    timeout = *scrape_timeout;\n\n    // leave some time for network\n    //\n    if (timeout > 1s) {\n      // trim half a second\n      timeout -= 500ms;\n    } else {\n      // halve the value\n      timeout /= 2;\n    }\n  }\n\n  // absolute time at which to finish sending\n  u64 timeout_ns = monotonic() + std::chrono::nanoseconds(timeout).count();\n\n  char chunk_buffer[chunk_buffer_size];\n  u64 bytes_sent = 0;\n  bool error = false;\n\n  std::lock_guard<std::mutex> lock(queue_mutex_[queue_num]);\n\n  auto &queue = queues_[queue_num];\n\n  // start the response\n  if (mg_send_http_ok(conn, response_content_type, -1) == -1) {\n    ++num_failed_scrapes_;\n    return;\n  }\n\n  while (monotonic() < timeout_ns) {\n    u64 read_size = chunk_buffer_size;\n\n    if (scrape_size_limit_bytes_.has_value()) {\n      // Limit this read so the total number of bytes returned doesn't go\n      // over the scrape limit.\n      if (*scrape_size_limit_bytes_ > bytes_sent) {\n        read_size = std::min(chunk_buffer_size, *scrape_size_limit_bytes_ - bytes_sent);\n      } else {\n        // Already went over the limit. This can happen because mg_send_chunk\n        // is writing additional data (chunk headers, etc.)\n        break;\n      }\n    }\n\n    auto [nread, more] = read_from_queue(queue, chunk_buffer, read_size);\n\n    if (nread == 0) {\n      // nothing more to read from the queue\n      // assert(!more)\n      break;\n    }\n\n    // NOTE: mg_send_chunk doesn't do partial writes\n    auto nsent = mg_send_chunk(conn, chunk_buffer, nread);\n\n    if (nsent > 0) {\n      bytes_sent += nsent;\n    } else {\n      // zero signifies connection closed -- we count that also as failed write\n      error = true;\n      break;\n    }\n\n    if (!more) {\n      // Exhausted the queue. We don't want to try to read again as that can get\n      // us into a race with the producer.\n      break;\n    }\n  }\n\n  // terminating chunk\n  mg_send_chunk(conn, nullptr, 0);\n\n  bytes_served_ += bytes_sent;\n  if (error) {\n    ++num_failed_scrapes_;\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nPortRangePromHandler::PortRangePromHandler(\n    std::vector<ElementQueueStoragePtr> const &queues,\n    std::optional<u64> scrape_size_limit_bytes,\n    std::optional<u16> extra_ports_base)\n    : PrometheusHandler(queues, scrape_size_limit_bytes), extra_ports_base_(extra_ports_base)\n{}\n\nint PortRangePromHandler::choose_queue(CivetServer *server, mg_connection *conn)\n{\n  std::string queue_query_parameter;\n\n  // First, try to get the queue number from the URI query parameters.\n  // If it exists, we'll use it.\n  if (server->getParam(conn, \"queue\", queue_query_parameter, 0) == true) {\n    int queue_num;\n\n    if (auto [_, ec] = std::from_chars(\n            queue_query_parameter.data(), queue_query_parameter.data() + queue_query_parameter.size(), queue_num);\n        ec == std::errc()) {\n      // got a queue parameter. Validate the requested queue is not negative\n      if (queue_num < 0) {\n        LOG::error(\"PrometheusHandler: negative queue number in query parameter: {}\", queue_query_parameter);\n        mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n        return -1;\n      }\n\n      // non-negative queue number. can return it as chosen queue.\n      return queue_num;\n\n    } else {\n      LOG::error(\"PrometheusHandler: invalid queue number in query parameter: {}\", queue_query_parameter);\n      mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n      return -1;\n    }\n  }\n\n  // Didn't have an explicit query parameter. Try to extract the queue number from\n  // the port number in the Host header.\n  std::string_view host;\n  std::optional<int> port;\n  if (char const *hdr = server->getHeader(conn, \"Host\"); hdr != nullptr) {\n    host = hdr;\n  } else {\n    LOG::error(\"PrometheusHandler: missing required Host header\");\n    mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n    return -1;\n  }\n\n  if (auto sep = host.rfind(':'); sep != std::string_view::npos) {\n    std::string_view port_txt = host.substr(sep + 1);\n    int port_num;\n\n    if (auto [_, ec] = std::from_chars(port_txt.data(), port_txt.data() + port_txt.size(), port_num); ec == std::errc()) {\n      port = port_num;\n    } else {\n      LOG::error(\"PrometheusHandler: invalid port number in Host header: {}\", port_txt);\n      mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n      return -1;\n    }\n  } else {\n    LOG::warn(\"PrometheusHandler: port number not specified in Host header\");\n  }\n\n  std::size_t requested_queue = 0; // default\n\n  // NOTE: only one listening address can be selected using a command-line\n  // parameter, so it is safe to assume that the server has only one\n  // listening port.\n  int listening_port = server->getListeningPorts().at(0);\n\n  if (port.has_value() && (*port != listening_port)) {\n    int extra_ports_base = extra_ports_base_.value_or(listening_port + 1);\n\n    if (*port >= extra_ports_base) {\n      requested_queue = *port - extra_ports_base + 1;\n    } else {\n      LOG::error(\"PrometheusHandler: invalid port number\");\n      mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n      return -1;\n    }\n  }\n\n  return requested_queue;\n}\n\nbool PortRangePromHandler::handleGet(CivetServer *server, mg_connection *conn)\n{\n  auto requested_queue = choose_queue(server, conn);\n  if (requested_queue < 0) {\n    // there was a failure, error already printed and error code sent.\n    return true;\n  }\n\n  if ((size_t)requested_queue >= num_queues()) {\n    LOG::error(\"PrometheusHandler: invalid queue number\");\n    mg_printf(conn, \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n    return true;\n  }\n\n  write_content_from_queue(server, conn, requested_queue);\n\n  return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nSinglePortPromHandler::SinglePortPromHandler(\n    std::vector<ElementQueueStoragePtr> const &queues, std::optional<u64> scrape_size_limit_bytes)\n    : PrometheusHandler(queues, scrape_size_limit_bytes)\n{}\n\nbool SinglePortPromHandler::handleGet(CivetServer *server, mg_connection *conn)\n{\n  write_content_from_queues(server, conn);\n\n  return true;\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/prometheus_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/element_queue_cpp.h>\n\n#include <CivetServer.h>\n\n#include <atomic>\n#include <chrono>\n#include <mutex>\n#include <optional>\n\nnamespace reducer {\n\nclass PrometheusHandler : public CivetHandler {\npublic:\n  PrometheusHandler(std::vector<ElementQueueStoragePtr> const &queues, std::optional<u64> scrape_size_limit_bytes);\n\n  // Number of bytes of content served by this handler.\n  u64 bytes_served() const { return bytes_served_; };\n\n  // Number of times writing a response has failed.\n  u64 num_failed_scrapes() const { return num_failed_scrapes_; }\n\n  // Number of queues this handler is reading from.\n  size_t num_queues() const { return queues_.size(); }\n\nprotected:\n  using timeout_t = std::chrono::milliseconds;\n\n  // Returns the scrape timeout that is passed in the\n  // X-Prometheus-Scrape-Timeout-Seconds header.\n  std::optional<timeout_t> get_scrape_timeout(CivetServer *server, mg_connection *conn);\n\n  // Writes the content response by reading from all queues.\n  void write_content_from_queues(CivetServer *server, mg_connection *conn);\n\n  // Writes the content response by reading from the specified queue.\n  void write_content_from_queue(CivetServer *server, mg_connection *conn, size_t queue_num);\n\nprivate:\n  // Queues from which the content will be read from.\n  std::vector<ElementQueue> queues_;\n  // A mutex for each individual queue.\n  std::vector<std::mutex> queue_mutex_;\n  // This mutex needs to be locked when reading from multiple queues.\n  std::mutex handler_mutex_;\n\n  // Maximum number of bytes to return in one response.\n  std::optional<u64> scrape_size_limit_bytes_;\n\n  // Number of bytes served.\n  std::atomic<u64> bytes_served_{0};\n  // Number of times writing a response has failed.\n  std::atomic<u64> num_failed_scrapes_{0};\n\n  // Next queue to be scraped.\n  //\n  // This is used in `write_content_from_queues()`, where reading from queues\n  // can be interrupted by timeout or scrape size limit. In such case, on the\n  // next scrape request it is important to start on the next queue in line.\n  size_t next_queue_{0};\n};\n\n// Prometheus handler for scraping on a port range.\n//\n// Each scrape port is associated with a single queue.\n// The port is obtained from the `Host` HTTP header.\n//\nclass PortRangePromHandler : public PrometheusHandler {\npublic:\n  PortRangePromHandler(\n      std::vector<ElementQueueStoragePtr> const &queues,\n      std::optional<u64> scrape_size_limit_bytes,\n      std::optional<u16> extra_ports_base = std::nullopt);\n\nprivate:\n  std::optional<u16> extra_ports_base_;\n  bool handleGet(CivetServer *server, mg_connection *conn) override;\n\n  // Decides which queue this request should service, and sends an error\n  // response if there is no valid queue communicated in the request.\n  // @returns a positive queue index, on success. Negative on failure.\n  int choose_queue(CivetServer *server, mg_connection *conn);\n};\n\n// Prometheus handler for scraping on a single port.\n//\nclass SinglePortPromHandler : public PrometheusHandler {\npublic:\n  SinglePortPromHandler(std::vector<ElementQueueStoragePtr> const &queues, std::optional<u64> scrape_size_limit_bytes);\n\nprivate:\n  bool handleGet(CivetServer *server, mg_connection *conn) override;\n};\n\n} /* namespace reducer */\n"
  },
  {
    "path": "reducer/prometheus_publisher.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"prometheus_publisher.h\"\n\n#include \"prometheus_handler.h\"\n\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n\n#include <util/log.h>\n#include <util/time.h>\n\n#include <CivetServer.h>\n\n#include <stdexcept>\n#include <string>\n\nnamespace reducer {\n\nnamespace {\n\n// 1MB upper limit\nconstexpr std::streamoff UPPER_LIMIT = 1 * 1024 * 1024;\n\nstd::vector<ElementQueueStoragePtr> make_queue_buffers(size_t n)\n{\n  static constexpr u32 queue_n_elems = (1 << 19);\n  // Each element is 4 bytes\n  static constexpr u32 queue_buf_len = (queue_n_elems << 5);\n\n  assert(n > 0);\n\n  std::vector<ElementQueueStoragePtr> result;\n\n  for (size_t i = 0; i < n; ++i) {\n    result.emplace_back(std::make_shared<MemElementQueueStorage>(queue_n_elems, queue_buf_len));\n  }\n\n  return result;\n}\n\ntemplate <typename... Args>\nstd::unique_ptr<PrometheusHandler> make_handler(PrometheusPublisher::HandlerType type, Args &&...args)\n{\n  switch (type) {\n  case PrometheusPublisher::SINGLE_PORT:\n    return std::make_unique<SinglePortPromHandler>(std::forward<Args>(args)...);\n  case PrometheusPublisher::PORT_RANGE:\n    return std::make_unique<PortRangePromHandler>(std::forward<Args>(args)...);\n  default:\n    throw std::runtime_error(\"unknown value for handler type\");\n  }\n}\n\nstd::unique_ptr<CivetServer> make_http_server(std::string_view bind_addr, int num_threads)\n{\n  std::string listening_ports{bind_addr.data(), bind_addr.size()};\n\n  std::vector<std::string> options{\n      \"listening_ports\",\n      listening_ports,\n      \"num_threads\",\n      std::to_string(num_threads),\n  };\n\n  return std::make_unique<CivetServer>(options);\n}\n\n} // namespace\n\nPrometheusPublisher::PrometheusPublisher(\n    HandlerType handler_type,\n    size_t num_writer_threads,\n    std::string_view http_bind_addr,\n    int http_num_threads,\n    std::optional<u64> scrape_size_limit_bytes)\n    : queue_buffers_(make_queue_buffers(num_writer_threads)),\n      http_handler_(make_handler(handler_type, queue_buffers_, scrape_size_limit_bytes)),\n      http_server_(make_http_server(http_bind_addr, http_num_threads))\n{\n  http_server_->addHandler(\"\", *http_handler_);\n}\n\nPrometheusPublisher::~PrometheusPublisher() {}\n\nPublisher::WriterPtr PrometheusPublisher::make_writer(size_t thread_num)\n{\n  assert(thread_num < queue_buffers_.size());\n  return std::make_unique<Writer>(queue_buffers_[thread_num]);\n}\n\nu64 PrometheusPublisher::bytes_served() const\n{\n  return http_handler_->bytes_served();\n}\n\nu64 PrometheusPublisher::num_failed_scrapes() const\n{\n  return http_handler_->num_failed_scrapes();\n}\n\nvoid PrometheusPublisher::write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const\n{\n  PromStats stats;\n  stats.metrics.bytes_ingested = bytes_served();\n  stats.metrics.failed_scrapes = num_failed_scrapes();\n  encoder.write_internal_stats(stats, time_ns);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Writer\n//\n\nnamespace {\n\nssize_t write_to_queue(ElementQueue &write_queue, std::stringstream &ss)\n{\n  size_t len = ss.tellp();\n\n  // write msg to queue to be read by by prometheus handler\n  int res = eq_write(&write_queue, len);\n\n  if (res == -EINVAL) {\n    // attempted to write more than the queue size! This is never okay,\n    // and should never happen. In this catastrophic case, throw an\n    // exception so we can know about it and debug.\n    throw std::runtime_error(\"tried to write more than queue length\");\n  }\n\n  if (res == -ENOSPC) {\n    // no space in the queue\n    return -1;\n  }\n\n  assert(res >= 0);\n\n  // if we reached here, can write the element to offset\n  ss.read((char *)write_queue.data + res, len);\n\n  return len;\n}\n\n} // namespace\n\nPrometheusPublisher::Writer::Writer(ElementQueueStoragePtr const &queue) : write_queue_(queue)\n{\n  write_queue_.start_write_batch();\n}\n\nPrometheusPublisher::Writer::~Writer()\n{\n  write_queue_.finish_write_batch();\n}\n\nvoid PrometheusPublisher::Writer::write(std::stringstream &stream)\n{\n  stream.seekp(0, std::ios_base::end);\n\n  if (stream.tellp() >= UPPER_LIMIT) {\n    ++stats_.big_items_dropped;\n\n    LOG::error(\n        \"dropping stringstream since it is above the upper limit\"\n        \" - {} bytes won't be written to the TSDB\",\n        static_cast<std::streamoff>(stream.tellp()));\n    return;\n  }\n\n  if (stream.tellp() == 0) {\n    return;\n  }\n\n  auto n = write_to_queue(write_queue_, stream);\n\n  if (n < 0) {\n    bytes_failed_to_write_ += static_cast<u64>(static_cast<std::streamoff>(stream.tellp()));\n  } else {\n    bytes_written_ += n;\n  }\n}\n\nvoid PrometheusPublisher::Writer::write(std::string_view prefix, std::string_view labels, std::string_view suffix)\n{\n  u32 len = prefix.size() + labels.size() + suffix.size();\n\n  int res = eq_write(&write_queue_, len);\n\n  if (res == -EINVAL) {\n    throw std::runtime_error(\"tried to write more than queue length\");\n  } else if (res == -ENOSPC) {\n    bytes_failed_to_write_ += len;\n    return;\n  }\n  assert(res >= 0);\n\n  char *cur = write_queue_.data + res;\n\n  memcpy(cur, prefix.data(), prefix.size());\n  memcpy(cur + prefix.size(), labels.data(), labels.size());\n  memcpy(cur + prefix.size() + labels.size(), suffix.data(), suffix.size());\n\n  bytes_written_ += len;\n}\n\nvoid PrometheusPublisher::Writer::flush()\n{\n  write_queue_.finish_write_batch();\n  write_queue_.start_write_batch();\n}\n\nvoid PrometheusPublisher::Writer::write_internal_stats(\n    InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const\n{\n  BigItemsDroppedStats stats;\n  stats.labels.shard = std::to_string(shard);\n  stats.labels.module = module;\n  stats.metrics.prometheus_big_items_dropped = stats_.big_items_dropped;\n  encoder.write_internal_stats(stats, time_ns);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/prometheus_publisher.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"prometheus_handler.h\"\n#include \"publisher.h\"\n\n#include <util/element_queue_cpp.h>\n\n#include <iosfwd>\n#include <memory>\n#include <optional>\n#include <vector>\n\nclass CivetServer;\n\nnamespace reducer {\n\nclass PrometheusHandler;\n\n// Class used to publish time-series data for Prometheus to scrape.\n//\n// Prometheus is scraping using HTTP, so objects of this class instantiate\n// a HTTP server to serve requests.\n//\n// As publishing to Prometheus is usually done from multiple threads, this\n// class allows for it by keeping a queue for each publishing thread.\n//\n// To write time-series data the |make_writer| method is first used.\n// It creates an object that exposes various writing functions.\n// Each such writer can only be used from a single thread.\n//\nclass PrometheusPublisher : public Publisher {\npublic:\n  class Writer;\n\n  enum HandlerType {\n    // Handler for scraping on a single port. Multiple writers are allowed\n    // for the single specified Prometheus scrape port.\n    SINGLE_PORT,\n    // Handler for scraping on a port range.\n    // Number of writers must match number of Prometheus scrape ports.\n    PORT_RANGE,\n  };\n\n  // Constructs the object and starts the HTTP server.\n  //\n  // \\param handler_type type of handler to use (see PrometheusHandler class)\n  // \\param num_writer_threads the number of threads that will be writing\n  // \\param http_bind_addr IP address on which to listen for HTTP requests\n  // \\param http_num_threads number of HTTP server threads\n  // \\param scrape_size_limit_bytes max size of one scrape response\n  //\n  PrometheusPublisher(\n      HandlerType handler_type,\n      size_t num_writer_threads,\n      std::string_view http_bind_addr,\n      int http_num_threads = 1,\n      std::optional<u64> scrape_size_limit_bytes = std::nullopt);\n\n  virtual ~PrometheusPublisher();\n\n  // Creates a writer object for the specified thread.\n  virtual WriterPtr make_writer(size_t thread_num) override;\n\n  // Number of bytes of content scraped by prometheus.\n  u64 bytes_served() const;\n\n  // Number of times scraping by prometheus has failed.\n  u64 num_failed_scrapes() const;\n\n  // Gets this writer's stats encoded for TSDB output.\n  virtual void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const override;\n\nprivate:\n  // Queue buffer for sending to prometheus handler.\n  std::vector<ElementQueueStoragePtr> queue_buffers_;\n  // Handler for HTTP requests.\n  std::unique_ptr<PrometheusHandler> http_handler_;\n  // HTTP server for prometheus to query.\n  std::unique_ptr<CivetServer> http_server_;\n};\n\n// Writer for PrometheusPublisher.\n//\nclass PrometheusPublisher::Writer : public Publisher::Writer {\npublic:\n  Writer(ElementQueueStoragePtr const &queue);\n\n  Writer(Writer const &) = delete;\n  Writer(Writer &&) = default;\n\n  ~Writer();\n\n  // Writes provided stream's content.\n  void write(std::stringstream &ss) override;\n\n  // Writes prefix, followed by labels, finished with suffix.\n  void write(std::string_view prefix, std::string_view labels, std::string_view suffix) override;\n\n  // Finishes this and starts a new batch.\n  void flush() override;\n\n  // Number of bytes successfully written.\n  u64 bytes_written() const override { return bytes_written_; }\n\n  // Number of bytes that were not written.\n  u64 bytes_failed_to_write() const override { return bytes_failed_to_write_; }\n\n  // Gets this writer's stats encoded for TSDB output.\n  void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const override;\n\nprivate:\n  ElementQueue write_queue_;\n  u64 bytes_written_{0};\n  u64 bytes_failed_to_write_{0};\n\n  struct Stats {\n    std::size_t big_items_dropped = 0;\n  };\n\n  Stats stats_ = {};\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/publisher.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <generated/ebpf_net/aggregation/auto_handles.h>\n\n#include <chrono>\n#include <iosfwd>\n#include <iostream>\n#include <memory>\n#include <string_view>\n\nnamespace reducer {\nclass InternalMetricsEncoder;\n\n// Abstract class representing publishers that send time-series data to a\n// time-series database.\n//\nclass Publisher {\npublic:\n  class Writer {\n  public:\n    virtual ~Writer() = default;\n\n    // Derived classes need to override version(s) of write() they support.\n\n    // Writes provided stream's content.\n    virtual void write(std::stringstream &ss)\n    {\n      std::cerr << \"Publisher::Writer::write(stringstream) not supported\" << std::endl;\n      std::abort();\n    }\n\n    // Writes prefix, followed by labels, finished with suffix.\n    virtual void write(std::string_view prefix, std::string_view labels, std::string_view suffix)\n    {\n      std::cerr << \"Publisher::Writer::write(prefix, labels, suffix) not supported\" << std::endl;\n      std::abort();\n    }\n\n    // Finishes this and starts a new batch.\n    virtual void flush() = 0;\n\n    // Number of bytes successfully written.\n    virtual u64 bytes_written() const { return 0; }\n\n    // Number of bytes that were not written.\n    virtual u64 bytes_failed_to_write() const { return 0; }\n\n    // Publishes this Writer's internal metrics.  This version is called from the logging core to publish internal metrics on\n    // the logging core's Publisher::Writer that publishes internal metrics.\n    virtual void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns, int shard, std::string_view module) const {}\n\n    // Publishes this Writer's internal metrics.  This version is called from the aggregation core and sends the internal\n    // metrics to the logging core to be processed and published as appropriate.\n    virtual void write_internal_stats_to_logging_core(\n        ::ebpf_net::aggregation::auto_handles::agg_core_stats &agg_core_stats,\n        u64 time_ns,\n        int shard,\n        std::string_view module) const {};\n  };\n\n  using WriterPtr = std::unique_ptr<Writer>;\n\n  virtual ~Publisher() = default;\n\n  // Creates a writer object for the specified thread.\n  virtual WriterPtr make_writer(size_t thread_num) = 0;\n\n  // Gets this publisher's stats encoded for TSDB output.\n  virtual void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns) const {}\n\n  // Performs whatever shutdown actions are needed, e.g. closing sockets,\n  // stopping threads, etc.\n  virtual void shutdown() {}\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/reducer.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <config.h>\n\n#include <reducer/constants.h>\n#include <reducer/core_type.h>\n#include <reducer/disabled_metrics.h>\n#include <reducer/ingest/agent_span.h>\n#include <reducer/ingest/component.h>\n#include <reducer/matching/component.h>\n#include <reducer/null_publisher.h>\n#include <reducer/otlp_grpc_formatter.h>\n#include <reducer/otlp_grpc_publisher.h>\n#include <reducer/prometheus_publisher.h>\n#include <reducer/reducer.h>\n#include <reducer/reducer_config.h>\n#include <reducer/util/index_dumper.h>\n\n#include <channel/component.h>\n#include <common/client_type.h>\n\n#include <geoip/geoip.h>\n\n#include <util/boot_time.h>\n#include <util/debug.h>\n#include <util/environment_variables.h>\n#include <util/error_handling.h>\n#include <util/file_ops.h>\n#include <util/log.h>\n#include <util/uv_helpers.h>\n\n#include <spdlog/fmt/chrono.h>\n#include <spdlog/spdlog.h>\n\n#include <ctime>\n#include <filesystem>\n#include <iomanip>\n#include <sstream>\n#include <thread>\n#include <vector>\n\nnamespace reducer {\n\nReducer::Reducer(uv_loop_t &loop, ReducerConfig &config)\n    : loop_(loop),\n      config_(config),\n      ingest_to_matching_queues_(config_.num_ingest_shards, config_.num_matching_shards),\n      ingest_to_logging_queues_(config_.num_ingest_shards, 1),\n      matching_to_logging_queues_(config_.num_matching_shards, 1),\n      matching_to_aggregation_queues_(config_.num_matching_shards, config_.num_aggregation_shards),\n      aggregation_to_logging_queues_(config_.num_aggregation_shards, 1)\n{}\n\nvoid Reducer::startup()\n{\n  LOG::info(\"Starting OpenTelemetry eBPF Reducer version {} ({})\", versions::release, release_mode_string);\n\n  init_config();\n  init_cores();\n  start_threads();\n\n  uv_run(&loop_, UV_RUN_DEFAULT);\n  close_uv_loop_cleanly(&loop_);\n\n  for (auto &thread : threads_) {\n    thread.join();\n  }\n\n  LOG::info(\"OpenTelemetry eBPF Reducer exiting\");\n}\n\nvoid Reducer::shutdown()\n{\n  LOG::info(\"Stopping ingest core threads...\");\n  ingest_core_->stop_async();\n\n  for (std::size_t i = 0; i < matching_cores_.size(); ++i) {\n    LOG::info(\"Stopping matching core thread {}...\", i);\n    matching_cores_[i]->stop_async();\n  }\n\n  for (std::size_t i = 0; i < agg_cores_.size(); ++i) {\n    LOG::info(\"Stopping aggregation core thread {}...\", i);\n    agg_cores_[i]->stop_async();\n  }\n\n  LOG::info(\"Stopping logging core thread...\");\n  logging_core_->stop_async();\n\n  LOG::info(\"Stopping main loop...\");\n  uv_stop(&loop_);\n}\n\nvoid Reducer::init_config()\n{\n  reducer::aggregation::AggCore::set_id_id_enabled(config_.enable_id_id);\n  reducer::aggregation::AggCore::set_az_id_enabled(config_.enable_az_id);\n  reducer::aggregation::AggCore::set_flow_logs_enabled(config_.enable_flow_logs);\n\n  reducer::logging::LoggingCore::set_otlp_formatted_internal_metrics_enabled(config_.enable_otlp_grpc_metrics);\n  reducer::OtlpGrpcFormatter::set_metric_description_field_enabled(config_.enable_otlp_grpc_metric_descriptions);\n\n  reducer::aggregation::AggCore::set_node_ip_field_disabled(config_.disable_node_ip_field);\n  reducer::matching::MatchingCore::set_autonomous_system_ip_enabled(config_.enable_autonomous_system_ip);\n\n  // If the path to a GeoIP database is defined, try loading the database here\n  // and print an error message if it fails.\n  // Unfortunately, the database structure is not thread safe and is not\n  // cloneable, so each matching shard will have to load it again for itself.\n  //\n  if (config_.geoip_path) {\n    try {\n      geoip::database geoip_db(config_.geoip_path->c_str());\n      LOG::info(\"Loaded GeoIP database from '{}'.\", *config_.geoip_path);\n    } catch (std::exception &exc) {\n      LOG::error(\"Failed to load GeoIP database from '{}': {}.\", *config_.geoip_path, exc.what());\n      config_.geoip_path.reset(); // so matching shards don't try loading it for naught\n    }\n  }\n\n  if (config_.index_dump_interval) {\n    // Index dumping is turned on.\n    auto dump_interval = std::chrono::seconds{config_.index_dump_interval};\n\n    std::filesystem::path dump_dir;\n    if (auto data_dir = try_get_env_var(DATA_DIR_VAR); !data_dir.empty()) {\n      dump_dir = std::filesystem::path(data_dir) / \"dump\";\n    } else {\n      dump_dir = std::filesystem::current_path() / \"dump\";\n    }\n\n    LOG::info(\"Dumping indexes to {} at {} interval\", dump_dir, dump_interval);\n\n    if (!std::filesystem::exists(dump_dir)) {\n      std::error_code ec;\n      if (!std::filesystem::create_directories(dump_dir, ec)) {\n        LOG::critical(\"Could not create directory {}: {}\", dump_dir, ec);\n        exit(1);\n      }\n    } else if (!std::filesystem::is_directory(dump_dir)) {\n      LOG::critical(\"{} exists but is not a directory!\", dump_dir);\n      exit(1);\n    }\n\n    IndexDumper::set_dump_dir(dump_dir.native());\n    IndexDumper::set_cooldown(dump_interval);\n  } else {\n    IndexDumper::set_dump_dir(\"\");\n    IndexDumper::set_cooldown(0s);\n  }\n}\n\nvoid Reducer::init_cores()\n{\n  const size_t num_stat_writers = 1; // one for the logging core\n\n  if (config_.enable_otlp_grpc_metrics) {\n    stats_publisher_ = std::make_unique<reducer::OtlpGrpcPublisher>(\n        num_stat_writers,\n        std::string(config_.otlp_grpc_metrics_address + \":\" + std::to_string(config_.otlp_grpc_metrics_port)));\n  } else {\n    stats_publisher_ = std::make_unique<reducer::PrometheusPublisher>(\n        reducer::PrometheusPublisher::SINGLE_PORT,\n        num_stat_writers,\n        config_.internal_prom_bind,\n        1,\n        config_.stats_scrape_size_limit_bytes);\n  }\n\n  // index of the next stat writer thread to make a writer for\n  size_t stat_writer_num = 0;\n\n  auto initial_timestamp = monotonic() + get_boot_time();\n\n  reducer::DisabledMetrics disabled_metrics(config_.disable_metrics, config_.enable_metrics);\n  logging_core_ = std::make_unique<reducer::logging::LoggingCore>(\n      ingest_to_logging_queues_,\n      matching_to_logging_queues_,\n      aggregation_to_logging_queues_,\n      stats_publisher_->make_writer(stat_writer_num++),\n      disabled_metrics,\n      /* shard */ 0,\n      initial_timestamp);\n\n  // matching, logging and aggregation cores are receiving messages only from\n  // other in-process core(s), so authentication is not needed\n  logging_core_->set_connection_authenticated();\n\n  agg_cores_.reserve(config_.num_aggregation_shards);\n  for (size_t shard = 0; shard < config_.num_aggregation_shards; ++shard) {\n    std::string otlp_endpoint;\n    if (config_.enable_otlp_grpc_metrics) {\n      otlp_endpoint = std::string(config_.otlp_grpc_metrics_address + \":\" + std::to_string(config_.otlp_grpc_metrics_port));\n    }\n\n    auto agg_core = std::make_unique<reducer::aggregation::AggCore>(\n        matching_to_aggregation_queues_,\n        aggregation_to_logging_queues_,\n        shard,\n        initial_timestamp,\n        otlp_endpoint,\n        config_.disable_node_ip_field);\n\n    agg_core->set_connection_authenticated();\n    agg_cores_.push_back(std::move(agg_core));\n  }\n\n  reducer::matching::MatchingCore::enable_aws_enrichment(config_.enable_aws_enrichment);\n\n  matching_cores_.reserve(config_.num_matching_shards);\n  for (size_t shard = 0; shard < config_.num_matching_shards; ++shard) {\n    auto matching_core = std::make_unique<reducer::matching::MatchingCore>(\n        ingest_to_matching_queues_,\n        matching_to_aggregation_queues_,\n        matching_to_logging_queues_,\n        config_.geoip_path,\n        shard,\n        initial_timestamp);\n    matching_core->set_connection_authenticated();\n    matching_cores_.push_back(std::move(matching_core));\n  }\n\n  ingest_core_ = std::make_unique<reducer::ingest::IngestCore>(\n      ingest_to_logging_queues_, ingest_to_matching_queues_, config_.telemetry_port);\n\n  // all writers created\n  ASSUME(stat_writer_num == num_stat_writers);\n}\n\nvoid Reducer::start_threads()\n{\n  threads_.reserve(config_.num_matching_shards + config_.num_aggregation_shards + 3);\n\n  // start all threads\n  threads_.emplace_back(&reducer::logging::LoggingCore::run, logging_core_.get());\n  for (auto &agg_core : agg_cores_) {\n    threads_.emplace_back(&reducer::aggregation::AggCore::run, agg_core.get());\n  }\n  for (auto &matching_core : matching_cores_) {\n    threads_.emplace_back(&reducer::matching::MatchingCore::run, matching_core.get());\n  }\n  threads_.emplace_back(&reducer::ingest::IngestCore::run, ingest_core_.get());\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/reducer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <reducer/aggregation/agg_core.h>\n#include <reducer/ingest/ingest_core.h>\n#include <reducer/logging/logging_core.h>\n#include <reducer/matching/matching_core.h>\n#include <reducer/publisher.h>\n#include <reducer/reducer_config.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <thread>\n\nnamespace reducer {\n\nclass Reducer {\npublic:\n  Reducer(uv_loop_t &loop, ReducerConfig &config);\n\n  void startup();\n  void shutdown();\n\nprivate:\n  void init_config();\n  void init_cores();\n  void start_threads();\n\n  uv_loop_t &loop_;\n  ReducerConfig &config_;\n\n  std::unique_ptr<reducer::Publisher> stats_publisher_;\n\n  reducer::RpcQueueMatrix ingest_to_matching_queues_;\n  reducer::RpcQueueMatrix ingest_to_logging_queues_;\n  reducer::RpcQueueMatrix matching_to_logging_queues_;\n  reducer::RpcQueueMatrix matching_to_aggregation_queues_;\n  reducer::RpcQueueMatrix aggregation_to_logging_queues_;\n\n  std::unique_ptr<reducer::logging::LoggingCore> logging_core_;\n  std::vector<std::unique_ptr<reducer::aggregation::AggCore>> agg_cores_;\n  std::vector<std::unique_ptr<reducer::matching::MatchingCore>> matching_cores_;\n  std::unique_ptr<reducer::ingest::IngestCore> ingest_core_;\n\n  std::vector<std::thread> threads_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/reducer_config.cc",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n#include \"reducer_config.h\"\n\nnamespace reducer {\n\n// Intentionally left minimal; defaults and parsing are handled in Rust.\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/reducer_config.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <reducer/tsdb_format.h>\n\n#include <optional>\n#include <string>\n\nnamespace reducer {\n\n// Configuration options for running Reducer.\n//\nstruct ReducerConfig {\n  u32 telemetry_port = 0;\n\n  u32 num_ingest_shards = 0;\n  u32 num_matching_shards = 0;\n  u32 num_aggregation_shards = 0;\n  u32 partitions_per_shard = 0;\n\n  bool enable_id_id = false;\n  bool enable_az_id = false;\n  bool enable_flow_logs = false;\n\n  bool enable_otlp_grpc_metrics = false;\n  std::string otlp_grpc_metrics_address;\n  u32 otlp_grpc_metrics_port = 0;\n  int otlp_grpc_batch_size = 0;\n  bool enable_otlp_grpc_metric_descriptions = false;\n\n  bool disable_prometheus_metrics = false;\n  bool shard_prometheus_metrics = false;\n  std::string prom_bind;\n  std::optional<u64> scrape_size_limit_bytes;\n  std::string internal_prom_bind;\n  std::optional<u64> stats_scrape_size_limit_bytes;\n  reducer::TsdbFormat scrape_metrics_tsdb_format;\n\n  bool disable_node_ip_field = false;\n  bool enable_autonomous_system_ip = false;\n\n  std::optional<std::string> geoip_path;\n\n  bool enable_aws_enrichment = false;\n  bool enable_percentile_latencies = false;\n\n  std::string disable_metrics;\n  std::string enable_metrics;\n\n  u64 index_dump_interval = 0;\n};\n\n// No defaults defined here; defaults live in Rust layer.\n\n} // namespace reducer\n\n#include \"reducer_config.inl\"\n"
  },
  {
    "path": "reducer/reducer_config.inl",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <utility>\n\nnamespace reducer {\n\ntemplate <typename Out> Out &&operator<<(Out &&out, ReducerConfig const &config)\n{\n  out << \"telemetry_port: \" << config.telemetry_port << \"\\n\"\n      << \"num_ingest_shards: \" << config.num_ingest_shards << \"\\n\"\n      << \"num_matching_shards: \" << config.num_matching_shards << \"\\n\"\n      << \"num_aggregation_shards: \" << config.num_aggregation_shards << \"\\n\"\n      << \"partitions_per_shard: \" << config.partitions_per_shard << \"\\n\"\n      << \"enable_id_id: \" << config.enable_id_id << \"\\n\"\n      << \"enable_az_id: \" << config.enable_az_id << \"\\n\"\n      << \"enable_flow_logs: \" << config.enable_flow_logs << \"\\n\"\n      << \"enable_otlp_grpc_metrics: \" << config.enable_otlp_grpc_metrics << \"\\n\"\n      << \"otlp_grpc_metrics_address: \" << config.otlp_grpc_metrics_address << \"\\n\"\n      << \"otlp_grpc_metrics_port: \" << config.otlp_grpc_metrics_port << \"\\n\"\n      << \"otlp_grpc_batch_size: \" << config.otlp_grpc_batch_size << \"\\n\"\n      << \"enable_otlp_grpc_metric_descriptions: \" << config.enable_otlp_grpc_metric_descriptions << \"\\n\"\n      << \"disable_prometheus_metrics: \" << config.disable_prometheus_metrics << \"\\n\"\n      << \"shard_prometheus_metrics: \" << config.shard_prometheus_metrics << \"\\n\"\n      << \"prom_bind: \" << config.prom_bind << \"\\n\"\n      << \"internal_prom_bind: \" << config.internal_prom_bind << \"\\n\";\n\n  if (config.scrape_size_limit_bytes) {\n    out << \"scrape_size_limit_bytes: \" << *config.scrape_size_limit_bytes << \"\\n\";\n  } else {\n    out << \"scrape_size_limit_bytes: unlimited\\n\";\n  }\n\n  if (config.stats_scrape_size_limit_bytes) {\n    out << \"stats_scrape_size_limit_bytes: \" << *config.stats_scrape_size_limit_bytes << \"\\n\";\n  } else {\n    out << \"stats_scrape_size_limit_bytes: unlimited\\n\";\n  }\n\n  out << \"scrape_metrics_tsdb_format: \" << to_string(config.scrape_metrics_tsdb_format) << \"\\n\"\n      << \"disable_node_ip_field: \" << config.disable_node_ip_field << \"\\n\"\n      << \"enable_autonomous_system_ip: \" << config.enable_autonomous_system_ip << \"\\n\"\n      << \"geoip_path: \" << (config.geoip_path ? *config.geoip_path : \"none\") << \"\\n\"\n      << \"enable_aws_enrichment: \" << config.enable_aws_enrichment << \"\\n\"\n      << \"enable_percentile_latencies: \" << config.enable_percentile_latencies << \"\\n\"\n      << \"disable_metrics: \" << config.disable_metrics << \"\\n\"\n      << \"enable_metrics: \" << config.enable_metrics << \"\\n\"\n      << \"index_dump_interval: \" << config.index_dump_interval << \"\\n\";\n\n  return std::forward<Out>(out);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/rpc_queue_matrix.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/element_queue_cpp.h>\n#include <util/element_queue_writer.h>\n\n#include <cassert>\n#include <memory>\n#include <vector>\n\nnamespace reducer {\n\n// This class is used to help with creation of queues that are used for\n// message-passing between to apps.\n//\nclass RpcQueueMatrix {\npublic:\n  // Default number of elements in a queue shared buffer.\n  static constexpr u32 default_queue_n_elems = (1 << 18);\n  // Default number of bytes in a queue shared buffer.\n  static constexpr u32 default_queue_buf_len = (1 << 23);\n\n  // Constructs the object for |num_senders| senders and |num_receivers|\n  // receivers.\n  RpcQueueMatrix(\n      size_t num_senders,\n      size_t num_receivers,\n      u32 queue_n_elems = default_queue_n_elems,\n      u32 queue_buf_len = default_queue_buf_len)\n      : num_senders_(num_senders), num_receivers_(num_receivers)\n  {\n    size_t const num_entries = num_receivers * num_senders;\n\n    entries_.reserve(num_entries);\n\n    for (size_t i = 0; i < num_entries; ++i) {\n      entries_.emplace_back(make_storage(queue_n_elems, queue_buf_len));\n    }\n  }\n\n  // Creates a vector of element queue objects for the specified receiver.\n  //\n  // This vector is normally used in construction of receiver core instance.\n  //\n  template <typename Reader = ElementQueue, typename... Args> std::vector<Reader> make_readers(size_t receiver, Args &&...args)\n  {\n    assert(receiver < num_receivers_);\n\n    std::vector<Reader> readers;\n\n    size_t const offset = receiver * num_senders_;\n    size_t const stride = 1;            // we're using receiver-major ordering\n    size_t const length = num_senders_; // a reader for each sender\n\n    readers.reserve(length);\n\n    for (size_t i = 0; i < length; ++i) {\n      size_t const pos = offset + (i * stride);\n      readers.emplace_back(entries_[pos].storage, std::forward<Args>(args)...);\n    }\n\n    return readers;\n  }\n\n  // Creates a vector of writer objects for the specified sender.\n  //\n  // This vector is normally used in construction of sender core instances,\n  // where it is passed to the constuctor of the app's Index class.\n  //\n  template <typename Writer, typename... Args> std::vector<Writer> make_writers(size_t sender, Args &&...args)\n  {\n    assert(sender < num_senders_);\n\n    std::vector<Writer> writers;\n\n    size_t const offset = sender;\n    size_t const stride = num_senders_;   // we're using receiver-major ordering\n    size_t const length = num_receivers_; // a writer to each receiver\n\n    writers.reserve(length);\n\n    for (size_t i = 0; i < length; ++i) {\n      size_t const pos = offset + (i * stride);\n      writers.emplace_back(entries_[pos].queue_writer, std::forward<Args>(args)...);\n    }\n\n    return writers;\n  }\n\n  // Returns the number of senders this matrix is constructed for.\n  size_t num_senders() const { return num_senders_; }\n  // Returns the number of receivers this matrix is constructed for.\n  size_t num_receivers() const { return num_receivers_; }\n\nprivate:\n  // Holds everything needed for messaging between one sender and one receiver.\n  struct Entry {\n    ElementQueueStoragePtr storage;\n    ElementQueue writer_queue;\n    ElementQueueWriter queue_writer;\n\n    Entry(ElementQueueStoragePtr s) : storage(s), writer_queue(storage), queue_writer(writer_queue) {}\n  };\n\n  size_t num_senders_;\n  size_t num_receivers_;\n\n  std::vector<Entry> entries_;\n\n  static ElementQueueStoragePtr make_storage(u32 num_elems, u32 buf_len)\n  {\n    return std::make_shared<MemElementQueueStorage>(num_elems, buf_len);\n  }\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/rpc_queue_matrix_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/rpc_queue_matrix.h>\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\n#include <string>\n\nnamespace reducer {\nnamespace {\n\nusing ::testing::NotNull;\n\nclass Writer {\npublic:\n  Writer(IBufferedWriter &buffered_writer) : buffered_writer_(buffered_writer) {}\n\n  std::error_code write(char const *buf, size_t len)\n  {\n    auto buffer = buffered_writer_.start_write(len);\n    if (!buffer) {\n      return buffer.error();\n    }\n\n    EXPECT_THAT(*buffer, NotNull());\n    memcpy(*buffer, buf, len);\n    buffered_writer_.finish_write();\n    return {};\n  }\n\nprivate:\n  IBufferedWriter &buffered_writer_;\n};\n\nstd::string make_msg(int x, int y)\n{\n  return std::to_string(x) + \":\" + std::to_string(y);\n}\n\nTEST(RpcQueueMatrixTest, TestConstruction)\n{\n  size_t const num_senders = 3;\n  size_t const num_receivers = 2;\n\n  RpcQueueMatrix queues(num_senders, num_receivers);\n\n  for (size_t r = 0; r < num_receivers; ++r) {\n    std::vector<ElementQueue> readers = queues.make_readers(r);\n\n    EXPECT_EQ(readers.size(), num_senders);\n\n    for (auto &reader : readers) {\n      reader.start_read_batch();\n\n      EXPECT_EQ(reader.peek(), -ENOENT);\n\n      reader.finish_read_batch();\n    }\n  }\n}\n\nTEST(RpcQueueMatrixTest, TestMessaging)\n{\n  size_t const num_senders = 3;\n  size_t const num_receivers = 2;\n\n  RpcQueueMatrix queues(num_senders, num_receivers);\n\n  for (size_t s = 0; s < num_senders; ++s) {\n    std::vector<Writer> writers = queues.make_writers<Writer>(s);\n\n    EXPECT_EQ(writers.size(), num_receivers);\n\n    for (size_t r = 0; r < num_receivers; ++r) {\n      Writer &writer = writers[r];\n      std::string msg = make_msg(s, r);\n      writer.write(msg.c_str(), msg.size());\n    }\n  }\n\n  for (size_t r = 0; r < num_receivers; ++r) {\n    std::vector<ElementQueue> readers = queues.make_readers(r);\n\n    EXPECT_EQ(readers.size(), num_senders);\n\n    for (size_t s = 0; s < num_senders; ++s) {\n      ElementQueue &reader = readers[s];\n      reader.start_read_batch();\n\n      EXPECT_TRUE(reader.peek() > 0);\n\n      char *buf = nullptr;\n      int len = reader.read(buf);\n\n      EXPECT_THAT(buf, NotNull());\n\n      std::string sent = make_msg(s, r);\n      std::string received = std::string(buf, len);\n\n      EXPECT_EQ(received, sent);\n\n      reader.finish_read_batch();\n    }\n  }\n}\n\n} // namespace\n} // namespace reducer\n"
  },
  {
    "path": "reducer/rpc_stats.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"rpc_stats.h\"\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <util/element_queue_cpp.h>\n#include <util/log_modifiers.h>\n\nnamespace reducer {\n\nRpcSenderStats::RpcSenderStats(\n    int sender_shard, std::string_view sender_app, std::string_view receiver_app, RpcQueueMatrix &queues)\n    : sender_shard_(sender_shard),\n      sender_app_(sender_app),\n      receiver_app_(receiver_app),\n      writers_(queues.make_writers<QueueWriterRef>(sender_shard))\n{}\n\nvoid RpcSenderStats::check_utilization()\n{\n  for (auto &writer_ref : writers_) {\n    ElementQueue const &queue = writer_ref.get().queue();\n\n    u32 buf_used = queue.buf_used();\n    u32 elem_count = queue.elem_count();\n\n    double buf_util = buf_used / (double)queue.buf_capacity();\n    double elem_util = elem_count / (double)queue.elem_capacity();\n\n    max_buf_used_ = std::max(max_buf_used_, buf_used);\n    max_elem_count_ = std::max(max_elem_count_, elem_count);\n\n    max_buf_util_ = std::max(max_buf_util_, buf_util);\n    max_elem_util_ = std::max(max_elem_util_, elem_util);\n  }\n}\n\nvoid RpcSenderStats::write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns)\n{\n  u64 num_write_stalls{0};\n\n  for (auto &writer_ref : writers_) {\n    num_write_stalls += writer_ref.get().num_write_stalls();\n  }\n\n  if (num_write_stalls > last_num_write_stalls_) {\n    u64 count = num_write_stalls - last_num_write_stalls_;\n\n    RpcWriteStallsStats stats;\n    stats.labels.module = sender_app_;\n    stats.labels.shard = std::to_string(sender_shard_);\n    stats.labels.peer = receiver_app_;\n    stats.metrics.stalls = count;\n    encoder.write_internal_stats(stats, time_ns);\n\n    last_num_write_stalls_ = num_write_stalls;\n  }\n\n  RpcQueueUtilizationStats stats;\n  stats.labels.module = sender_app_;\n  stats.labels.shard = std::to_string(sender_shard_);\n  stats.labels.peer = receiver_app_;\n\n  stats.metrics.max_buf_used = max_buf_used_;\n  stats.metrics.max_buf_util = max_buf_util_;\n  stats.metrics.max_elem_util = max_elem_util_;\n\n  encoder.write_internal_stats(stats, time_ns);\n\n  max_buf_used_ = 0;\n  max_elem_count_ = 0;\n\n  max_buf_util_ = 0.0;\n  max_elem_util_ = 0.0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nRpcReceiverStats::RpcReceiverStats(int receiver_shard, std::string_view sender_app, std::string_view receiver_app)\n    : receiver_shard_(receiver_shard), sender_app_(sender_app), receiver_app_(receiver_app)\n{}\n\nvoid RpcReceiverStats::record_latency(std::chrono::nanoseconds time_diff)\n{\n  if (time_diff.count() <= 0) {\n    return;\n  }\n\n  max_latency_ns_ = std::max(max_latency_ns_, (u64)time_diff.count());\n}\n\nvoid RpcReceiverStats::write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns)\n{\n  RpcLatencyStats stats;\n  stats.labels.module = receiver_app_;\n  stats.labels.shard = std::to_string(receiver_shard_);\n  stats.labels.peer = sender_app_;\n  stats.metrics.max_latency_ns = max_latency_ns_;\n  encoder.write_internal_stats(stats, time_ns);\n\n  max_latency_ns_ = 0;\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/rpc_stats.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <generated/ebpf_net/matching/auto_handles.h>\n\n#include <chrono>\n#include <functional>\n#include <sstream>\n#include <string>\n#include <vector>\n\nclass ElementQueueWriter;\n\nnamespace reducer {\n\nclass RpcQueueMatrix;\nclass InternalMetricsEncoder;\n\n// Collects and writes out stats on RPC queues from sender's standpoint.\n//\n// Each core that sends RPC messages will have one object of this class\n// per receiver application.\n//\nclass RpcSenderStats {\npublic:\n  // Constructs the object.\n  // sender_shard -- index of the core this object is used in\n  // sender_app -- name of the application this core is running\n  // receiver_app -- name of the application the receiver core is running\n  //\n  RpcSenderStats(int sender_shard, std::string_view sender_app, std::string_view receiver_app, RpcQueueMatrix &queues);\n\n  // Checks utilization of all queues this sender is writing to.\n  void check_utilization();\n\n  // Writes stats out.\n  void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns);\n\n  //// Writes internal stats to Logging core from different cores.\n  template <typename CoreStatsHandle>\n  void write_internal_metrics_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns);\n\nprivate:\n  int sender_shard_;\n  std::string sender_app_;\n  std::string receiver_app_;\n\n  // Access to writes this sender is using.\n  using QueueWriterRef = std::reference_wrapper<ElementQueueWriter>;\n  std::vector<QueueWriterRef> writers_;\n\n  // Last umber of write stalls.\n  u64 last_num_write_stalls_{0};\n\n  // Maximum usage of queue buffer, in bytes.\n  u32 max_buf_used_{0};\n  // Maximum usage of queue elements, in number of elements\n  u32 max_elem_count_{0};\n\n  // Maximum ratio of queue buffer usage and capacity.\n  double max_buf_util_{0};\n  // Maximum ratio of queue elements usage and capacity.\n  double max_elem_util_{0};\n};\n\n// Collects and writes out stats on RPC queues from receiver's standpoint.\n//\n// Each core that receives RPC messages will have one object of this class\n// per sender application.\n//\n\nclass RpcReceiverStats {\npublic:\n  // Constructs the object.\n  // receiver_shard -- index of the core this object is used in\n  // sender_app -- name of the application that the sender core is running\n  // receiver_app -- name of the application this core is running\n  //\n  RpcReceiverStats(int receiver_shard, std::string_view sender_app, std::string_view receiver_app);\n\n  // Records the supplied time differential (latency).\n  // This is the difference between the time when a message is send and the time\n  // when it was extracted from the queue.\n  void record_latency(std::chrono::nanoseconds time_diff);\n\n  // Writes stats out.\n  void write_internal_stats(InternalMetricsEncoder &encoder, u64 time_ns);\n\n  // Writes stats out to Logging core.\n  template <typename CoreStatsHandle>\n  void write_internal_metrics_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns);\n\nprivate:\n  int receiver_shard_;\n  std::string sender_app_;\n  std::string receiver_app_;\n\n  u64 max_latency_ns_{0};\n};\n\n} // namespace reducer\n\n#include \"rpc_stats.inl\"\n"
  },
  {
    "path": "reducer/rpc_stats.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include <reducer/constants.h>\n#include <reducer/internal_metrics_encoder.h>\n#include <reducer/internal_stats.h>\n#include <reducer/rpc_queue_matrix.h>\n\n#include <util/element_queue_cpp.h>\n#include <util/log_modifiers.h>\n\nnamespace reducer {\n\n//// Writes internal stats to Logging core from different cores.\ntemplate <typename CoreStatsHandle>\nvoid RpcSenderStats::write_internal_metrics_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns)\n{\n  u64 num_write_stalls{0};\n\n  for (auto &writer_ref : writers_) {\n    num_write_stalls += writer_ref.get().num_write_stalls();\n  }\n\n  if (num_write_stalls > last_num_write_stalls_) {\n    u64 count = num_write_stalls - last_num_write_stalls_;\n\n    RpcWriteStallsStats stats;\n    stats.labels.module = sender_app_;\n    stats.labels.shard = std::to_string(sender_shard_);\n    stats.labels.peer = receiver_app_;\n    stats.metrics.stalls = count;\n    internal_metrics.rpc_write_stalls_stats(jb_blob(sender_app_), sender_shard_, jb_blob(receiver_app_), count, time_ns);\n\n    last_num_write_stalls_ = num_write_stalls;\n  }\n\n  RpcQueueUtilizationStats stats;\n  stats.labels.module = sender_app_;\n  stats.labels.shard = std::to_string(sender_shard_);\n  stats.labels.peer = receiver_app_;\n\n  stats.metrics.max_buf_used = max_buf_used_;\n  stats.metrics.max_buf_util = max_buf_util_;\n  stats.metrics.max_elem_util = max_elem_util_;\n\n  internal_metrics.rpc_write_utilization_stats(\n      jb_blob(sender_app_), sender_shard_, jb_blob(receiver_app_), max_buf_used_, max_buf_util_, max_elem_util_, time_ns);\n\n  max_buf_used_ = 0;\n  max_elem_count_ = 0;\n\n  max_buf_util_ = 0.0;\n  max_elem_util_ = 0.0;\n}\n\n//// Writes internal stats to Logging core from different cores.\ntemplate <typename CoreStatsHandle>\nvoid RpcReceiverStats::write_internal_metrics_to_logging_core(CoreStatsHandle &internal_metrics, u64 time_ns)\n{\n  RpcLatencyStats stats;\n  stats.labels.module = receiver_app_;\n  stats.labels.shard = std::to_string(receiver_shard_);\n  stats.labels.peer = sender_app_;\n  stats.metrics.max_latency_ns = max_latency_ns_;\n  internal_metrics.rpc_receive_stats(jb_blob(receiver_app_), receiver_shard_, jb_blob(sender_app_), max_latency_ns_, time_ns);\n\n  max_latency_ns_ = 0;\n}\n\n} // namespace reducer"
  },
  {
    "path": "reducer/stat_info.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"stat_info.h\"\n\nnamespace {\n\nstatic constexpr std::string_view UNIT_BYTES = \"By\";\nstatic constexpr std::string_view UNIT_MICROSECONDS = \"us\";\nstatic constexpr std::string_view UNIT_DIMENSIONLESS = \"1\";\n\n} // namespace\n\nnamespace reducer {\n\n////////////////////////////////////////////////////////////////////////////////\n// EBPF_NET\n//\n\nEbpfNetMetricInfo EbpfNetMetricInfo::prometheus_bytes_discarded{\n    EbpfNetMetrics::prometheus_bytes_discarded,\n    \"some description\"\n    \"some description\",\n    UNIT_MICROSECONDS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::codetiming_min_ns{\n    EbpfNetMetrics::codetiming_min_ns,\n    \"some description \"\n    \"some description \",\n    UNIT_MICROSECONDS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::entrypoint_info{\n    EbpfNetMetrics::entrypoint_info,\n    \"description\"\n    \"description \",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_requests_failed{\n    EbpfNetMetrics::otlp_grpc_requests_failed,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_requests_sent{\n    EbpfNetMetrics::otlp_grpc_requests_sent,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::connections{\n    EbpfNetMetrics::connections,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::rpc_queue_elem_utilization_fraction{\n    EbpfNetMetrics::rpc_queue_elem_utilization_fraction,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::disconnects{\n    EbpfNetMetrics::codetiming_avg_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::codetiming_avg_ns{\n    EbpfNetMetrics::codetiming_avg_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::client_handle_pool{\n    EbpfNetMetrics::client_handle_pool,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::span_utilization{\n    EbpfNetMetrics::span_utilization,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::up{\n    EbpfNetMetrics::up,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::rpc_queue_buf_utilization_fraction{\n    EbpfNetMetrics::rpc_queue_buf_utilization_fraction,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::collector_log_count{\n    EbpfNetMetrics::collector_log_count,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::time_since_last_message_ns{\n    EbpfNetMetrics::time_since_last_message_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::bpf_log{\n    EbpfNetMetrics::bpf_log,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::codetiming_count{\n    EbpfNetMetrics::codetiming_count,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::message{\n    EbpfNetMetrics::message,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_bytes_failed{\n    EbpfNetMetrics::otlp_grpc_bytes_failed,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_bytes_sent{\n    EbpfNetMetrics::otlp_grpc_bytes_sent,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::pipeline_message_error{\n    EbpfNetMetrics::pipeline_message_error,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::prometheus_bytes_written{\n    EbpfNetMetrics::prometheus_bytes_written,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::codetiming_max_ns{\n    EbpfNetMetrics::codetiming_max_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::span_utilization_max{\n    EbpfNetMetrics::span_utilization_max,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::client_handle_pool_fraction{\n    EbpfNetMetrics::client_handle_pool_fraction,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::span_utilization_fraction{\n    EbpfNetMetrics::span_utilization_fraction,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::rpc_latency_ns{\n    EbpfNetMetrics::rpc_latency_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::agg_root_truncation{\n    EbpfNetMetrics::agg_root_truncation,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::clock_offset_ns{\n    EbpfNetMetrics::clock_offset_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_metrics_failed{\n    EbpfNetMetrics::otlp_grpc_metrics_failed,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_metrics_sent{\n    EbpfNetMetrics::otlp_grpc_metrics_sent,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::otlp_grpc_unknown_response_tags{\n    EbpfNetMetrics::otlp_grpc_unknown_response_tags,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::collector_health{\n    EbpfNetMetrics::collector_health,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::codetiming_sum_ns{\n    EbpfNetMetrics::codetiming_sum_ns,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::rpc_queue_buf_utilization{\n    EbpfNetMetrics::rpc_queue_buf_utilization,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::pipeline_agent_connections{\n    EbpfNetMetrics::pipeline_agent_connections,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::prometheus_bytes_ingested{\n    EbpfNetMetrics::prometheus_bytes_ingested,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::prometheus_failed_scrapes{\n    EbpfNetMetrics::prometheus_failed_scrapes,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::prometheus_big_items_dropped{\n    EbpfNetMetrics::prometheus_big_items_dropped,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n\nEbpfNetMetricInfo EbpfNetMetricInfo::rpc_write_stalls{\n    EbpfNetMetrics::rpc_write_stalls,\n    \" some definitions\"\n    \" some description.\",\n    UNIT_DIMENSIONLESS};\n} // namespace reducer\n"
  },
  {
    "path": "reducer/stat_info.h",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#pragma once\n\n#include \"metric_info.h\"\n#include \"outbound_stats.h\"\n\n#include <string>\n#include <string_view>\n\nnamespace reducer {\n\n// Information on stats (internal metrics) with EBPF_NET prefixed outbound metrics.\n//\nstruct EbpfNetMetricInfo : public OutboundMetricInfo<EbpfNetMetrics> {\n  using OutboundMetricInfo<EbpfNetMetrics>::OutboundMetricInfo;\n\n  static EbpfNetMetricInfo agg_root_truncation;\n  static EbpfNetMetricInfo bpf_log;\n  static EbpfNetMetricInfo client_handle_pool;\n  static EbpfNetMetricInfo client_handle_pool_fraction;\n  static EbpfNetMetricInfo clock_offset_ns;\n  static EbpfNetMetricInfo codetiming_avg_ns;\n  static EbpfNetMetricInfo codetiming_count;\n  static EbpfNetMetricInfo codetiming_max_ns;\n  static EbpfNetMetricInfo codetiming_min_ns;\n  static EbpfNetMetricInfo codetiming_sum_ns;\n  static EbpfNetMetricInfo collector_health;\n  static EbpfNetMetricInfo collector_log_count;\n  static EbpfNetMetricInfo connections;\n  static EbpfNetMetricInfo disconnects;\n  static EbpfNetMetricInfo entrypoint_info;\n  static EbpfNetMetricInfo message;\n  static EbpfNetMetricInfo otlp_grpc_bytes_failed;\n  static EbpfNetMetricInfo otlp_grpc_bytes_sent;\n  static EbpfNetMetricInfo otlp_grpc_metrics_failed;\n  static EbpfNetMetricInfo otlp_grpc_metrics_sent;\n  static EbpfNetMetricInfo otlp_grpc_requests_failed;\n  static EbpfNetMetricInfo otlp_grpc_requests_sent;\n  static EbpfNetMetricInfo otlp_grpc_unknown_response_tags;\n  static EbpfNetMetricInfo pipeline_agent_connections;\n  static EbpfNetMetricInfo pipeline_message_error;\n  static EbpfNetMetricInfo prometheus_big_items_dropped;\n  static EbpfNetMetricInfo prometheus_bytes_discarded;\n  static EbpfNetMetricInfo prometheus_bytes_ingested;\n  static EbpfNetMetricInfo prometheus_bytes_written;\n  static EbpfNetMetricInfo prometheus_failed_scrapes;\n  static EbpfNetMetricInfo rpc_latency_ns;\n  static EbpfNetMetricInfo rpc_queue_buf_utilization;\n  static EbpfNetMetricInfo rpc_queue_buf_utilization_fraction;\n  static EbpfNetMetricInfo rpc_queue_elem_utilization_fraction;\n  static EbpfNetMetricInfo rpc_write_stalls;\n  static EbpfNetMetricInfo span_utilization;\n  static EbpfNetMetricInfo span_utilization_fraction;\n  static EbpfNetMetricInfo span_utilization_max;\n  static EbpfNetMetricInfo time_since_last_message_ns;\n  static EbpfNetMetricInfo up;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/thread_safe_map.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// ThreadSafeMap is an associative map data structure that allows for quick\n// thread-safe lookups and inserts. Only a handful of operations are supported,\n// and the inserted values cannot be mutated.\n//\n// NOTE: By default this class uses abseil's mutexes. These are fast, but\n// don't have ThreadSanitizer support by default. If you wish to use thread\n// sanitization and don't care about the performance hit, just compile with\n// `-DUSE_STL_MUTEX`.\n\n#include <absl/container/flat_hash_map.h>\n#include <absl/hash/hash.h>\n#include <absl/synchronization/mutex.h>\n\n#include <cstdlib>\n#include <memory>\n#include <mutex>\n#include <shared_mutex>\n#include <vector>\n\ntemplate <typename Key, typename Value> class ThreadSafeMap {\npublic:\n  // TODO(dandrino): Add customization in how the underlying values are stored\n  // if that becomes a feature that we need.\n\n  // `bucket_count` specifies how many buckets to use.\n  explicit ThreadSafeMap(std::size_t bucket_count = kDefaultBucketCount);\n\n  // Looks up the value for the given key and returns it as a shared_ptr. In\n  // the even that the underlying map entry has been overwritten by another\n  // thread, outstanding references to this value will still be valid.\n  std::shared_ptr<const Value> get(const Key &key) const;\n\n  // Inserts the given value into the map for the provided key. If an entry\n  // already exists for the given key, will overwrite it. Returns the\n  // shared_ptr that would be returned by calling `get` on the same key.\n  std::shared_ptr<const Value> insert(const Key &key, Value value);\n\n  // Looks up the value for the given key. If none exists, inserts the value\n  // created using the provided function. Returns a shared_key to the value.\n  std::shared_ptr<const Value> get_or_insert(const Key &key, std::function<Value()> value_func);\n\n  // Removes the value associated with the provided key from the map.\n  // Returns true if removed, false otherwise.\n  bool erase(const Key &key);\n\n  // Writes the contents of this map to the provided STL-map-like data\n  // structure. There is no guaranteed that the populated values are the same\n  // as those in this class at the moment of invocation.\n  // NOTE: This is sort of a poor man's alternative to adding full iterator\n  // support, and primarily exists now for debugging purposes.\n  template <typename Map> void write_to(Map *out) const;\n\nprivate:\n  // A bucket is a subpartition of the map space that contend for the same\n  // mutex lock.\n  struct Bucket {\n    absl::flat_hash_map<Key, std::shared_ptr<Value>> map;\n#ifdef USE_STL_MUTEX\n    mutable std::shared_mutex mu;\n#else\n    mutable absl::Mutex mu;\n#endif\n  };\n\n  // Returns the bucket for the given key.\n  Bucket *get_bucket(const Key &key);\n  const Bucket *get_bucket(const Key &key) const { return const_cast<ThreadSafeMap<Key, Value> *>(this)->get_bucket(key); }\n\n  // 256 ought to be enough for anybody.\n  static const std::size_t kDefaultBucketCount = 256;\n\n  std::vector<Bucket> buckets_;\n\n  const absl::Hash<Key> hasher_ = {};\n};\n\n// Implementation below.\n\ntemplate <typename Key, typename Value>\nThreadSafeMap<Key, Value>::ThreadSafeMap(const std::size_t bucket_count) : buckets_(bucket_count)\n{}\n\ntemplate <typename Key, typename Value> std::shared_ptr<const Value> ThreadSafeMap<Key, Value>::get(const Key &key) const\n{\n  const Bucket *const bucket = get_bucket(key);\n#ifdef USE_STL_MUTEX\n  std::shared_lock l(bucket->mu);\n#else\n  absl::ReaderMutexLock l(&bucket->mu);\n#endif\n\n  auto it = bucket->map.find(key);\n  if (it == bucket->map.end())\n    return {}; // Not found\n\n  return it->second;\n}\n\ntemplate <typename Key, typename Value>\nstd::shared_ptr<const Value> ThreadSafeMap<Key, Value>::insert(const Key &key, Value value)\n{\n  auto inserted_value = std::make_shared<Value>(std::move(value));\n\n  Bucket *const bucket = get_bucket(key);\n#ifdef USE_STL_MUTEX\n  std::unique_lock l(bucket->mu);\n#else\n  absl::MutexLock l(&bucket->mu);\n#endif\n\n  bucket->map[key] = inserted_value;\n  return inserted_value;\n}\n\ntemplate <typename Key, typename Value>\nstd::shared_ptr<const Value> ThreadSafeMap<Key, Value>::get_or_insert(const Key &key, std::function<Value()> value_func)\n{\n  Bucket *const bucket = get_bucket(key);\n#ifdef USE_STL_MUTEX\n  std::unique_lock l(bucket->mu);\n#else\n  absl::MutexLock l(&bucket->mu);\n#endif\n\n  auto it = bucket->map.find(key);\n  if (it != bucket->map.end()) {\n    return it->second;\n  } else {\n    auto inserted_value = std::make_shared<Value>(value_func());\n    bucket->map[key] = inserted_value;\n    return inserted_value;\n  }\n}\n\ntemplate <typename Key, typename Value> bool ThreadSafeMap<Key, Value>::erase(const Key &key)\n{\n  Bucket *const bucket = get_bucket(key);\n#ifdef USE_STL_MUTEX\n  std::unique_lock l(bucket->mu);\n#else\n  absl::MutexLock l(&bucket->mu);\n#endif\n\n  if (bucket->map.erase(key) > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}\n\ntemplate <typename Key, typename Value>\ntypename ThreadSafeMap<Key, Value>::Bucket *ThreadSafeMap<Key, Value>::get_bucket(const Key &key)\n{\n  const std::size_t index = hasher_(key) % buckets_.size();\n  return &buckets_[index];\n}\n\ntemplate <typename Key, typename Value> template <typename Map> void ThreadSafeMap<Key, Value>::write_to(Map *const out) const\n{\n  for (const Bucket &bucket : buckets_) {\n#ifdef USE_STL_MUTEX\n    std::shared_lock l(bucket.mu);\n#else\n    absl::ReaderMutexLock l(&bucket.mu);\n#endif\n    std::copy(bucket.map.begin(), bucket.map.end(), std::inserter(*out, out->end()));\n  }\n}\n"
  },
  {
    "path": "reducer/tsdb_format.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAMESPACE reducer\n#define ENUM_NAME TsdbFormat\n#define ENUM_TYPE std::uint16_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(prometheus, 1, \"\")                                                                                                         \\\n  X(json, 2, \"\")                                                                                                               \\\n  X(otlp_grpc, 3, \"\")\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "reducer/tsdb_formatter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"tsdb_formatter.h\"\n#include \"json_formatter.h\"\n#include \"otlp_grpc_formatter.h\"\n#include \"prometheus_formatter.h\"\n\n#include <reducer/constants.h>\n\n#include <util/time.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <ctime>\n#include <stdexcept>\n\nnamespace reducer {\n\nstd::unique_ptr<TsdbFormatter>\nTsdbFormatter::make(TsdbFormat format, std::optional<std::reference_wrapper<Publisher::WriterPtr>> writer)\n{\n  switch (format) {\n  case TsdbFormat::prometheus:\n    return std::make_unique<PrometheusFormatter>();\n  case TsdbFormat::json:\n    return std::make_unique<JsonTsdbFormatter>();\n  case TsdbFormat::otlp_grpc:\n    if (!writer) {\n      throw std::invalid_argument(\"otlp_grpc format requires writer\");\n    }\n    return std::make_unique<OtlpGrpcFormatter>(*writer);\n  default:\n    throw std::invalid_argument(\"unsupported format\");\n  }\n}\n\nvoid TsdbFormatter::set_aggregation(std::string_view aggregation)\n{\n  assign_label(\"aggregation\", aggregation);\n  aggregation_ = aggregation;\n  aggregation_changed_ = true;\n}\n\nvoid TsdbFormatter::set_rollup(rollup_t rollup)\n{\n  rollup_ = rollup;\n  rollup_changed_ = true;\n}\n\nvoid TsdbFormatter::set_timestamp(timestamp_t timestamp)\n{\n  timestamp_ = timestamp;\n  timestamp_changed_ = true;\n}\n\nvoid TsdbFormatter::set_labels(labels_t labels)\n{\n  labels_ = std::move(labels);\n  labels_changed_ = true;\n}\n\nvoid TsdbFormatter::set_labels(std::initializer_list<std::tuple<std::string_view, std::string_view>> labels)\n{\n  labels_.clear();\n  for (auto const &[name, value] : labels) {\n    assign_label(name, value);\n  }\n  labels_changed_ = true;\n}\n\nvoid TsdbFormatter::assign_label(std::string_view name, std::string_view value)\n{\n  assign_label(std::string(name), std::string(value));\n}\n\nvoid TsdbFormatter::assign_label(std::string name, std::string value)\n{\n  labels_.insert_or_assign(name, value);\n  labels_changed_ = true;\n}\n\nvoid TsdbFormatter::remove_label(std::string_view name)\n{\n  labels_.erase(std::string(name));\n  labels_changed_ = true;\n}\n\nvoid TsdbFormatter::clear_labels()\n{\n  labels_.clear();\n  labels_changed_ = true;\n}\n\nvoid TsdbFormatter::write(std::string_view metric_name, value_t value, Publisher::WriterPtr const &writer)\n{\n  write(MetricInfo{metric_name}, value, writer);\n}\n\nvoid TsdbFormatter::write(MetricInfo const &metric, value_t value, Publisher::WriterPtr const &writer)\n{\n  format(\n      metric,\n      value,\n      aggregation_,\n      aggregation_changed_,\n      rollup_,\n      rollup_changed_,\n      labels_,\n      labels_changed_,\n      timestamp_,\n      timestamp_changed_,\n      writer);\n\n  aggregation_changed_ = false;\n  rollup_changed_ = false;\n  labels_changed_ = false;\n  timestamp_changed_ = false;\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/tsdb_formatter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"metric_info.h\"\n#include \"publisher.h\"\n#include \"tsdb_format.h\"\n\n#include <platform/types.h>\n\n#include <chrono>\n#include <functional>\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <variant>\n\nnamespace reducer {\n\n// Formats time-series entries that are to be sent to a TSDB.\n//\n// Formatted time-series entries are produced by calling the `write` method.\n//\n// Before calling `write`, time-series parameters are set using\n// `set_aggregation`, `set_rollup`, `set_labels` and `set_timestamp`.\n//\n// Aggregation is a string like `az_az`, `az_role`, `role_role`, etc.\n// Rollup is the number of seconds that the time-series is rolled-up to.\n//\n// This interface reflects the usage pattern where a number of similar\n// time-series are formatted in succession, each one differing from others only\n// in the metric name and value, while having the same set of labels, same\n// timestamp, aggregation name and rollup. The interface allows implementations\n// to format and cache portions of the output that doesn't change between calls\n// to `write`.\n//\nclass TsdbFormatter {\n  friend class OtlpGrpcFormatterTest;\n\npublic:\n  // Creates an instance for the specified format.\n  static std::unique_ptr<TsdbFormatter>\n  make(TsdbFormat format, std::optional<std::reference_wrapper<Publisher::WriterPtr>> writer = std::nullopt);\n\n  using value_t = std::variant<u32, u64, double>;\n  using rollup_t = std::optional<int>;\n  using labels_t = std::map<std::string, std::string>;\n  using timestamp_t = std::chrono::nanoseconds;\n\n  virtual ~TsdbFormatter() {}\n\n  // Some formatters may buffer metrics that need to be flushed periodically and before being destructed.\n  virtual void flush() {};\n\n  // Assigns the aggregation name (e.g. az_az, role_az, etc.)\n  void set_aggregation(std::string_view aggregation);\n  // Assigns the rollup count (e.g. 30, 60, ...).\n  void set_rollup(rollup_t rollup);\n  // Assigns the timestamp of time-series entry.\n  void set_timestamp(timestamp_t timestamp);\n\n  // Assigns time-series labels (set of key/value pairs).\n  void set_labels(labels_t labels);\n  void set_labels(std::initializer_list<std::tuple<std::string_view, std::string_view>> labels);\n\n  // Helper function to set labels from NodeLabels, FlowLabels objects.\n  template <typename Labels> void set_labels(Labels const &labels)\n  {\n    labels_.clear();\n    labels.foreach ([this](std::string_view name, std::string_view value) {\n      if (!value.empty()) {\n        assign_label(name, value);\n      };\n    });\n  }\n\n  // Assigns a specific label.\n  void assign_label(std::string_view name, std::string_view value);\n  void assign_label(std::string name, std::string value);\n\n  // Removes a specific label.\n  void remove_label(std::string_view name);\n\n  // Clears the labels\n  void clear_labels();\n\n  // Writes the formatted entry using the provided publisher writer object.\n  void write(std::string_view metric_name, value_t value, Publisher::WriterPtr const &writer);\n\n  // Writes the formatted entry using the provided publisher writer object.\n  void write(MetricInfo const &metric, value_t value, Publisher::WriterPtr const &writer);\n\n  // Writes the formatted entry as a flow log.\n  template <typename TMetrics> void write_flow_log(TMetrics const &metrics)\n  {\n    format_flow_log(metrics, labels_, labels_changed_, timestamp_, timestamp_changed_);\n\n    aggregation_changed_ = false;\n    rollup_changed_ = false;\n    labels_changed_ = false;\n    timestamp_changed_ = false;\n  };\n\nprotected:\n  // Subclasses implement this function to do the actual formatting.\n  virtual void format(\n      MetricInfo const &metric,\n      value_t value,\n      std::string_view aggregation,\n      bool aggregation_changed,\n      rollup_t rollup,\n      bool rollup_changed,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed,\n      Publisher::WriterPtr const &writer) = 0;\n\n  // Subclasses that support formatting metrics as flow logs implement this function to do the actual formatting.\n  virtual void format_flow_log(\n      ebpf_net::metrics::tcp_metrics const &tcp_metrics,\n      labels_t labels,\n      bool labels_changed,\n      timestamp_t timestamp,\n      bool timestamp_changed) {};\n\nprivate:\n  std::string aggregation_;\n  bool aggregation_changed_{false};\n\n  rollup_t rollup_;\n  bool rollup_changed_{false};\n\n  labels_t labels_;\n  bool labels_changed_{false};\n\n  timestamp_t timestamp_{0};\n  bool timestamp_changed_{false};\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/uid_key.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"uid_key.h\"\n\n#include <util/lookup3.h>\n\n#include <cstring>\n\nnamespace reducer {\n\nu64 uid_to_u64(const std::string_view uid)\n{\n  uint32_t pc = 0;\n  uint32_t pb = 0;\n  lookup3_hashlittle2((void *)uid.data(), uid.length(), &pc, &pb);\n  return (u64)pc + (((u64)pb) << 32);\n}\n\nvoid uid_suffix(std::string_view uid, u8 *dest, std::size_t dest_size)\n{\n  if (uid.length() >= dest_size) {\n    // uid too long, take the suffix\n    memcpy(dest, uid.data() + uid.length() - dest_size, dest_size);\n  } else {\n    // uid can fit in dest, set to the entire string, zeroing the rest\n    memset(dest, 0, dest_size);\n    memcpy(dest, uid.data(), uid.length());\n  }\n}\n\nu64 make_uid_key(std::string_view uid, u8 *out_suffix, std::size_t suffix_size)\n{\n  uid_suffix(uid, out_suffix, suffix_size);\n  return uid_to_u64(uid);\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/uid_key.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <string_view>\n\nnamespace reducer {\n\n// Creates a hash to use as a 64 bit collision reducer for pod and containers\n// UIDs.\nu64 uid_to_u64(std::string_view uid);\n\n// Copies at most `dest_size` bytes of `uid` into `dest`, zeroing the rest.\nvoid uid_suffix(std::string_view uid, u8 *dest, std::size_t dest_size);\n\n// Constructs an \"UID key\" from the supplied UID.\n//\n// UID key is a uid_suffix/uid_hash pair generated from the original UID:\n// - uid_suffix is a copy of last `suffix_size` charactes, padded with zeroes if\n//   `uid` length is less than `suffix_size`;\n// - uid_hash is a hash of the whole UID.\n//\n// Returns the uid_hash, places the uid_suffix into the memory location pointed\n// to by `out_suffix`.\n//\nu64 make_uid_key(std::string_view uid, u8 *out_suffix, std::size_t suffix_size);\n\ntemplate <typename T> T make_uid_key(std::string_view uid)\n{\n  T key;\n  key.uid_hash = make_uid_key(uid, key.uid_suffix.data(), key.uid_suffix.max_size());\n  return key;\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/util/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nadd_library(\n  blob_collector\n  STATIC\n    blob_collector.cc\n)\ntarget_link_libraries(\n  blob_collector\n    logging\n)\n\nadd_library(\n  index_dumper\n  STATIC\n    index_dumper.cc\n)\ntarget_link_libraries(\n  index_dumper\n    logging\n    spdlog\n)\n\nadd_library(\n  thread_ops\n  STATIC\n    thread_ops.cc\n)\ntarget_link_libraries(\n  thread_ops\n)\n\nadd_library(\n  signal_handling\n  STATIC\n    signal_handler.cc\n)\ntarget_link_libraries(\n  signal_handling\n    thread_ops\n    libuv-interface\n)\n\nadd_library(\n  time_tracker\n  STATIC\n    time_tracker.cc\n)\ntarget_link_libraries(\n  time_tracker\n    libuv-interface\n)\n\nadd_library(\n  virtual_clock\n  STATIC\n    virtual_clock.cc\n)\nadd_unit_test(\n  virtual_clock\n  LIBS\n    virtual_clock\n)\n"
  },
  {
    "path": "reducer/util/blob_collector.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/util/blob_collector.h>\n\n#include <common/component.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n\nnamespace {\nconstexpr auto COOLDOWN = 2s;\n} // namespace\n\nvoid BlobCollector::collect(\n    std::underlying_type_t<CollectedBlobType> raw_type, u64 subtype, std::string_view metadata, std::string_view blob)\n{\n  auto const type = sanitize_enum(static_cast<CollectedBlobType>(raw_type));\n  auto const index = enum_index_of(type);\n\n  if (!rate_counters_[index].elapsed(COOLDOWN)) {\n    return;\n  }\n\n  rate_counters_[index].reset();\n\n  switch (type) {\n  default:\n    // TODO: this should be saved in appropriate storage.\n    //       For now just log so we can tune the parser\n    LOG::warn(\"collected blob `{}` (subtype={} metadata={}): ```{}```\", to_string(type), subtype, metadata, blob);\n    break;\n  }\n}\n"
  },
  {
    "path": "reducer/util/blob_collector.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/collected_blob_type.h>\n#include <util/enum.h>\n#include <util/stop_watch.h>\n\nclass BlobCollector {\npublic:\n  void\n  collect(std::underlying_type_t<CollectedBlobType> raw_type, u64 subtype, std::string_view metadata, std::string_view blob);\n\nprivate:\n  enum_traits<CollectedBlobType>::array_map<StopWatch<>> rate_counters_;\n};\n"
  },
  {
    "path": "reducer/util/docker_image.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string_view>\n\nstruct DockerImageMetadata {\n  static constexpr char VERSION_DELIMITER{':'};\n  static constexpr char IMAGE_DELIMITER{'/'};\n  static constexpr char CHECKSUM_DELIMITER{'@'};\n\n  explicit constexpr DockerImageMetadata(std::string_view image);\n\n  constexpr std::string_view registry() const { return registry_; }\n  constexpr std::string_view name() const { return name_; }\n  constexpr std::string_view version() const { return version_; }\n\nprivate:\n  std::string_view registry_;\n  std::string_view name_;\n  std::string_view version_;\n};\n\n#include <reducer/util/docker_image.inl>\n"
  },
  {
    "path": "reducer/util/docker_image.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\nconstexpr DockerImageMetadata::DockerImageMetadata(std::string_view image)\n{\n  if (auto const i = image.find_last_of(IMAGE_DELIMITER); i != std::string_view::npos) {\n    registry_ = image.substr(0, i);\n    image.remove_prefix(registry_.size() + 1);\n  }\n\n  if (auto const i = image.find_first_of(CHECKSUM_DELIMITER); i != std::string_view::npos) {\n    version_ = image.substr(i + 1);\n    name_ = image.substr(0, i);\n    return;\n  }\n\n  if (auto const i = image.find_first_of(VERSION_DELIMITER); i != std::string_view::npos) {\n    name_ = image.substr(0, i);\n\n    if (name_ == \"sha256\") {\n      name_ = std::string_view{};\n      version_ = image;\n    } else {\n      version_ = image.substr(i + 1);\n    }\n  } else {\n    name_ = image;\n  }\n}\n"
  },
  {
    "path": "reducer/util/index_dumper.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/util/index_dumper.h>\n\n#include <platform/types.h>\n\nstd::string IndexDumper::dump_dir_{};\n\nvoid IndexDumper::set_dump_dir(std::string_view dir)\n{\n  dump_dir_ = dir;\n}\n\nstd::chrono::seconds IndexDumper::cooldown_{0};\n\nvoid IndexDumper::set_cooldown(std::chrono::seconds cooldown)\n{\n  cooldown_ = cooldown;\n}\n"
  },
  {
    "path": "reducer/util/index_dumper.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <chrono>\n#include <string>\n#include <string_view>\n\nclass IndexDumper {\npublic:\n  template <typename Index> void dump(std::string_view app, int shard, Index const &index, std::chrono::seconds timestamp);\n\n  // Sets the output directory.\n  // If the output directory is not set, then the process' working directory is used.\n  // This function is not thread-safe and should be called by `main` before any threads are created.\n  static void set_dump_dir(std::string_view dir);\n\n  // This function is not thread-safe and should be called by `main` before any threads are created.\n  // A value of 0 (default) disables index dumping.\n  static void set_cooldown(std::chrono::seconds cooldown);\n\nprivate:\n  std::chrono::seconds last_dump_ = {};\n  std::size_t iteration_ = 0;\n\n  static std::string dump_dir_;\n  static std::chrono::seconds cooldown_;\n};\n\n#include <reducer/util/index_dumper.inl>\n"
  },
  {
    "path": "reducer/util/index_dumper.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/time.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <fstream>\n\ntemplate <typename Index>\nvoid IndexDumper::dump(std::string_view app, int shard, Index const &index, std::chrono::seconds timestamp)\n{\n  if (!cooldown_.count()) {\n    return;\n  }\n  if (timestamp <= last_dump_ + cooldown_) {\n    return;\n  }\n  last_dump_ = timestamp;\n\n  auto const file_name =\n      fmt::format(\"{}_{}-{}_{}.json\", app, shard, iteration_++, integer_time<std::chrono::seconds>(timestamp));\n\n  std::string file_path;\n  if (!dump_dir_.empty()) {\n    file_path = fmt::format(\"{}/{}\", dump_dir_, file_name);\n  } else {\n    // Output in the working directory.\n    file_path = file_name;\n  }\n\n  std::ofstream out{file_path};\n\n  out << index;\n\n  out.flush();\n  out.close();\n}\n"
  },
  {
    "path": "reducer/util/signal_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"signal_handler.h\"\n#include \"thread_ops.h\"\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/uv_helpers.h>\n\n#include <signal.h>\n#include <stdexcept>\n\nnamespace reducer {\n\nSignalHandler::SignalHandler()\n{\n  CHECK_UV(uv_loop_init(&loop_));\n\n  CHECK_UV(uv_async_init(&loop_, &stop_async_, &on_stop_async));\n\n  stop_async_.data = this;\n}\n\nvoid SignalHandler::run()\n{\n  set_self_thread_name(\"signal_handler\").on_error([](auto const &error) {\n    LOG::warn(\"unable to set name for signal handler thread: {}\", error);\n  });\n  uv_run(&loop_, UV_RUN_DEFAULT);\n}\n\nvoid SignalHandler::stop_async()\n{\n  CHECK_UV(uv_async_send(&stop_async_));\n}\n\nvoid SignalHandler::handle(int signum, Callback callback)\n{\n  std::lock_guard<std::mutex> lock(mutex_);\n\n  uv_signal_t &handler = handlers_[signum];\n\n  CHECK_UV(uv_signal_init(&loop_, &handler));\n\n  handler.data = this;\n\n  CHECK_UV(uv_signal_start(&handler, on_signal, signum));\n\n  callbacks_[signum] = callback;\n}\n\nvoid SignalHandler::on_stop_async(uv_async_t *handle)\n{\n  auto obj = reinterpret_cast<SignalHandler *>(handle->data);\n  uv_stop(&obj->loop_);\n}\n\nvoid SignalHandler::on_signal(uv_signal_t *handle, int signum)\n{\n  auto obj = reinterpret_cast<SignalHandler *>(handle->data);\n  obj->on_signal(signum);\n}\n\nvoid SignalHandler::on_signal(int signum)\n{\n  LOG::info(\"Caught signal {}\", signum);\n\n  Callback callback;\n\n  {\n    std::lock_guard<std::mutex> lock(mutex_);\n\n    if (auto it = callbacks_.find(signum); it != callbacks_.end()) {\n      callback = callbacks_[signum];\n    }\n  }\n\n  if (callback) {\n    callback(signum);\n  }\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/util/signal_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <uv.h>\n\n#include <functional>\n#include <map>\n#include <mutex>\n\nnamespace reducer {\n\n// Libuv loop for handling Unix signals.\n//\n// Use this class instead of handling signals (e.g. SIGINT and SIGTERM) in\n// each libuv loop.\n//\nclass SignalHandler {\npublic:\n  using Callback = std::function<void(int)>;\n\n  SignalHandler();\n  SignalHandler(const SignalHandler &) = delete;\n\n  // Runs the libuv loop.\n  void run();\n\n  // Stops the loop.\n  void stop_async();\n\n  // Registers a callback for the specified signal.\n  // NOTE: overwrites previously registered callback.\n  void handle(int signum, Callback callback);\n\nprivate:\n  // Libuv loop object.\n  uv_loop_t loop_;\n  // Async object used for stopping the loop from another thread.\n  uv_async_t stop_async_;\n\n  // Signal to libuv handler map.\n  std::map<int, uv_signal_t> handlers_;\n  // Signal to callback map.\n  std::map<int, Callback> callbacks_;\n\n  // Mutex object for serializing access to handler and callbacks maps.\n  std::mutex mutex_;\n\n  // Callback function for stop_async.\n  static void on_stop_async(uv_async_t *handle);\n\n  // Callback function for libuv signal handlers.\n  static void on_signal(uv_signal_t *handle, int signum);\n\n  // Called on every registered signal.\n  void on_signal(int signum);\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/util/thread_ops.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/util/thread_ops.h>\n\n#include <pthread.h>\n\nExpected<bool, std::errc> set_self_thread_name(std::string_view name)\n{\n  char buffer[16] = {0};\n  auto const length = std::max((sizeof(buffer) / sizeof(*buffer)) - 1, name.size());\n  name.copy(buffer, length);\n  buffer[length] = '\\0';\n\n  if (int const error = pthread_setname_np(pthread_self(), buffer)) {\n    return {unexpected, static_cast<std::errc>(error)};\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "reducer/util/thread_ops.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/expected.h>\n\n#include <system_error>\n#include <thread>\n\n/**\n * Sets the current thread name to the given string.\n *\n * The thread name has a limitation of 15 characters, so it will be truncated\n * accordingly.\n *\n * Returns true on success or an error code on failure.\n */\nExpected<bool, std::errc> set_self_thread_name(std::string_view name);\n"
  },
  {
    "path": "reducer/util/time_tracker.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/userspace-time.h>\n#include <reducer/util/time_tracker.h>\n\n#include <cmath>\n\nusing namespace std::literals::chrono_literals;\n\nconstexpr auto ACCEPTED_CLOCK_DIFFERENCE = 10s;\n\nTimeTracker::TimeTracker() : last_message_seen_(now()) {}\n\ninline TimeTracker::timestamp_t TimeTracker::now() const\n{\n  return std::chrono::nanoseconds(fp_get_time_ns());\n}\n\nbool TimeTracker::message_received(timestamp_t client_timestamp)\n{\n  auto const timestamp = now();\n\n  // keeps track of server time of last received message\n  last_message_seen_ = timestamp;\n\n  // computes clock offset between client and server\n  if (client_timestamp.count()) {\n    static_assert(std::is_signed_v<timestamp_t::rep>);\n    timestamp_t const diff = timestamp - client_timestamp;\n\n    // TODO: IMPLEMENT MOVING AVERAGE TO USE LAST n OFFSETS RATHER THAN LAST\n    // TODO: ACCOUNT FOR ROUND-TRIP TIME\n    // TODO: DITCH SAMPLES / DISCONNECT CLIENT WHEN OFFSET IS ABOVE A THRESHOLD\n    clock_offset_ = timestamp_t(std::abs(diff.count()));\n\n    if (clock_offset_ >= ACCEPTED_CLOCK_DIFFERENCE) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nstd::chrono::nanoseconds TimeTracker::clock_offset() const\n{\n  return clock_offset_;\n}\n\nstd::chrono::nanoseconds TimeTracker::time_since_last_message() const\n{\n  return now() - last_message_seen_;\n}\n"
  },
  {
    "path": "reducer/util/time_tracker.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <uv.h>\n\n#include <chrono>\n\n/**\n * This class tracks time between client and server.\n *\n * Things it can do:\n * - calculate clock offset between client and server;\n * - track timeouts from last message received from client.\n */\nclass TimeTracker {\npublic:\n  using timestamp_t = std::chrono::nanoseconds;\n\n  TimeTracker();\n\n  /**\n   * Updates the tracker when a message has been received.\n   *\n   * @returns: true if the client clock seems sane, false if it differs above\n   *   the accepted threshold.\n   */\n  bool message_received(timestamp_t client_timestamp);\n\n  std::chrono::nanoseconds clock_offset() const;\n  std::chrono::nanoseconds time_since_last_message() const;\n\nprivate:\n  inline timestamp_t now() const;\n\n  timestamp_t clock_offset_ = timestamp_t::zero();\n  timestamp_t last_message_seen_;\n};\n"
  },
  {
    "path": "reducer/util/virtual_clock.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"virtual_clock.h\"\n\n#include <algorithm>\n\nVirtualClock::VirtualClock(fast_div const &divider) : divider_(divider), timeslot_duration_(divider_.estimated_reciprocal()) {}\n\nvoid VirtualClock::add_inputs(size_t n)\n{\n  inputs_.resize(inputs_.size() + n);\n}\n\nsize_t VirtualClock::n_inputs() const\n{\n  return inputs_.size();\n}\n\nbool VirtualClock::is_current(size_t input_index)\n{\n  return current_timeslot_.has_value() && (inputs_.at(input_index).timeslot == current_timeslot_);\n}\n\nbool VirtualClock::can_update(size_t input_index)\n{\n  return (inputs_.at(input_index).timeslot == current_timeslot_);\n}\n\nint VirtualClock::update(size_t input_index, u64 timestamp)\n{\n  auto &input = inputs_.at(input_index);\n\n  if (input.timeslot != current_timeslot_) {\n    return -EPERM;\n  }\n\n  const timeslot_t timeslot = timestamp / divider_;\n\n  if (input.timeslot) {\n    timeslot_diff_t timeslot_diff = (timeslot_diff_t)timeslot - *input.timeslot;\n    if (timeslot_diff >= 0) {\n      *input.timeslot += timeslot_diff;\n    } else {\n      return -EINVAL;\n    }\n  } else {\n    input.timeslot = timeslot;\n  }\n\n  return 0;\n}\n\nbool VirtualClock::advance()\n{\n  if (current_timeslot_) {\n    if (auto advance_slots = min_input_advance().value_or(0); advance_slots > 0) {\n      // All inputs have moved into newer timeslots.\n      *current_timeslot_ += advance_slots;\n      return true;\n    }\n  } else {\n    // Initializing the current timeslot to the earliest input timeslot.\n    current_timeslot_ = earliest_input_timeslot();\n  }\n\n  return false;\n}\n\nstd::optional<VirtualClock::timeslot_t> VirtualClock::earliest_input_timeslot()\n{\n  std::optional<timeslot_t> min_timeslot;\n\n  for (auto &input : inputs_) {\n    if (!input.timeslot) {\n      return std::nullopt;\n    }\n\n    min_timeslot = min_timeslot ? std::min(*min_timeslot, *input.timeslot) : *input.timeslot;\n  }\n\n  std::optional<timeslot_diff_t> min_diff;\n\n  for (auto &input : inputs_) {\n    timeslot_diff_t diff = (timeslot_diff_t)(*input.timeslot) - *min_timeslot;\n    min_diff = min_diff ? std::min(*min_diff, diff) : diff;\n  }\n\n  return *min_timeslot + *min_diff;\n}\n\nstd::optional<VirtualClock::timeslot_diff_t> VirtualClock::min_input_advance()\n{\n  std::optional<timeslot_diff_t> min_advance;\n\n  for (auto &input : inputs_) {\n    if (!input.timeslot) {\n      return std::nullopt;\n    }\n\n    timeslot_diff_t advance = (timeslot_diff_t)(*input.timeslot) - *current_timeslot_;\n\n    min_advance = min_advance ? std::min(*min_advance, advance) : advance;\n  }\n\n  return min_advance;\n}\n"
  },
  {
    "path": "reducer/util/virtual_clock.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <util/fast_div.h>\n\n#include <optional>\n#include <vector>\n\n// Clock driven by multiple inputs.\n//\n// Input timestamps are divided into timeslots based on the divider which can\n// be supplied to the constructor. Once all imputs move out of the current\n// timeslot, the clock can advance.\n//\n// Inputs are first added using the `add_inputs()` method.\n//\nclass VirtualClock {\npublic:\n  typedef u16 timeslot_t;\n\n  // Constructs the object by using the specified timestamp divider.\n  explicit VirtualClock(fast_div const &divider = {1e9, 16});\n\n  // Adds `n` additional inputs.\n  void add_inputs(size_t n);\n\n  // Returns the current number of inputs this clock has.\n  size_t n_inputs() const;\n\n  // Returns whether the specified input is current with this clock.\n  // Current means that the input timeslot is aligned with the clock's timeslot.\n  // Assumes `input_index` < `n_inputs()`.\n  bool is_current(size_t input_index);\n\n  // Returns whether the specified input can be updated.\n  // Assumes `input_index` < `n_inputs()`.\n  bool can_update(size_t input_index);\n\n  // Updates the specified input.\n  // Assumes `input_index` < `n_inputs()`.\n  // Returns 0 on success;\n  //         -EINVAL if the supplied timestamp points to a past timeslot;\n  //         -EPERM if the specified input can't be updated (`can_update()`\n  //                would return `false`).\n  int update(size_t input_index, u64 timestamp);\n\n  // Duration of time slots, in timestamp units.\n  double timeslot_duration() const { return timeslot_duration_; }\n\n  // Current timeslot, or nullopt if not yet initialized.\n  std::optional<timeslot_t> current_timeslot() const { return current_timeslot_; }\n\n  // Advances this clock's timeslot, if possible.\n  // Returns `true` if advanced, `false` otherwise.\n  bool advance();\n\nprivate:\n  typedef s16 timeslot_diff_t;\n\n  struct Input {\n    std::optional<timeslot_t> timeslot;\n  };\n\n  std::vector<Input> inputs_;\n\n  // Divides input timestamps into clock timeslots.\n  fast_div divider_;\n  // Approximate duration of timeslots in timestamp units.\n  double timeslot_duration_{0};\n\n  // This clock's current timeslot.\n  std::optional<timeslot_t> current_timeslot_;\n\n  // Returns the earliest timeslot value of all inputs, or nullopt if\n  // not all inputs have been updated.\n  std::optional<timeslot_t> earliest_input_timeslot();\n\n  // Returns the smallest advance in timeslots of all inputs, or nullopt\n  // if not all inputs have been updated.\n  // Assumes `current_timeslot_` is initialized.\n  std::optional<timeslot_diff_t> min_input_advance();\n};\n"
  },
  {
    "path": "reducer/util/virtual_clock_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"virtual_clock.h\"\n\n#include <gtest/gtest.h>\n\n#include <cmath>\n#include <limits>\n\nconstexpr auto TIMESLOT_MIN = std::numeric_limits<VirtualClock::timeslot_t>::min();\nconstexpr auto TIMESLOT_MAX = std::numeric_limits<VirtualClock::timeslot_t>::max();\n\nstatic const VirtualClock DEFAULT_CLOCK;\n\nstatic const u64 TIMESTAMP_STEP = (u64)std::ceil(DEFAULT_CLOCK.timeslot_duration());\n\nTEST(virtual_clock, empty)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n\n  ASSERT_EQ(clock.n_inputs(), (size_t)0);\n  ASSERT_FALSE(clock.current_timeslot().has_value());\n}\n\nTEST(virtual_clock, add_inputs)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  ASSERT_EQ(clock.n_inputs(), (size_t)2);\n  ASSERT_FALSE(clock.current_timeslot().has_value());\n}\n\nTEST(virtual_clock, current_timeslot)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = 0;\n\n  clock.update(0, timestamp);\n\n  ASSERT_FALSE(clock.current_timeslot().has_value());\n\n  clock.update(1, timestamp);\n\n  ASSERT_FALSE(clock.current_timeslot().has_value());\n\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n}\n\nTEST(virtual_clock, can_update)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = 0;\n\n  ASSERT_TRUE(clock.can_update(0));\n  ASSERT_TRUE(clock.can_update(1));\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp);\n\n  ASSERT_FALSE(clock.can_update(0));\n  ASSERT_FALSE(clock.can_update(1));\n\n  clock.advance();\n\n  ASSERT_TRUE(clock.can_update(0));\n  ASSERT_TRUE(clock.can_update(1));\n\n  // update input 1 past the current slot\n  ASSERT_EQ(clock.update(1, timestamp + TIMESTAMP_STEP), 0);\n\n  // can't advance until input 0 advances\n  ASSERT_EQ(clock.advance(), false);\n\n  ASSERT_TRUE(clock.can_update(0));\n  ASSERT_FALSE(clock.can_update(1));\n  ASSERT_NE(clock.update(1, timestamp + TIMESTAMP_STEP), 0);\n\n  // advance input 0 and the clock\n  ASSERT_EQ(clock.update(0, timestamp + TIMESTAMP_STEP), 0);\n  ASSERT_EQ(clock.advance(), true);\n\n  // inputs are in sync\n  ASSERT_TRUE(clock.is_current(0));\n  ASSERT_TRUE(clock.is_current(1));\n  ASSERT_TRUE(clock.can_update(0));\n  ASSERT_TRUE(clock.can_update(1));\n}\n\nTEST(virtual_clock, initial_slot_min)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  clock.update(0, TIMESTAMP_STEP * (TIMESLOT_MIN + 42));\n  clock.update(1, TIMESTAMP_STEP * (TIMESLOT_MIN + 43));\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MIN + 42);\n}\n\nTEST(virtual_clock, initial_slot_mid)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  const auto TIMESLOT_MID = TIMESLOT_MAX / 2;\n\n  clock.update(0, TIMESTAMP_STEP * (TIMESLOT_MID - 10));\n  clock.update(1, TIMESTAMP_STEP * (TIMESLOT_MID + 10));\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MID - 10);\n}\n\nTEST(virtual_clock, initial_slot_wrap)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  clock.update(0, TIMESTAMP_STEP * TIMESLOT_MAX);\n  clock.update(1, TIMESTAMP_STEP * (TIMESLOT_MAX + 1));\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MAX);\n}\n\nTEST(virtual_clock, initial_slot_wrap_2)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  const auto TIMESLOT_MID = TIMESLOT_MAX / 2;\n\n  clock.update(0, TIMESTAMP_STEP * (TIMESLOT_MID + 10));\n  clock.update(1, TIMESTAMP_STEP * (TIMESLOT_MAX + 1));\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MID + 10);\n}\n\nTEST(virtual_clock, initial_slot_wrap_3)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  const auto TIMESLOT_MID = TIMESLOT_MAX / 2;\n\n  clock.update(0, TIMESTAMP_STEP * (TIMESLOT_MID - 10));\n  clock.update(1, TIMESTAMP_STEP * (TIMESLOT_MAX + 1));\n  clock.advance();\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MIN);\n}\n\nTEST(virtual_clock, advance)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = 0;\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp);\n\n  ASSERT_EQ(clock.advance(), false);\n\n  timestamp += TIMESTAMP_STEP;\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp);\n\n  ASSERT_EQ(clock.advance(), true);\n}\n\nTEST(virtual_clock, advance_catchup)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = TIMESTAMP_STEP * 42;\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp + 2 * TIMESTAMP_STEP);\n\n  ASSERT_EQ(clock.advance(), false);\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), 42);\n\n  clock.update(0, timestamp + TIMESTAMP_STEP);\n  ASSERT_EQ(clock.advance(), true);\n  ASSERT_EQ(clock.current_timeslot().value(), 43);\n\n  clock.update(0, timestamp + 2 * TIMESTAMP_STEP);\n  ASSERT_EQ(clock.advance(), true);\n  ASSERT_EQ(clock.current_timeslot().value(), 44);\n\n  ASSERT_TRUE(clock.is_current(0));\n  ASSERT_TRUE(clock.is_current(1));\n}\n\nTEST(virtual_clock, advance_skipslots)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = TIMESTAMP_STEP * 42;\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp + 2 * TIMESTAMP_STEP);\n  ASSERT_EQ(clock.advance(), false);\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), 42);\n\n  clock.update(0, timestamp + 2 * TIMESTAMP_STEP);\n  ASSERT_EQ(clock.advance(), true);\n  ASSERT_EQ(clock.current_timeslot().value(), 44);\n\n  ASSERT_TRUE(clock.is_current(0));\n  ASSERT_TRUE(clock.is_current(1));\n}\n\nTEST(virtual_clock, advance_wraparound)\n{\n  VirtualClock clock = DEFAULT_CLOCK;\n  clock.add_inputs(2);\n\n  u64 timestamp = TIMESTAMP_STEP * TIMESLOT_MAX;\n\n  clock.update(0, timestamp);\n  clock.update(1, timestamp);\n  ASSERT_EQ(clock.advance(), false);\n\n  ASSERT_TRUE(clock.current_timeslot().has_value());\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MAX);\n\n  timestamp += TIMESTAMP_STEP;\n\n  clock.update(0, timestamp);\n  ASSERT_EQ(clock.advance(), false);\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MAX);\n\n  clock.update(1, timestamp);\n  ASSERT_EQ(clock.advance(), true);\n  ASSERT_EQ(clock.current_timeslot().value(), TIMESLOT_MIN);\n\n  ASSERT_TRUE(clock.is_current(0));\n  ASSERT_TRUE(clock.is_current(1));\n}\n"
  },
  {
    "path": "reducer/worker.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <reducer/worker.h>\n\n#include <channel/callbacks.h>\n#include <channel/tcp_channel.h>\n#include <reducer/ingest/component.h>\n#include <reducer/util/thread_ops.h>\n#include <util/defer.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/uv_helpers.h>\n\n#include <absl/container/node_hash_map.h>\n#include <absl/synchronization/mutex.h>\n#include <absl/synchronization/notification.h>\n#include <uv.h>\n\n#include <iostream>\n#include <memory>\n#include <thread>\n#include <unistd.h>\n#include <vector>\n\nnamespace reducer {\n\nnamespace {\n\n// Decorator class for the user-provided Callbacks to provide some Worker\n// class state management.\nclass WorkerCallbacksDecorator : public ::channel::Callbacks {\npublic:\n  WorkerCallbacksDecorator(\n      std::unique_ptr<::channel::Callbacks> underlying_callbacks,\n      std::function<void()> close_cb,\n      ::channel::TCPChannel *const tcp_channel)\n      : underlying_callbacks_(std::move(underlying_callbacks)), close_cb_(std::move(close_cb)), tcp_channel_(tcp_channel)\n  {}\n\n  ~WorkerCallbacksDecorator() override = default;\n\n  uint32_t received_data(const uint8_t *const data, const int data_len) override\n  {\n    return underlying_callbacks_->received_data(data, data_len);\n  }\n\n  void on_error(int err) override\n  {\n    underlying_callbacks_->on_error(err);\n\n    // UV_EOF indicates that the connection has closed.\n    if (err == UV_EOF) {\n      tcp_channel_->close_permanently();\n    }\n  }\n\n  void on_closed() override\n  {\n    underlying_callbacks_->on_closed();\n    close_cb_();\n  }\n\n  void on_connect() override { underlying_callbacks_->on_connect(); }\n\n  ::channel::Callbacks *underlying_callbacks() { return underlying_callbacks_.get(); }\n\nprivate:\n  std::unique_ptr<::channel::Callbacks> underlying_callbacks_;\n  const std::function<void()> close_cb_;\n  ::channel::TCPChannel *const tcp_channel_;\n};\n\n} // namespace\n\nWorker::Worker()\n{\n  // Initialize the uv loop.\n  CHECK_UV(uv_loop_init(&loop_));\n\n  // Initialize the async for opening tcp sockets.\n  CHECK_UV(uv_async_init(&loop_, &open_tcp_socks_async_, &Worker::open_tcp_socks_async_cb));\n  open_tcp_socks_async_.data = this;\n\n  // Initialize the stopping async.\n  CHECK_UV(uv_async_init(&loop_, &stop_async_, &Worker::stop_async_cb));\n  stop_async_.data = this;\n\n  // Initialize the visitor async.\n  CHECK_UV(uv_async_init(&loop_, &visit_async_, &Worker::visit_async_cb));\n  visit_async_.data = this;\n}\n\nWorker::~Worker()\n{\n  stop();\n}\n\nvoid Worker::start(std::size_t thread_num)\n{\n  // Start the main thread.\n  thread_ = std::thread([this, thread_num] {\n    set_self_thread_name(fmt::format(\"ingest_{}\", thread_num)).on_error([=](auto const &error) {\n      LOG::warn(\"unable to set name for ingest core worker thread {}: {}\", thread_num, error);\n    });\n    on_thread_start();\n    thread_started_.Notify();\n    uv_run(&loop_, UV_RUN_DEFAULT);\n    close_uv_loop_cleanly(&loop_);\n    on_thread_stop();\n  });\n  thread_started_.WaitForNotification();\n  started_ = true;\n\n  LOG::trace_in(ingest::Component::worker, \"Worker {:p} started\", (void *)this);\n}\n\nvoid Worker::stop()\n{\n  if (!started_) {\n    return;\n  }\n\n  CHECK_UV(uv_async_send(&stop_async_));\n  thread_.join();\n  started_ = false;\n}\n\nvoid Worker::assign(const uv_tcp_t &tcp_conn)\n{\n  // Verify that start() has been called.\n  if (!started_) {\n    LOG::critical(\"Make sure to call Worker::start() before accepting tcp connections\");\n    std::exit(1);\n  }\n\n  // Verify that the socket descriptor the a file descriptor. This should be\n  // true on all unix-based OSs, so this should not fail (unless on Windows or\n  // something...)\n  static_assert(\n      std::is_same<uv_os_fd_t, uv_os_sock_t>::value, \"The socket descriptor must be the same type as a file descriptor\");\n  uv_os_sock_t fd;\n  CHECK_UV(uv_fileno(reinterpret_cast<const uv_handle_t *>(&tcp_conn), reinterpret_cast<uv_os_fd_t *>(&fd)));\n\n  // Duplicate the file descriptor and add it to the fd queue.\n  const uv_os_sock_t fd_dupe = dup(fd);\n  {\n    absl::MutexLock l(&mu_);\n    tcp_sock_fds_.push_back(fd_dupe);\n  }\n\n  // Tell the uv loop to open the TCP connection.\n  CHECK_UV(uv_async_send(&open_tcp_socks_async_));\n\n  LOG::trace_in(\n      ingest::Component::worker, \"Worker {:p}: assigned file descriptor {}\", (void *)this, reinterpret_cast<int>(fd_dupe));\n}\n\nstd::shared_ptr<absl::Notification> Worker::visit_thread(std::function<void()> cb)\n{\n  // Verify that start() has been called.\n  if (!started_) {\n    LOG::critical(\"Make sure to call Worker::start() before visiting the thread.\");\n    std::terminate();\n  }\n\n  // Enqueue the visitor.\n  const auto done = std::make_shared<absl::Notification>();\n  {\n    absl::MutexLock l(&mu_);\n    visitors_.push_back(Visitor{\n        .cb = std::move(cb),\n        .done = done,\n    });\n  }\n\n  // Trigger the async.\n  CHECK_UV(uv_async_send(&visit_async_));\n\n  return done;\n}\n\nstd::shared_ptr<absl::Notification> Worker::visit_callbacks(CallbacksCb cb)\n{\n  return visit_thread([this, captured_cb = std::move(cb)] {\n    for (auto &kv : tcp_channel_to_payload_) {\n      auto *const callbacks_decorator = static_cast<WorkerCallbacksDecorator *>(kv.second.callbacks.get());\n      captured_cb(callbacks_decorator->underlying_callbacks());\n    }\n  });\n}\n\nstd::unique_ptr<channel::Callbacks> Worker::create_callbacks(uv_loop_t &loop, ::channel::TCPChannel * /* unused */)\n{\n  return std::make_unique<channel::Callbacks>();\n}\n\nvoid Worker::open_tcp_socks_async_cb(uv_async_t *const handle)\n{\n  auto *const worker = reinterpret_cast<Worker *>(handle->data);\n\n  // Get the pending list of tcp sockets file descriptors.\n  std::vector<uv_os_sock_t> tcp_sock_fds;\n  {\n    absl::MutexLock l(&worker->mu_);\n    std::swap(worker->tcp_sock_fds_, tcp_sock_fds);\n  }\n\n  // Create a new tcp connection for each socket fd.\n  for (const uv_os_sock_t &fd : tcp_sock_fds) {\n    TcpPayload payload;\n\n    // Instantiate the TCP channel.\n    payload.tcp_channel = std::make_unique<::channel::TCPChannel>(worker->loop_);\n    auto *const tcp_channel_ptr = payload.tcp_channel.get();\n\n    // Create the callbacks.\n    payload.callbacks = std::make_unique<WorkerCallbacksDecorator>(\n        worker->create_callbacks(worker->loop_, tcp_channel_ptr),\n        [worker, tcp_channel_ptr] { worker->tcp_channel_to_payload_.erase(tcp_channel_ptr); },\n        tcp_channel_ptr);\n\n    // Store the payload.\n    TcpPayload *const payload_ptr = &worker->tcp_channel_to_payload_.emplace(tcp_channel_ptr, std::move(payload)).first->second;\n\n    payload_ptr->callbacks->on_connect();\n\n    // Start accepting messages.\n    tcp_channel_ptr->open_fd(*payload_ptr->callbacks, fd);\n  }\n}\n\nvoid Worker::stop_async_cb(uv_async_t *const handle)\n{\n  auto *const worker = reinterpret_cast<Worker *>(handle->data);\n  uv_stop(&worker->loop_);\n}\n\nvoid Worker::visit_async_cb(uv_async_t *const handle)\n{\n  auto *const worker = reinterpret_cast<Worker *>(handle->data);\n  worker->invoke_visitors();\n}\n\nvoid Worker::invoke_visitors()\n{\n  // NOTE: calling empty() here outside of a mutex lock is acceptable.\n  if (visitors_.empty()) {\n    return;\n  }\n\n  // Get the list of pending visitors.\n  std::vector<Visitor> visitors;\n  {\n    absl::MutexLock l(&mu_);\n    std::swap(visitors, visitors_);\n  }\n\n  // Invoke each and notify the corresponding `visit_thread` calls to unblock.\n  for (Visitor &visitor : visitors) {\n    visitor.cb();\n    visitor.done->Notify();\n  }\n}\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/worker.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include \"channel/callbacks.h\"\n#include \"channel/tcp_channel.h\"\n\n#include \"absl/base/thread_annotations.h\"\n#include <absl/container/node_hash_map.h>\n#include <absl/synchronization/mutex.h>\n#include <absl/synchronization/notification.h>\n\n#include <uv.h>\n\n#include <memory>\n#include <thread>\n#include <vector>\n\nnamespace reducer {\n\n// This class is responsible for accepting TCP connections (presumably from a\n// libuv-based TCP server) and running them on a different `uv_loop_t`\n// instance. This can be used to distribute the work of several TCP connections\n// across multiple threads.\nclass Worker {\npublic:\n  // The size of the internally allocated buffer in which messages are stored.\n  static const std::size_t kBufferSize = 64 << 10; // 64 KiB\n\n  Worker();\n  virtual ~Worker();\n\n  // Starts the event-processing thread for this worker\n  void start(std::size_t thread_num);\n\n  // Stops the currently-running thread, and blocks until the stop has\n  // completed.\n  void stop();\n\n  // Assigns the provided TCP connection to this worker. This function\n  // duplicates the connections file descriptor, and it is the responsibility of\n  // the caller to close the original connection. `uv_accept` must already have\n  // been called on `tcp_conn` before passing it here.\n  // Requires that `start()` was already called.\n  void assign(const uv_tcp_t &tcp_conn);\n\n  // Invokes the provided callback in the context of this class's worker thread.\n  // Can be used to inspect thread-local values for this class's owned thread.\n  // Must not be invoked from within this class's thread itself.\n  // Requires that `start()` was already called.\n  // This function will run asynchronously, use the returned notification for\n  // blocking. For example:\n  //\n  //   Worker* worker = ...;\n  //   worker->visit_thread([] {\n  //     /* Do stuff in the worker thread context */\n  //   })->WaitForNotification();\n  //\n  [[nodiscard]] std::shared_ptr<absl::Notification> visit_thread(std::function<void()> cb);\n\n  // Same as above, but invokes the provided callback on the callbacks of each\n  // of this class's connections.\n  using CallbacksCb = std::function<void(::channel::Callbacks *)>;\n  [[nodiscard]] std::shared_ptr<absl::Notification> visit_callbacks(CallbacksCb cb);\n\n  // Invokes all registered visitor callbacks.\n  void invoke_visitors();\n\nprotected:\n  // Invoked when the internal thread has been started or before it stops.\n  // Each of these will be invoked within the thread itself.\n  virtual void on_thread_start() {}\n  virtual void on_thread_stop() {}\n\n  // Returns a set of callbacks to be invoked. Each time a new connection\n  // arrives it will be assigned a new set of callbacks provided by this\n  // function.\n  virtual std::unique_ptr<channel::Callbacks> create_callbacks(uv_loop_t &loop, ::channel::TCPChannel *tcp_channel);\n\nprivate:\n  // Callbacks used by libuv.\n  static void open_tcp_socks_async_cb(uv_async_t *handle);\n  static void stop_async_cb(uv_async_t *handle);\n  static void visit_async_cb(uv_async_t *handle);\n\n  // The contents of the `data` pointer in a uv_tcp_t object.\n  struct TcpPayload {\n    std::unique_ptr<::channel::TCPChannel> tcp_channel;\n    std::unique_ptr<channel::Callbacks> callbacks;\n  };\n\n  // The queued entry for `visit_thread` calls.\n  struct Visitor {\n    std::function<void()> cb;\n    std::shared_ptr<absl::Notification> done;\n  };\n\n  // The loop that accepts messages on behalf of this worker.\n  uv_loop_t loop_;\n\n  // The thread that runs `loop_`.\n  std::thread thread_;\n\n  // A mapping of each tcp connection to its payload.\n  absl::node_hash_map<::channel::TCPChannel *, TcpPayload> tcp_channel_to_payload_;\n\n  // Various fields that deal with the queueing and processing of\n  // newly-assigned TCP sockets.\n  uv_async_t open_tcp_socks_async_;\n  std::vector<uv_os_sock_t> tcp_sock_fds_ ABSL_GUARDED_BY(mu_);\n  mutable absl::Mutex mu_;\n\n  // The queue of visitors.\n  uv_async_t visit_async_;\n  std::vector<Visitor> visitors_ ABSL_GUARDED_BY(mu_);\n\n  // The async used to stop this thread.\n  uv_async_t stop_async_;\n\n  // Notification to determine when the worker thread has begun.\n  bool started_ = false;\n  absl::Notification thread_started_;\n};\n\n} // namespace reducer\n"
  },
  {
    "path": "reducer/write_metrics.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config.h>\n\n#include \"disabled_metrics.h\"\n#include \"metric_info.h\"\n#include \"outbound_metrics.h\"\n#include \"tsdb_formatter.h\"\n\n#include <generated/ebpf_net/metrics.h>\n\nnamespace reducer {\n\n#define WRITE(metric_info, value)                                                                                              \\\n  if (!disabled_metrics.is_metric_disabled(metric_info.metric)) {                                                              \\\n    formatter.write(metric_info, value, writer);                                                                               \\\n  }\n\ninline double divide(double dividend, u32 divisor)\n{\n  return divisor ? dividend / divisor : 0.0;\n}\n\ninline void write_metrics(\n    ebpf_net::metrics::tcp_metrics const &m,\n    Publisher::WriterPtr const &writer,\n    TsdbFormatter &formatter,\n    const DisabledMetrics &disabled_metrics)\n{\n  if (disabled_metrics.is_metric_group_disabled<TcpMetrics>())\n    return;\n\n  double sum_srtt = double(m.sum_srtt) / 8 / 1'000'000; // RTTs are measured in units of 1/8 microseconds.\n\n  WRITE(TcpMetricInfo::bytes, m.sum_bytes);\n  WRITE(TcpMetricInfo::rtt_num_measurements, m.active_rtts);\n  WRITE(TcpMetricInfo::active, m.active_sockets);\n  WRITE(TcpMetricInfo::rtt_average, divide(sum_srtt, m.active_rtts));\n  WRITE(TcpMetricInfo::packets, m.sum_delivered);\n  WRITE(TcpMetricInfo::retrans, m.sum_retrans);\n  WRITE(TcpMetricInfo::syn_timeouts, m.syn_timeouts);\n  WRITE(TcpMetricInfo::new_sockets, m.new_sockets);\n  WRITE(TcpMetricInfo::resets, m.tcp_resets);\n}\n\ninline void write_metrics(\n    ebpf_net::metrics::udp_metrics const &m,\n    Publisher::WriterPtr const &writer,\n    TsdbFormatter &formatter,\n    const DisabledMetrics &disabled_metrics)\n{\n  if (disabled_metrics.is_metric_group_disabled<UdpMetrics>())\n    return;\n\n  WRITE(UdpMetricInfo::bytes, m.bytes);\n  WRITE(UdpMetricInfo::packets, m.packets);\n  WRITE(UdpMetricInfo::active, m.active_sockets);\n  WRITE(UdpMetricInfo::drops, m.drops);\n}\n\ninline void write_metrics(\n    ebpf_net::metrics::dns_metrics const &m,\n    Publisher::WriterPtr const &writer,\n    TsdbFormatter &formatter,\n    const DisabledMetrics &disabled_metrics)\n{\n  if (disabled_metrics.is_metric_group_disabled<DnsMetrics>())\n    return;\n\n  double dns_sum_total_time = double(m.sum_total_time_ns) / 1'000'000'000;\n  double dns_sum_processing_time = double(m.sum_processing_time_ns) / 1'000'000'000;\n\n  WRITE(DnsMetricInfo::client_duration_average, divide(dns_sum_total_time, m.responses));\n  WRITE(DnsMetricInfo::server_duration_average, divide(dns_sum_processing_time, m.responses));\n  WRITE(DnsMetricInfo::active_sockets, m.active_sockets);\n  WRITE(DnsMetricInfo::responses, m.responses);\n  WRITE(DnsMetricInfo::timeouts, m.timeouts);\n}\n\ninline void write_metrics(\n    ebpf_net::metrics::http_metrics const &m,\n    Publisher::WriterPtr const &writer,\n    TsdbFormatter &formatter,\n    const DisabledMetrics &disabled_metrics)\n{\n  if (disabled_metrics.is_metric_group_disabled<HttpMetrics>())\n    return;\n\n  double http_sum_total_time = double(m.sum_total_time_ns) / 1'000'000'000;\n  double http_sum_processing_time = double(m.sum_processing_time_ns) / 1'000'000'000;\n\n  WRITE(HttpMetricInfo::client_duration_average, divide(http_sum_total_time, m.active_sockets));\n  WRITE(HttpMetricInfo::server_duration_average, divide(http_sum_processing_time, m.active_sockets));\n  WRITE(HttpMetricInfo::active_sockets, m.active_sockets);\n\n  static constexpr std::string_view status_code_label = \"status_code\";\n\n  formatter.assign_label(status_code_label, \"200\");\n  WRITE(HttpMetricInfo::status_code, m.sum_code_200);\n\n  formatter.assign_label(status_code_label, \"400\");\n  WRITE(HttpMetricInfo::status_code, m.sum_code_400);\n\n  formatter.assign_label(status_code_label, \"500\");\n  WRITE(HttpMetricInfo::status_code, m.sum_code_500);\n\n  formatter.assign_label(status_code_label, \"other\");\n  WRITE(HttpMetricInfo::status_code, m.sum_code_other);\n\n  formatter.remove_label(status_code_label);\n}\n\n#undef WRITE\n\n// The following functions are used to write metrics as flow logs.\ninline void\nwrite_flow_log(ebpf_net::metrics::tcp_metrics const &metrics, TsdbFormatter &formatter, const DisabledMetrics &disabled_metrics)\n{\n  if (disabled_metrics.is_metric_group_disabled<TcpMetrics>())\n    return;\n\n  formatter.write_flow_log(metrics);\n}\n\ninline void\nwrite_flow_log(ebpf_net::metrics::udp_metrics const &metrics, TsdbFormatter &formatter, const DisabledMetrics &disabled_metrics)\n{}\n\ninline void\nwrite_flow_log(ebpf_net::metrics::dns_metrics const &metrics, TsdbFormatter &formatter, const DisabledMetrics &disabled_metrics)\n{}\n\ninline void write_flow_log(\n    ebpf_net::metrics::http_metrics const &metrics, TsdbFormatter &formatter, const DisabledMetrics &disabled_metrics)\n{}\n\n} // namespace reducer\n"
  },
  {
    "path": "release-please-config.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n  \"bootstrap-sha\": \"\",\n  \"include-v-in-tag\": true,\n  \"bump-minor-pre-major\": true,\n  \"bump-patch-for-minor-pre-major\": true,\n  \"changelog-path\": \"CHANGELOG.md\",\n  \"changelog-sections\": [\n    { \"type\": \"feat\",       \"section\": \"Features\" },\n    { \"type\": \"fix\",        \"section\": \"Bug Fixes\" },\n    { \"type\": \"perf\",       \"section\": \"Performance\" },\n    { \"type\": \"revert\",     \"section\": \"Reverts\" },\n    { \"type\": \"deps\",       \"section\": \"Dependencies\", \"hidden\": false },\n    { \"type\": \"docs\",       \"section\": \"Documentation\", \"hidden\": false },\n    { \"type\": \"ci\",         \"section\": \"CI\", \"hidden\": false },\n    { \"type\": \"chore\",      \"section\": \"Chores\", \"hidden\": true },\n    { \"type\": \"refactor\",   \"section\": \"Refactoring\", \"hidden\": false },\n    { \"type\": \"test\",       \"section\": \"Tests\", \"hidden\": true },\n    { \"type\": \"build\",      \"section\": \"Build\", \"hidden\": true }\n  ],\n  \"packages\": {\n    \".\": {\n      \"release-type\": \"simple\",\n      \"changelog-path\": \"CHANGELOG.md\",\n      \"version-file\": \"VERSION\"\n    }\n  }\n}\n"
  },
  {
    "path": "render/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nget_property(\n  RENDER_COMPILER\n  TARGET\n    render_compiler\n  PROPERTY\n    RENDER_COMPILER_PATH\n)\nrender_compile(\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  PACKAGE\n    ebpf_net\n  APPS\n    agent_internal\n    kernel_collector\n    cloud_collector\n    ingest\n    matching\n    aggregation\n    logging\n  COMPILER\n    ${RENDER_COMPILER}\n  OUTPUT_DIR\n    \"${CMAKE_BINARY_DIR}/generated\"\n  DEPENDS\n    render_compiler\n)\n\nadd_library(\n  render_pipeline\n  INTERFACE\n)\ntarget_link_libraries(\n  render_pipeline\n  INTERFACE\n    render_ebpf_net_ingest\n    render_ebpf_net_matching\n    render_ebpf_net_aggregation\n    render_ebpf_net_logging\n)\n"
  },
  {
    "path": "render/ebpf_net.render",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\npackage ebpf_net\n\n/* RPC ID ranges.\n *\n * NOTE: when modifying, make sure that existing IDs stay the same\n *       (add IDs higher than previously used, add at the end of the list).\n * RPC IDs are allocated here, but mapped over the list of contiguous app message ids\n * for each message. 300 is the first allocated RPC id per historical convention.\n */\nnamespace {\n  ingest: 301-310,321-330,341,350-360,390-420,491-520,531-550\n  agent_internal: 331-340,361-380\n  matching: 421-440,471-490\n  kernel_collector: 521-530\n  cloud_collector: 441-450\n  aggregation: 451-470\n  logging: 600-699\n}\n\n/******************************************************************************\n * APP SPANS\n ******************************************************************************/\napp agent_internal {\n  span agent_internal\n    impl \"ebpf_net::agent_internal::AgentInternalSpanBase\"\n    include \"<generated/ebpf_net/agent_internal/span_base.h>\"\n  {\n    pool_size 1\n    singleton\n    0: log dns_packet {\n      description \"a DNS packet sent or received by an application\"\n      severity 0\n      1: u64 sk\n      2: string pkt\n      3: u16 total_len\n      4: u8 is_rx                 // 0 = sent, 1 = received\n    }\n    1: log reset_tcp_counters {\n      description \"new_socket and the sk has the following counters\"\n      severity 0\n      1: u64 sk\n      2: u64 bytes_acked\n      3: u32 packets_delivered\n      4: u32 packets_retrans\n      5: u64 bytes_received\n      6: u32 pid\n    }\n    2: log new_sock_created {\n      description \"new socket has been created\"\n      severity 0\n      1: u32 pid\n      2: u64 sk\n    }\n    3: log udp_new_socket {\n      description \"found new or scanned existing udp socket\"\n      severity 0\n      1: u32 pid\n      2: u64 sk\n      3: u8 laddr[16]\n      4: u16 lport\n    }\n    4: log udp_destroy_socket {\n      description \"udp socket was destroyed\"\n      severity 0\n      1: u64 sk\n    }\n    5: log udp_stats {\n      description \"udp socket sent datagrams\"\n      severity 0\n      1: u64 sk\n      2: u8 raddr[16]\n      3: u32 packets\n      4: u32 bytes\n      5: u8 changed_af\n      6: u16 rport\n      7: u8 is_rx\n      8: u8 laddr[16]\n      9: u16 lport\n      10: u32 drops\n    }\n    6: log pid_info {\n      description \"new process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n      3: u64 cgroup\n      4: s32 parent_pid\n    }\n    7: log pid_close {\n      description \"close process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n    }\n    20: log pid_set_comm {\n      description \"process comm change\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n    }\n    8: log set_state_ipv4 {\n      description \"socket state changed or enumerating sockets\"\n      severity 0\n      1: u32 dest\n      2: u32 src\n      3: u16 dport\n      4: u16 sport\n      5: u64 sk\n      7: u32 tx_rx // 0-unknown, 1-connector, 2-listener\n    }\n    9: log set_state_ipv6 {\n      description \"socket state changed or enumerating sockets\"\n      severity 0\n      1: u8 dest[16]\n      2: u8 src[16]\n      3: u16 dport\n      4: u16 sport\n      5: u64 sk\n      7: u32 tx_rx // 0-unknown, 1-connector, 2-listener\n    }\n    10: log rtt_estimator {\n      description \"periodic telemetry about a socket\"\n      severity 0\n      1: u32 srtt\n      2: u32 snd_cwnd\n      3: u64 bytes_acked\n      4: u8 ca_state\n      5: u64 sk\n      6: u32 packets_in_flight\n      7: u32 packets_delivered\n      8: u32 packets_retrans\n      9: u32 rcv_holes\n      10: u64 bytes_received\n      11: u32 rcv_delivered\n      12: u32 rcv_rtt\n    }\n    11: log close_sock_info {\n      description \"close socket info\"\n      severity 0\n      1: u64 sk\n    }\n    12: log kill_css {\n      description \"destroy a css\"\n      severity 0\n      1: u64 cgroup\n      2: u64 cgroup_parent\n      3: u8 name[256]\n      // Be careful: mind the 512 byte limit for the bpf stack.\n      // We need the whole name because systemd style cgroup names are long and\n      // have the container id at the end.\n    }\n    13: log css_populate_dir {\n      description \"create subsys files in a cgroup directory\"\n      severity 0\n      1: u64 cgroup\n      2: u64 cgroup_parent\n      3: u8 name[256] // See kill_css note.\n    }\n    14: log existing_cgroup_probe {\n      description \"existing_cgroup_probe used by kprobe:cgroup_clone_children_read for cgroup v1 and kprobe:cgroup_control for cgroup v2.\"\n      severity 0\n      1: u64 cgroup\n      2: u64 cgroup_parent\n      3: u8 name[256] // See kill_css note.\n    }\n    15: log cgroup_attach_task {\n      description \"cgroup_attach_task\"\n      severity 0\n      1: u64 cgroup\n      2: u32 pid\n      3: u8 comm[16]\n    }\n    // unpacks connection information from the params of this function\n    // struct nf_conn ct.tuplehash holds two nf_conntrack_tuple structs\n    // see nf_conntrack_tuple.h for more info\n    16: log nf_conntrack_alter_reply {\n      description \"nf_conntrack_alter_reply\"\n      severity 0\n      1: u64 ct\n      2: u32 src_ip // in network byte order\n      3: u16 src_port // in network byte order\n      4: u32 dst_ip // in network byte order\n      5: u16 dst_port // in network byte order\n      6: u8 proto\n      7: u32 nat_src_ip // in network byte order\n      8: u16 nat_src_port // in network byte order\n      9: u32 nat_dst_ip // in network byte order\n      10: u16 nat_dst_port // in network byte order\n      11: u8 nat_proto\n    }\n    17: log nf_nat_cleanup_conntrack {\n      description \"nf_nat_cleanup_conntrack\"\n      severity 0\n      1: u64 ct\n      2: u32 src_ip // in network byte order\n      3: u16 src_port // in network byte order\n      4: u32 dst_ip // in network byte order\n      5: u16 dst_port // in network byte order\n      6: u8 proto\n    }\n    18: log existing_conntrack_tuple {\n      description \"existing_conntrack_tuple\"\n      severity 0\n      1: u64 ct\n      2: u32 src_ip // in network byte order\n      3: u16 src_port // in network byte order\n      4: u32 dst_ip // in network byte order\n      5: u16 dst_port // in network byte order\n      6: u8 proto\n      7: u8 dir\n    }\n    19: log tcp_syn_timeout {\n      description \"tcp socket experienced a timeout in SYN_RECV or SYN_SENT state\"\n      severity 0\n      1: u64 sk\n    }\n    21: log http_response {\n      description \"http response code and latency\"\n      severity 0\n      1: u64 sk\n      2: u32 pid\n      3: u16 code           // http response code\n      4: u64 latency_ns     // in nanoseconds (client=round-trip time, server=processing time)\n      5: u8 client_server   // 0 = client, 1 = server\n    }\n    22: log bpf_log {\n      description \"log/warning/error event\"\n      severity 0\n      1: u64 filelineid // file/line debug information id\n      2: u64 code       // error/warning/message code\n      3: u64 arg0       // code-specific arguments (generic)\n      4: u64 arg1\n      5: u64 arg2\n    }\n    23: log stack_trace {\n      description \"debugging stack trace event\"\n      severity 0\n      1: s32 kernel_stack_id  // id of the kernel stack trace\n      2: s32 user_stack_id    // id of the userland stack trace\n      3: u32 tgid             // tgid\n      4: u8 comm[16]          // process command\n    }\n    24: log tcp_data {\n      description \"tcp packet data from bpf\"\n      severity 0\n      1: u64 sk               // sock\n      2: u32 pid              // tgid\n      3: u32 length           // data length\n      4: u64 offset           // offset into the stream for the data\n      5: u8 stream_type       // enum STREAM_TYPE { ST_SEND = 0, ST_RECV = 1 }\n      6: u8 client_server     // enum CLIENT_SERVER_TYPE { SC_CLIENT = 0, SC_SERVER = 1 }\n    }\n    26: log pid_exit {\n      description \"called when a thread exits\"\n      severity 0\n\n      1: u64 tgid\n      2: u32 pid\n\n      3: s32 exit_code\n    }\n    27: log report_debug_event {\n      description \"called when a bpf debug event is triggered - for debugging purposes\"\n      severity 0\n\n      1: u16 event // unique dentifying code for the debug event\n      2: u64 arg1 // event dependent data\n      3: u64 arg2 // event dependent data\n      4: u64 arg3 // event dependent data\n      5: u64 arg4 // event dependent data\n    }\n    28: log tcp_reset {\n      description \"tcp RST was sent/received\"\n      severity 0\n      1: u64 sk\n      2: u8 is_rx\n    }\n  }\n} /* app agent_internal */\n\napp ingest {\n\n  span process impl \"reducer::ingest::ProcessSpan\" include \"<reducer/ingest/process_span.h>\" {\n    pool_size 10000000\n\n    string<16> comm\n\n    reference<cgroup> cgroup\n    reference<service> service\n\n    // userspace proxy hack: cgroup of the target process\n    reference<cgroup> cgroup_override\n\n    0: start pid_info ref pid {\n      description \"new process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n    }\n    5: end pid_close_info ref pid {\n      description \"close process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n    }\n    35: start pid_info_create_deprecated ref pid {\n      description \"new process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n      3: u64 cgroup\n    }\n    108: start pid_info_create ref pid {\n      description \"new process info\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n      3: u64 cgroup\n      4: s32 parent_pid\n      5: string cmdline\n    }\n    39: log pid_cgroup_move ref pid {\n      description \"process attached to a cgroup\"\n      severity 0\n      1: u32 pid\n      3: u64 cgroup\n    }\n    41: log pid_set_comm ref pid {\n      description \"process comm changed\"\n      severity 0\n      1: u32 pid\n      2: u8 comm[16]\n    }\n    109: log pid_set_cmdline ref pid {\n      description \"process command-line changed\"\n      severity 0\n      1: u32 pid\n      2: string cmdline\n    }\n  } /* span process */\n\n  // processes proxied by the kernel collector\n  // transitional span so we can migrate from the non-proxied version\n  // gradually without breaking existing functionality\n  span tracked_process\n    impl \"ebpf_net::ingest::TrackedProcessSpanBase\"\n    include \"<generated/ebpf_net/ingest/span_base.h>\"\n  {\n    pool_size 10000000\n\n    72: msg _start {}\n    73: msg _end {}\n\n    74: msg set_tgid {\n      description \"set tgid\"\n      severity 0\n      1: u32 tgid\n    }\n\n    75: msg set_cgroup {\n      description \"set cgroup\"\n      severity 0\n      1: u64 cgroup\n    }\n\n    76: msg set_command {\n      description \"set cmd line\"\n      severity 0\n      1: string command\n    }\n\n    89: msg pid_exit {\n      description \"pid_exit\"\n      severity 0\n\n      // the TGID this PID belongs to\n      1: u64 tgid\n\n      // the PID that's exiting\n      2: u32 pid\n\n      // the exit code of the PID when it finished execution\n      3: s32 exit_code\n    }\n  } /* span tracked_process_span */\n\n  span cgroup impl \"reducer::ingest::CgroupSpan\" include \"<reducer/ingest/cgroup_span.h>\" {\n    pool_size 800000\n\n    // cgroup name prefix\n    string<256> name\n\n    // if this cgroup is a pod\n    u8 pod_uid_suffix[64]\n    u64 pod_uid_hash\n\n    u16 cpu_soft_limit // cpu shares in kernel lingo: https://elixir.bootlin.com/linux/v5.8-rc3/source/kernel/sched/fair.c#L3122\n    u32 cpu_hard_quota\n    u32 cpu_hard_period\n\n    reference<cgroup> parent\n\n    reference<service> service\n\n    reference<container> container\n\n    36: start cgroup_create_deprecated ref cgroup {\n      description \"cgroup created\"\n      severity 0\n      1: u64 cgroup\n      2: u64 cgroup_parent\n      3: u8 name[64]\n    }\n    106: start cgroup_create ref cgroup {\n      description \"cgroup created\"\n      severity 0\n      1: u64 cgroup\n      2: u64 cgroup_parent\n      3: u8 name[256]\n    }\n    37: end cgroup_close ref cgroup {\n      description \"cgroup destroyed\"\n      severity 0\n      1: u64 cgroup\n    }\n    38: log container_metadata ref cgroup {\n      description \"Container metadata\"\n      severity 0\n      1: u64 cgroup\n      2: string id\n      3: string name\n      4: string image\n      5: string ip_addr\n      6: string cluster\n      7: string container_name\n      8: string task_family\n      9: string task_version\n      10: string ns\n    }\n    52: log pod_name ref cgroup {\n      description \"The name of the pod derived from docker metadata\"\n      severity 0\n      1: u64 cgroup\n      2: string _deprecated_pod_uid\n      3: string name\n    }\n    80: log nomad_metadata ref cgroup {\n      description \"Nomad metadata extracted from the container's environment\"\n      severity 0\n      1: u64 cgroup\n      2: string ns\n      3: string group_name\n      4: string task_name\n      5: string job_name\n    }\n    84: log k8s_metadata ref cgroup {\n      description \"K8s metadata extracted from the container's labels\"\n      severity 0\n      1: u64 cgroup\n      2: string container_name\n      3: string pod_name\n      4: string pod_ns\n      5: string pod_uid\n      6: string sandbox_uid\n    }\n    85: log k8s_metadata_port ref cgroup {\n      description \"K8s ports metadata extracted from the container's labels\"\n      severity 0\n      1: u64 cgroup\n      2: u16 port\n      3: u8 protocol // as in `PortProtocol` from `common/port_protocol.h`\n      4: string name\n    }\n    86: log container_resource_limits_deprecated ref cgroup {\n      description \"container resource limits\"\n      severity 0\n      1: u64 cgroup\n      2: u16 cpu_shares\n      3: u16 cpu_period\n      4: u16 cpu_quota\n      5: u8 memory_swappiness\n      6: u64 memory_limit\n      7: u64 memory_soft_limit\n      8: s64 total_memory_limit\n    }\n    90: log container_resource_limits ref cgroup {\n      description \"container resource limits\"\n      severity 0\n      1: u64 cgroup\n      2: u16 cpu_shares\n      3: u32 cpu_period\n      4: u32 cpu_quota\n      5: u8 memory_swappiness\n      6: u64 memory_limit\n      7: u64 memory_soft_limit\n      8: s64 total_memory_limit\n    }\n    100: log container_annotation ref cgroup {\n      description \"Container annotation from `Config.Labels`\"\n      severity 0\n      1: u64 cgroup\n      2: string key\n      3: string value\n    }\n  } /* span cgroup */\n\n  span socket impl \"reducer::ingest::SocketSpan\" include \"<reducer/ingest/socket_span.h>\" {\n    pool_size 5000000\n\n    reference<process> process\n\n    1: start new_sock_info ref sk {\n      description \"new socket info\"\n      severity 0\n      1: u32 pid\n      2: u64 sk\n    }\n    2: log set_state_ipv4 ref sk {\n      description \"socket state changed or enumerating sockets\"\n      severity 0\n      1: u32 dest\n      2: u32 src\n      3: u16 dport\n      4: u16 sport\n      5: u64 sk\n      7: u32 tx_rx\n    }\n    3: log set_state_ipv6 ref sk {\n      description \"socket state changed or enumerating sockets\"\n      severity 0\n      1: u8 dest[16]\n      2: u8 src[16]\n      3: u16 dport\n      4: u16 sport\n      5: u64 sk\n      7: u32 tx_rx\n    }\n    15: log socket_stats ref sk {\n      description \"aggregated statistics for the socket\"\n      severity 0\n      1: u64 sk\n      2: u64 diff_bytes\n      3: u32 diff_delivered\n      4: u32 diff_retrans\n      5: u32 max_srtt\n      6: u8 is_rx\n    }\n    31: log nat_remapping ref sk {\n      description \"NAT remapping for a connection\"\n      severity 0\n      1: u64 sk\n      2: u32 src\n      3: u32 dst\n      4: u16 sport\n      5: u16 dport\n    }\n    7: end close_sock_info ref sk {\n      description \"close socket info\"\n      severity 0\n      1: u64 sk\n    }\n    40: log syn_timeout ref sk {\n      description \"tcp socket experienced a timeout in SYN_RECV or SYN_SENT state\"\n      severity 0\n      1: u64 sk\n    }\n    43: log http_response ref sk {\n      description \"http response code and latency\"\n      severity 0\n      1: u64 sk\n      2: u32 pid\n      3: u16 code           // http response code\n      4: u64 latency_ns     // in nanoseconds (client=round-trip time, server=processing time)\n      5: u8 client_server   // 0 = client, 1 = server\n    }\n    91: log tcp_reset ref sk {\n      description \"tcp RST was sent/received\"\n      severity 0\n      1: u64 sk\n      2: u8 is_rx\n    }\n  } /* span socket */\n\n  span agent impl \"reducer::ingest::AgentSpan\" include \"<reducer/ingest/agent_span.h>\" {\n    pool_size 512 /* See core.h CONN_POOL_SIZE */\n    singleton\n    6: log process_steady_state {\n      description \"sent when we reach steady state for processes\"\n      severity 0\n      1: u64 time\n    }\n    8: log socket_steady_state {\n      description \"sent when we reach steady state for sockets\"\n      severity 0\n      1: u64 time\n    }\n    9: log version_info {\n      description \"reported to the server when the agent starts\"\n      severity 0\n      no_authorization_needed\n      pipeline_only\n\n      1: u32 major\n      2: u32 minor\n      3: u32 patch\n    }\n    57: log set_node_info {\n      description \"reports node information from agent\"\n      severity 0\n      1: string az\n      2: string role\n      3: string instance_id\n      4: string instance_type\n    }\n    58: log set_config_label {\n      description \"report a custom label\"\n      severity 0\n      1: string key\n      2: string value\n    }\n    10: log set_availability_zone_deprecated {\n      description \"reports availability zone\"\n      severity 0\n      1: u8 retcode\n      2: u8 az[16]\n    }\n    11: log set_iam_role_deprecated {\n      description \"reports cloud platform IAM role\"\n      severity 0\n      1: u8 retcode\n      2: u8 role[64]\n    }\n    12: log set_instance_id_deprecated {\n      description \"reports cloud platform instance name, the 17 char hex notation\"\n      severity 0\n      1: u8 retcode\n      2: u8 id[17]\n    }\n    13: log set_instance_type_deprecated {\n      description \"reports cloud platform instance type\"\n      severity 0\n      1: u8 retcode\n      2: u8 val[17]\n    }\n    14: log dns_response_fake {\n      description \"Used to manually set a DNS entry at the server\"\n      severity 0\n      1: u16 total_dn_len\n      2: string ips\n      3: string domain_name\n    }\n    33: log dns_response_dep_a_deprecated {\n      description \"The domain and information from a DNS response\"\n      severity 0\n      1: u16 total_dn_len\n      2: string domain_name\n      3: string ipv4_addrs\n      4: string ipv6_addrs\n    }\n    16: log set_config_label_deprecated {\n      description \"report a label specified in the config file\"\n      severity 0\n      1: u8 key[20]\n      2: u8 val[40]\n    }\n    23: log api_key {\n      description \"the api key\"\n      severity 0\n      no_authorization_needed\n      pipeline_only\n\n      1: u8 tenant[20]\n      2: u8 api_key[64]\n    }\n    24: log private_ipv4_addr {\n      description \"a private ipv4 address\"\n      severity 0\n      1: u32 addr // in network byte order\n      2: u8 vpc_id[22]\n    }\n    25: log ipv6_addr {\n      description \"an ipv6 address\"\n      severity 0\n      1: u8 addr[16]\n      2: u8 vpc_id[22]\n    }\n    26: log public_to_private_ipv4 {\n      description \"the mapping of a public to private ipv4 address\"\n      severity 0\n      1: u32 public_addr // in network byte order\n      2: u32 private_addr // in network byte order\n      3: u8 vpc_id[22]\n    }\n    27: log metadata_complete {\n      description \"sent when we've finished sending all the agent's metadata\"\n      severity 0\n      1: u64 time\n    }\n    28: log bpf_lost_samples {\n      description \"perf reported that samples were lost\"\n      severity 1\n      pipeline_only\n\n      1: u64 count\n    }\n    29: log pod_new_legacy {\n      description \"New POD\"\n      severity 0\n      1: string uid\n      2: u32 ip\n      3: string owner_name\n      4: u8 owner_kind\n      5: string owner_uid\n      6: u8 is_host_network\n      // namespace of the pod\n      7: string ns\n    }\n    56: log pod_new_legacy2 {\n      description \"New POD\"\n      severity 0\n      1: string uid\n      2: u32 ip\n      3: string owner_name\n      4: u8 owner_kind\n      5: string owner_uid\n      6: u8 is_host_network\n      // namespace of the pod\n      7: string ns\n      8: string version\n    }\n    87: log pod_new_with_name {\n      description \"New POD\"\n      severity 0\n      1: string uid\n      2: u32 ip\n      3: string owner_name\n      4: string pod_name\n      5: u8 owner_kind\n      6: string owner_uid\n      7: u8 is_host_network\n      8: string ns\n      9: string version\n    }\n    42: log pod_container_legacy {\n      description \"POD has a container\"\n      severity 0\n      1: string uid\n      2: string container_id\n    }\n    66: log pod_container {\n      description \"POD has a container\"\n      severity 0\n      1: string uid\n      2: string container_id\n      3: string container_name\n      4: string container_image\n    }\n    30: log pod_delete {\n      description \"POD is deleted\"\n      severity 0\n      1: string uid\n    }\n    32: log pod_resync {\n      description \"Current live pod info is staled. Clean then up\"\n      severity 0\n      1: u64 resync_count\n    }\n    22: log span_duration_info {\n      description \"the duration and span type\"\n      severity 0\n      1: u64 duration\n    }\n    34: log heartbeat {\n      description \"heartbeat signal from agent/collector to server.\"\n      severity 0\n      pipeline_only\n    }\n    110: log connect {\n      description \"called by the agent to connect to intake\"\n      severity 0\n      no_authorization_needed\n      pipeline_only\n\n      1: u8 collector_type // ClientType enum\n      2: string hostname\n    }\n    51: log health_check {\n      description \"called to perform a health check on the server\"\n      severity 0\n      no_authorization_needed\n      pipeline_only\n\n      1: u8 client_type // ClientType enum\n      2: string origin\n    }\n\n    53: log log_message {\n      description \"log agent warnings\"\n      severity 0\n      pipeline_only\n\n      1: u8 log_level // spdlog::level::level_enum\n      2: string message\n    }\n\n    54: log agent_resource_usage {\n      description \"logs resource usage by the agent\"\n      severity 0\n      pipeline_only\n\n      1: u64 user_mode_time_us\n      2: u64 kernel_mode_time_us\n      3: u64 max_resident_set_size\n      4: u32 minor_page_faults\n      5: u32 major_page_faults\n      6: u32 block_input_count\n      7: u32 block_output_count\n      8: u32 voluntary_context_switch_count\n      9: u32 involuntary_context_switch_count\n      10: u16 cpu_usage_by_agent // per-thousand fixed point, lowest 3 digits are fractional\n      11: u16 cpu_idle // per-thousand fixed point, lowest 3 digits are fractional\n    }\n\n    55: log cloud_platform {\n      description \"logs which cloud platform the agent is running on\"\n      severity 0\n\n      1: u16 cloud_platform // as per `common/cloud_platform.h`\n    }\n\n    61: log os_info_deprecated {\n      description \"reports the os and distro under which the agent is running\"\n      severity 0\n\n      1: u8 os // as per `common/operating_system.h`\n      2: u8 flavor // as per `common/linux_distro.h`\n      3: string kernel_version\n\n      // this message is forwards compatible with support for new operating systems\n      // the field `distro` could be repurposed with, say, the OSX release or Windows version\n    }\n\n    107: log os_info {\n      description \"reports the os and distro under which the agent is running\"\n      severity 0\n\n      1: u8 os // as per `common/operating_system.h`\n      2: u8 flavor // as per `common/linux_distro.h`\n      3: string os_version\n      4: string kernel_version\n\n      // this message is forwards compatible with support for new operating systems\n      // the field `distro` could be repurposed with, say, the OSX release or Windows version\n    }\n\n    62: log kernel_headers_source {\n      description \"logs the source used to obtain kernel headers\"\n      severity 0\n      pipeline_only\n\n      1: u8 source // as per `collector/kernel/kernel_headers_source.h`\n    }\n\n    63: log entrypoint_error {\n      description \"logs errors that happen before launching the agent, at the container entrypoint\"\n      severity 0\n      pipeline_only\n\n      1: u8 error // as per `collector/kernel/entrypoint_error.h`\n    }\n\n    64: log bpf_compiled {\n      description \"tells the server that BPF was successfully compiled by the kernel collector\"\n      severity 0\n      pipeline_only\n    }\n\n    65: log begin_telemetry {\n      description \"tells the server that agent setup is complete and telemetry is about to flow\"\n      severity 0\n      pipeline_only\n    }\n\n    67: log cloud_platform_account_info {\n      description \"log under which cloud platform account id this collector is running\"\n      severity 0\n      1: string account_id\n    }\n\n    68: log collector_health {\n      description \"reports health status about collectors\"\n      severity 0\n      pipeline_only\n\n      1: u16 status // CollectorStatus enum in `common/collector_status.h`\n      2: u16 detail // extended info about the collector's status\n                    // cloud-collector: the http status code for api calls\n    }\n\n    70: log system_wide_process_settings {\n      description \"reports system-wide settings that matters for process stats\"\n      severity 0\n\n      1: u64 clock_ticks_per_second\n      2: u64 memory_page_bytes\n    }\n\n    // use cases for this message:\n    // - unexpected error condition has been met, so agent collects information and\n    //   submits to the server for further inspection\n    //\n    // NOTE: calls to this message should be rate limited to avoid network overhead\n    83: log collect_blob {\n      description \"a generic message used to collect random pieces of information from agents\"\n      severity 0\n      pipeline_only\n\n      1: u16 blob_type // as in the `CollectedBlobType` enum from `common/collected_blob_type.h`\n      2: u64 subtype // extra metadata field whose meaning varies by `type`\n      3: string metadata // some descriptive metadata about the blob\n      4: string blob // the contents of the blob\n    }\n\n    98: log report_cpu_cores {\n      1: u32 cpu_core_count\n    }\n\n    99: log bpf_log {\n      description \"reports health status about collectors\"\n      severity 0\n      pipeline_only\n\n      1: string filename\n      2: u32 line\n      2: u64 code       // error/warning/message code\n      3: u64 arg0       // code-specific arguments (generic)\n      4: u64 arg1\n      5: u64 arg2\n    }\n\n  } /* span agent */\n\n  span aws_network_interface\n    impl \"reducer::collector::cloud::AwsNetworkInterfaceSpan\"\n    include \"<reducer/ingest/aws_network_interface_span.h>\"\n  {\n    pool_size 60000 // arbitrary number, we need a better estimate\n    index (ip)\n\n    proxy matching.aws_enrichment\n\n    u128 ip\n\n    48: msg _start {}\n    49: msg _end {}\n\n    50: msg network_interface_info_deprecated {\n      description \"information about the network interface\"\n      severity 0\n      1: u8 ip_owner_id[18]\n      2: u8 vpc_id[22]\n      3: u8 az[16]\n      4: string interface_id\n      5: u16 interface_type\n      6: string instance_id\n      7: string instance_owner_id\n      8: string public_dns_name\n      9: string private_dns_name\n      10: string interface_description\n    }\n\n    59: msg network_interface_info {\n      description \"information about the network interface\"\n      severity 0\n      1: string ip_owner_id\n      2: string vpc_id\n      3: string az\n      4: string interface_id\n      5: u16 interface_type\n      6: string instance_id\n      7: string instance_owner_id\n      8: string public_dns_name\n      9: string private_dns_name\n      10: string interface_description\n    }\n  } /* span aws_network_interface */\n\n  span udp_socket impl \"reducer::ingest::UdpSocketSpan\" include \"<reducer/ingest/udp_socket_span.h>\" {\n    pool_size 120000\n\n    reference<process> process\n\n    17: start udp_new_socket ref sk_id {\n      description \"new udp socket was initialized\"\n      severity 0\n      1: u32 pid\n      2: u32 sk_id\n      3: u8 laddr[16]\n      4: u16 lport\n    }\n    19: log udp_stats_addr_unchanged ref sk_id {\n      description \"udp socket sent datagrams\"\n      severity 0\n      1: u32 sk_id\n      2: u8 is_rx\n      3: u32 packets\n      4: u32 bytes\n    }\n    20: log udp_stats_addr_changed_v4 ref sk_id {\n      description \"udp socket sent datagrams\"\n      severity 0\n      1: u32 sk_id\n      2: u8 is_rx\n      3: u32 packets\n      4: u32 bytes\n      5: u32 raddr\n      6: u16 rport\n      // TODO: Can uncomment this once we decide to support laddr info\n      //7: u32 laddr\n      //8: u16 lport\n    }\n    21: log udp_stats_addr_changed_v6 ref sk_id {\n      description \"udp socket sent datagrams\"\n      severity 0\n      1: u32 sk_id\n      2: u8 is_rx\n      3: u32 packets\n      4: u32 bytes\n      5: u8 raddr[16]\n      6: u16 rport\n      // TODO: Can uncomment this once we decide to support laddr info\n      //7: u8 laddr[16]\n      //8: u16 lport\n    }\n    44: log dns_response_dep_b ref sk_id { // deprecated\n      description \"DNS A/AAAA record query response, with name, response address(es), and latency information\"\n      severity 0\n      1: u32 sk_id\n      2: u16 total_dn_len                 // Total length of domain name without truncation (DNS_NAME_MAX_LENGTH)\n      3: string domain_name               // Domain name being queried (possibly truncated)\n      4: string ipv4_addrs                // IPv4 addresses corresponding to domain name 'dn'\n      5: string ipv6_addrs                // IPv6 addresses corresponding to domain name 'dn'\n      6: u64 latency_ns                   // Request to response latency (in ns)\n    }\n    45: log dns_timeout ref sk_id {\n      description \"A DNS A/AAAA record request timeout\"\n      severity 0\n      1: u32 sk_id\n      2: u16 total_dn_len                 // Total length of domain name without truncation (DNS_NAME_MAX_LENGTH)\n      3: string domain_name               // Domain name being queried (possibly truncated)\n      4: u64 timeout_ns                   // Timeout duration for request (in ns)\n    }\n    47: log udp_stats_drops_changed ref sk_id {\n      description \"udp socket drops\"\n      severity 0\n      1: u32 sk_id\n      2: u32 drops\n    }\n    60: log dns_response ref sk_id {\n      description \"DNS A/AAAA record query response, with name, response address(es), and latency information, with direction\"\n      severity 0\n      1: u32 sk_id\n      2: u16 total_dn_len                 // Total length of domain name without truncation (DNS_NAME_MAX_LENGTH)\n      3: string domain_name               // Domain name being queried (possibly truncated)\n      4: string ipv4_addrs                // IPv4 addresses corresponding to domain name 'dn'\n      5: string ipv6_addrs                // IPv6 addresses corresponding to domain name 'dn'\n      6: u64 latency_ns                   // Request to response total time (in ns) for clients, or processing time for servers\n      7: u8 client_server                 // 0 = client received response, 1 = server sent response\n    }\n\n    18: end udp_destroy_socket ref sk_id {\n      description \"udp socket was destroyed\"\n      severity 0\n      1: u32 sk_id\n    }\n  } /* span udp_socket */\n\n  /**\n   * An k8s_pod\n   */\n  span k8s_pod impl \"reducer::ingest::K8sPodSpan\" include \"<reducer/ingest/k8s_pod_span.h>\" {\n    pool_size 220000\n    index (uid_suffix, uid_hash)\n    proxy matching.k8s_pod\n\n    // can be obtained from docker label `io.kubernetes.pod.uid`\n    u8 uid_suffix[64]\n    // since uid's can theoretically be unbounded in length, we will use the\n    // the last 64 bytes of the uid, with the addition of a u64 which provides\n    // extra reduction in collisions\n    u64 uid_hash\n\n    // can be obtained from docker label `io.kubernetes.pod.name`??\n    string<64> owner_name   // name of the Deployment, DaemonSet, ReplicaSet or StatefulSet owning the pod\n\n    // owner uid\n    string<64> owner_uid\n\n    // can be obtained from docker label `io.kubernetes.pod.namespace`\n    string<64> ns\n\n    // can be obtained from docker label ``??\n    string<64> version\n  }\n\n  span k8s_container {\n    pool_size 600000\n    index (uid_suffix, uid_hash)\n    proxy matching.k8s_container\n\n    u8 uid_suffix[64]\n    u64 uid_hash\n\n    // can be obtained from docker label `io.kubernetes.container.name`\n    string<64> name\n\n    // can be obtained from docker label ``??\n    string<64> image\n  }\n\n  span container {\n    pool_size 500000\n    index (id)\n    string<64> id\n    string<64> name // i.e. ECS or k&s container name\n    string<80> role\n    string<64> version\n    string<64> ns\n    string<64> pod_name\n    u8 node_type // as in `NodeResolutionType`, either `K8S_CONTAINER`, `CONTAINER` or `NOMAD`\n    u32 update_count // incremented each time one or more fields are modified\n  }\n\n  span service {\n    pool_size 500000\n    index (name)\n    string<64> name\n  }\n\n  span flow {\n    pool_size 4200000\n    index (addr1, port1, addr2, port2)\n    proxy matching.flow shard_by (addr1, port1, addr2, port2)\n\n    u128 addr1\n    u16 port1\n    u128 addr2\n    u16 port2\n\n    reference<process> process1\n    reference<process> process2\n    u32 is_connector\n\n    reference<container> container1 cached {\n      id = process1.cgroup.container.id\n    }\n    reference<container> container2 cached {\n      id = process2.cgroup.container.id\n    }\n\n    // userspace proxy hack: container from process' overridden cgroup\n    //\n    reference<container> container1_override cached {\n      id = process1.cgroup_override.container.id\n    }\n    reference<container> container2_override cached {\n      id = process2.cgroup_override.container.id\n    }\n  }\n\n  span logger {\n    proxy logging.logger\n  }\n\n  span core_stats {\n    proxy logging.core_stats\n  }\n\n  span ingest_core_stats {\n    proxy logging.ingest_core_stats\n  }\n} /* app ingest */\n\napp matching {\n\n  span flow impl \"reducer::matching::FlowSpan\" include \"<reducer/matching/flow_span.h>\" {\n    pool_size 4200000\n    index (addr1, port1, addr2, port2)\n\n    aggregate tcp_a_to_b (root type tcp_metrics interval 30 slots 4)\n    aggregate tcp_b_to_a (root type tcp_metrics interval 30 slots 4)\n    aggregate http_a_to_b (root type http_metrics interval 30 slots 4)\n    aggregate http_b_to_a (root type http_metrics interval 30 slots 4)\n    aggregate udp_a_to_b (root type udp_metrics interval 30 slots 4)\n    aggregate udp_b_to_a (root type udp_metrics interval 30 slots 4)\n    aggregate dns_a_to_b (root type dns_metrics interval 30 slots 4)\n    aggregate dns_b_to_a (root type dns_metrics interval 30 slots 4)\n\n    u128 addr1\n    u16 port1\n    u128 addr2\n    u16 port2\n\n    reference<agg_root> agg_root\n\n    // the pods for either side, if enriched with pod info.\n    reference<k8s_pod> k8s_pod1\n    reference<k8s_pod> k8s_pod2\n\n    0: msg _start {}\n    1: msg _end {}\n    2: msg agent_info {\n      1: u8 side\n      2: string id\n      3: string az\n      4: string env\n      5: string role\n      6: string ns\n    }\n    3: msg task_info {\n      1: u8 side\n      2: string comm\n      3: string cgroup_name\n    }\n    4: msg socket_info {\n      1: u8 side\n      2: u8 local_addr[16]\n      3: u16 local_port\n      4: u8 remote_addr[16]\n      5: u16 remote_port\n      6: u8 is_connector\n      8: string remote_dns_name\n    }\n    5: msg k8s_info {\n      1: u8 side\n      2: u8 pod_uid_suffix[64]\n      3: u64 pod_uid_hash\n    }\n    6: msg tcp_update {\n      1: u8 side\n      2: u8 is_rx\n      3: u32 active_sockets\n      4: u32 sum_retrans\n      5: u64 sum_bytes\n      6: u64 sum_srtt\n      7: u64 sum_delivered\n      8: u32 active_rtts\n      9: u32 syn_timeouts\n     10: u32 new_sockets\n     11: u32 tcp_resets\n    }\n    7: msg udp_update {\n      1: u8 side\n      2: u8 is_rx\n      3: u32 active_sockets\n      4: u32 addr_changes\n      5: u32 packets\n      6: u64 bytes\n      7: u32 drops\n    }\n    8: msg http_update {\n      1: u8 side\n      2: u8 client_server\n\n      // Fields below are aliases of those in http_metrics\n      3: u32 active_sockets\n      4: u32 sum_code_200\n      5: u32 sum_code_400\n      6: u32 sum_code_500\n      7: u32 sum_code_other\n      8: u64 sum_total_time_ns\n      9: u64 sum_processing_time_ns\n    }\n    9: msg dns_update {\n      1: u8 side\n      2: u8 client_server\n\n      // Fields below are aliases of those in dns_metrics\n      3: u32 active_sockets\n      4: u32 requests_a\n      5: u32 requests_aaaa\n      6: u32 responses\n      7: u32 timeouts\n      8: u64 sum_total_time_ns\n      9: u64 sum_processing_time_ns\n    }\n    13: msg container_info {\n      1: u8 side\n      2: string name\n      3: string pod\n      4: string role\n      5: string version\n      6: string ns\n      7: u8 node_type // as in `NodeResolutionType`, either `CONTAINER` or `NOMAD`\n    }\n    14: msg service_info {\n      1: u8 side\n      2: string name\n    }\n  }\n\n  span aws_enrichment\n    impl \"reducer::matching::AwsEnrichmentSpan\"\n    include \"<reducer/matching/aws_enrichment_span.h>\"\n  {\n    pool_size 60000 // arbitrary number, we need a better estimate\n    index (ip)\n\n    u128 ip\n\n    10: msg _start {}\n    11: msg _end {}\n\n    12: msg aws_enrichment {\n      description \"enrichment from cloud-collector / aws node resolution\"\n      severity 0\n      1: string role\n      2: string az\n      3: string id\n    }\n  } /* span aws_enrichment */\n\n  span agg_root {\n    pool_size 4800000\n    proxy aggregation.agg_root shard_by (role1, az1, role2, az2)\n    string<80> role1\n    string<256> role2\n    string<32> az1\n    string<32> az2\n  }\n\n  span k8s_pod impl \"reducer::matching::K8sPodSpan\" include \"<reducer/matching/k8s_pod_span.h>\" {\n    pool_size 220000\n    index (uid_suffix, uid_hash)\n\n    u8 uid_suffix[64]\n\n    // since uid's can theoretically be unbounded in length, we will use the\n    // the last 64 bytes of the uid, with the addition of a u64 which provides\n    // extra reduction in collisions\n    u64 uid_hash\n\n    string<64> owner_name   // name of the Deployment, DaemonSet, ReplicaSet or StatefulSet owning the pod\n    string<64> owner_uid\n    string<64> pod_name\n    string<64> ns\n    string<64> version\n\n    15: msg _start {}\n    16: msg _end {}\n\n    17: msg set_pod_detail {\n      description \"sets the pod owner name and namespace\"\n      severity 0\n      1: string owner_name\n      2: string pod_name\n      3: string ns\n      4: string version\n      5: string owner_uid\n    }\n  } /* span k8s_pod */\n\n  span k8s_container impl \"reducer::matching::K8sContainerSpan\" include \"<reducer/matching/k8s_container_span.h>\" {\n    pool_size 600000\n    index (uid_suffix, uid_hash)\n\n    u8 uid_suffix[64]\n    u64 uid_hash\n\n    string<64> name\n    string<64> version\n\n    reference<k8s_pod> pod\n\n    18: msg _start {}\n    19: msg _end {}\n\n    20: msg set_container_pod {\n      description \"sets the pod for the given container\"\n      severity 0\n      1: u8 pod_uid_suffix[64]\n      2: u64 pod_uid_hash\n      3: string name\n      4: string image\n    }\n  } /* span k8s_container */\n\n  span core_stats {\n    proxy logging.core_stats\n  }\n\n  span logger {\n    proxy logging.logger\n  }\n} /* app matching */\n\napp aggregation {\n\n  /**\n   * a node which is part of a conversation\n   */\n  span node {\n    pool_size 5000000\n    index (id, ip, az)\n    string<80> id\n    string<45> ip\n    reference<az> az\n    string<64> pod_name\n  }\n\n  /**\n   * A (role,az) pair\n   */\n  span az {\n    pool_size 70000\n    index (s, role)\n    string<32> s\n    reference<role> role\n  }\n\n  /**\n   * A role with all its az's and nodes\n   */\n  span role {\n    pool_size 70000\n    index (s, version, env, ns, node_type, process, container)\n    string<256> s\n    string<80> uid\n    string<64> version\n    string<32> env\n    string<64> ns\n    u8 node_type\n    string<16> process\n    string<64> container\n  }\n\n  /*****************************************************************************\n   * node-pair aggregation tree\n   ****************************************************************************/\n  span agg_root\n       impl \"reducer::aggregation::AggRootSpan\"\n       include \"<reducer/aggregation/agg_root_span.h>\"\n  {\n    pool_size 4000000\n\n    aggregate tcp_a_to_b (root type tcp_metrics interval 30 slots 2)\n    {\n      update node_node.tcp_a_to_b\n    }\n    aggregate tcp_b_to_a (root type tcp_metrics interval 30 slots 2)\n    {\n      update node_node.tcp_b_to_a\n    }\n    aggregate http_a_to_b (root type http_metrics interval 30 slots 2)\n    {\n      update node_node.http_a_to_b\n    }\n    aggregate http_b_to_a (root type http_metrics interval 30 slots 2)\n    {\n      update node_node.http_b_to_a\n    }\n    aggregate udp_a_to_b (root type udp_metrics interval 30 slots 2)\n    {\n      update node_node.udp_a_to_b\n    }\n    aggregate udp_b_to_a (root type udp_metrics interval 30 slots 2)\n    {\n      update node_node.udp_b_to_a\n    }\n    aggregate dns_a_to_b (root type dns_metrics interval 30 slots 2)\n    {\n      update node_node.dns_a_to_b\n    }\n    aggregate dns_b_to_a (root type dns_metrics interval 30 slots 2)\n    {\n      update node_node.dns_b_to_a\n    }\n\n    reference<node> node1\n    reference<node> node2\n\n    reference<node_node> node_node cached {\n      node1 = node1\n      node2 = node2\n    }\n\n    10: msg _start {}\n    11: msg _end {}\n    12: msg update_node {\n      1: u8 side\n      2: string id\n      3: string az\n      4: string role\n      5: string version\n      6: string env\n      7: string ns\n      8: u8 node_type\n      9: string address\n      11: string process\n      12: string container\n      13: string pod_name\n      14: string role_uid\n    }\n    14: msg update_tcp_metrics {\n      1: u8 direction\n      2: u32 active_sockets\n      3: u32 sum_retrans\n      4: u64 sum_bytes\n      5: u64 sum_srtt\n      6: u64 sum_delivered\n      7: u32 active_rtts\n      8: u32 syn_timeouts\n      9: u32 new_sockets\n     10: u32 tcp_resets\n    }\n    15: msg update_udp_metrics {\n      1: u8 direction\n      2: u32 active_sockets\n      3: u32 addr_changes\n      4: u32 packets\n      5: u64 bytes\n      6: u32 drops\n    }\n    16: msg update_http_metrics {\n      1: u8 direction\n      2: u32 active_sockets\n      3: u32 sum_code_200\n      4: u32 sum_code_400\n      5: u32 sum_code_500\n      6: u32 sum_code_other\n      7: u64 sum_total_time_ns\n      8: u64 sum_processing_time_ns\n    }\n    17: msg update_dns_metrics {\n      1: u8 direction\n      2: u32 active_sockets\n      3: u32 requests_a\n      4: u32 requests_aaaa\n      5: u32 responses\n      6: u32 timeouts\n      7: u64 sum_total_time_ns\n      8: u64 sum_processing_time_ns\n    }\n  }\n\n  span node_node\n  {\n    pool_size 4000000\n    index (node1, node2)\n\n    aggregate tcp_a_to_b (type tcp_metrics interval 30 slots 1)\n    {\n      update az_node.tcp_a_to_b\n      update node_az.tcp_b_to_a\n    }\n    aggregate tcp_b_to_a (type tcp_metrics interval 30 slots 1)\n    {\n      update az_node.tcp_b_to_a\n      update node_az.tcp_a_to_b\n    }\n    aggregate http_a_to_b (type http_metrics interval 30 slots 1)\n    {\n      update az_node.http_a_to_b\n      update node_az.http_b_to_a\n    }\n    aggregate http_b_to_a (type http_metrics interval 30 slots 1)\n    {\n      update az_node.http_b_to_a\n      update node_az.http_a_to_b\n    }\n    aggregate udp_a_to_b (type udp_metrics interval 30 slots 1)\n    {\n      update az_node.udp_a_to_b\n      update node_az.udp_b_to_a\n    }\n    aggregate udp_b_to_a (type udp_metrics interval 30 slots 1)\n    {\n      update az_node.udp_b_to_a\n      update node_az.udp_a_to_b\n    }\n    aggregate dns_a_to_b (type dns_metrics interval 30 slots 1)\n    {\n      update az_node.dns_a_to_b\n      update node_az.dns_b_to_a\n    }\n    aggregate dns_b_to_a (type dns_metrics interval 30 slots 1)\n    {\n      update az_node.dns_b_to_a\n      update node_az.dns_a_to_b\n    }\n\n    reference<node> node1\n    reference<node> node2\n\n    reference<az_node> az_node cached {\n      az = node1.az\n      node = node2\n    }\n    reference<az_node> node_az cached {\n      node = node1\n      az = node2.az\n    }\n  }\n\n\n\n  span az_az {\n    pool_size 600000\n    index (az1, az2)\n    aggregate tcp_a_to_b (type tcp_metrics interval 30 slots 1)\n    {\n    }\n    aggregate http_a_to_b (type http_metrics interval 30 slots 1)\n    {\n    }\n    aggregate udp_a_to_b (type udp_metrics interval 30 slots 1)\n    {\n\n    }\n    aggregate dns_a_to_b (type dns_metrics interval 30 slots 1)\n    {\n    \n    }\n\n    reference<az> az1\n    reference<az> az2\n  }\n\n  span az_node {\n    pool_size 3000000\n    index (az, node)\n\n    aggregate tcp_a_to_b (type tcp_metrics interval 30 slots 1)\n    {\n      update az_az.tcp_a_to_b\n    }\n    aggregate tcp_b_to_a (type tcp_metrics interval 30 slots 1)\n    {\n    }\n    aggregate http_a_to_b (type http_metrics interval 30 slots 1)\n    {\n      update az_az.http_a_to_b\n    }\n    aggregate http_b_to_a (type http_metrics interval 30 slots 1)\n    {\n    }\n    aggregate udp_a_to_b (type udp_metrics interval 30 slots 1)\n    {\n      update az_az.udp_a_to_b\n    }\n    aggregate udp_b_to_a (type udp_metrics interval 30 slots 1)\n    {\n    }\n    aggregate dns_a_to_b (type dns_metrics interval 30 slots 1)\n    {\n      update az_az.dns_a_to_b\n    }\n    aggregate dns_b_to_a (type dns_metrics interval 30 slots 1)\n    {\n    }\n\n    reference<az> az\n    reference<node> node\n\n    reference<az_az> az_az auto {\n      az1 = az\n      az2 = node.az\n    }\n  }\n\n  span core_stats {\n    proxy logging.core_stats\n  }\n  \n  span agg_core_stats {\n    proxy logging.agg_core_stats\n  }\n} /* app aggregation */\n\napp kernel_collector {\n  span tracked_process\n    impl \"ebpf_net::kernel_collector::TrackedProcessSpanBase\"\n    include \"<generated/ebpf_net/kernel_collector/span_base.h>\"\n  {\n    pool_size 600000\n    index (cgroup, tgid)\n\n    proxy ingest.tracked_process\n\n    u64 cgroup\n    u32 tgid\n  }\n} /* app kernel_collector */\n\napp cloud_collector {\n  span aws_network_interface\n    impl \"ebpf_net::cloud_collector::AwsNetworkInterfaceSpanBase\"\n    include \"<generated/ebpf_net/cloud_collector/span_base.h>\"\n  {\n    pool_size 60000\n    index (ip)\n\n    proxy ingest.aws_network_interface\n\n    u128 ip\n  }\n} /* app cloud_collector */\n\napp logging {\n\n  span logger\n      impl \"reducer::logging::LoggerSpan\"\n      include \"<reducer/logging/logger_span.h>\"\n  {\n    pool_size 128\n\n    0: msg _start {}\n    1: msg _end {}\n    2: msg agent_lost_events {\n      description \"agent lost perf events\"\n      severity 3\n      1: u32 count\n      2: string client_hostname\n    }\n    3: msg pod_not_found {\n      description \"pod with the specified UID is unknown\"\n      severity 4\n      1: string uid\n      2: u8 on_delete\n    }\n    4: msg cgroup_not_found {\n      description \"cgroup with the specified ID is unknown\"\n      severity 4\n      1: u64 cgroup\n    }\n    5: msg rewriting_private_to_public_ip_mapping {\n      description \"rewriting existing private-to-public IP address mapping\"\n      severity 3\n      1: string private_addr\n      2: string existing_public_addr\n      3: string new_public_addr\n    }\n    6: msg private_ip_in_private_to_public_ip_mapping {\n      description \"private-only address found in private-to-public mappings\"\n      severity 3\n      1: string private_addr\n      2: string existing_public_addr\n    }\n    7: msg failed_to_insert_dns_record {\n      description \"failed to insert DNS record into IP->domain map\"\n      severity 4\n    }\n    8: msg tcp_socket_failed_getting_process_reference {\n      description \"TCP socket failed to get process reference\"\n      severity 4\n      1: u32 pid\n    }\n    9: msg udp_socket_failed_getting_process_reference {\n      description \"UDP socket failed to get process reference\"\n      severity 4\n      1: u32 pid\n    }\n    10: msg socket_address_already_assigned {\n      description \"attempt to assign socket address multiple times\"\n      severity 4\n    }\n    11: msg ingest_decompression_error {\n      description \"failed to decompress data coming from client\"\n      severity 4\n      1: u8 client_type\n      2: string client_hostname\n      3: string error\n    }\n    12: msg ingest_processing_error {\n      description \"failed to process data coming from client\"\n      severity 4\n      1: u8 client_type\n      2: string client_hostname\n      3: string error\n    }\n    13: msg ingest_connection_error {\n      description \"client connection error\"\n      severity 4\n      1: u8 client_type\n      2: string client_hostname\n      3: string error\n    }\n    14: msg agent_auth_success {\n      severity 2\n      1: u8 client_type\n      2: string client_hostname\n    }\n    15: msg agent_auth_failure {\n      severity 3\n      1: u8 client_type\n      2: string client_hostname\n      3: string error\n    }\n    16: msg agent_attempting_auth_using_api_key {\n      severity 3\n      1: u8 client_type\n      2: string client_hostname\n    }\n    17: msg k8s_container_pod_not_found {\n      severity 4\n      1: u8 pod_uid_suffix[64]\n      2: u64 pod_uid_hash\n    }\n    18: msg agent_connect_success {\n      severity 2\n      1: u8 client_type\n      2: string client_hostname\n    }\n  }\n\n  span core_stats\n     impl \"reducer::logging::CoreStatsSpan\"\n     include \"<reducer/logging/core_stats_span.h>\"\n  {\n    pool_size 128\n\n    19: msg _start {}\n    20: msg _end {}\n    21: msg span_utilization_stats {\n      1: string span_name\n      2: string module\n      3: u16 shard\n      4: u16 allocated\n      5: u16 max_allocated\n      6: u16 pool_size_\n      7: u64 time_ns\n    }\n    22: msg connection_message_stats {\n      1: string module\n      2: string msg_\n      3: u16 shard\n      4: u32 severity_\n      5: u16 conn\n      6: u64 time_ns\n      7: u64 count\n    }\n    23: msg connection_message_error_stats {\n      1: string module\n      2: u16 shard\n      3: u16 conn\n      4: string msg_\n      5: string error\n      6: u64 count\n      7: u64 time_ns\n    }\n    24: msg status_stats{\n      1: string module\n      2: u16 shard\n      3: string program\n      4: string version\n      5: u8 status\n      6: u64 time_ns\n    }\n    25: msg rpc_receive_stats{\n      1: string receiver_app\n      2: u16 shard\n      3: string sender_app\n      4: u64 max_latency_ns\n      5: u64 time_ns\n    }\n    26: msg rpc_write_stalls_stats{\n      1: string sender_app\n      2: u16 shard\n      3: string receiver_app\n      4: u64 count\n      5: u64 time_ns\n    }\n    27: msg rpc_write_utilization_stats{\n      1: string sender_app\n      2: u16 shard\n      3: string receiver_app\n      4: u32 max_buf_used\n      5: u64 max_buf_util\n      6: u64 max_elem_util\n      7: u64 time_ns\n    }\n    28: msg code_timing_stats{\n       1: string name\n       2: string filename\n       3: u16 line\n       4: u64 index_string\n       5: u64 count\n       6: u64 avg_ns\n       7: u64 min_ns\n       8: u64 max_ns\n       7: u64 sum_ns\n       8: u64 time_ns\n    }\n  }\n\n  span agg_core_stats\n     impl \"reducer::logging::AggCoreStatsSpan\"\n     include \"<reducer/logging/agg_core_stats_span.h>\"\n  {\n    pool_size 128\n\n    29: msg _start {}\n    30: msg _end {}\n    31: msg agg_root_truncation_stats{\n      1: string module\n      2: u16 shard\n      3: string field\n      4: u64 count\n      5: u64 time_ns\n    }\n    32: msg agg_prometheus_bytes_stats{\n      1: string module\n      2: u16 shard\n      3: u64 prometheus_bytes_written\n      4: u64 prometheus_bytes_discarded\n      5: u64 time_ns\n    }\n    44: msg agg_otlp_grpc_stats{\n      1: string module\n      2: u16 shard\n      3: string client_type\n      4: u64 bytes_failed\n      5: u64 bytes_sent\n      6: u64 data_points_failed\n      7: u64 data_points_sent\n      8: u64 requests_failed\n      9: u64 requests_sent\n      10: u64 unknown_response_tags\n      11: u64 time_ns\n    }\n  }\n\n  span ingest_core_stats\n     impl \"reducer::logging::IngestCoreStatsSpan\"\n     include \"<reducer/logging/ingest_core_stats_span.h>\"\n  {\n    pool_size 128\n\n    33: msg _start {}\n    34: msg _end {}\n    35: msg client_handle_pool_stats{\n      1: string module\n      2: u16 shard\n      3: string span_name\n      4: string version\n      5: string cloud\n      6: string env\n      7: string role\n      8: string az\n      9: string node_id\n      10: string kernel_version\n      11: u16 client_type\n      12: string agent_hostname\n      13: string os\n      14: string os_version\n      15: u64 time_ns\n      16: u64 client_handle_pool\n      17: u64 client_handle_pool_fraction\n    }\n    36: msg agent_connection_message_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string agent_hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string message\n      16: u16 severity_\n      17: u64 count\n    }\n    37: msg agent_connection_message_error_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string agent_hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string message\n      16: string error\n      17: u64 count\n    }\n    38: msg connection_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string agent_hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: u64 time_since_last_message_ns\n      16: u64 clock_offset_ns\n    }\n    39: msg collector_log_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string agent_hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string severity_\n      16: u32 count\n    }\n    40: msg entry_point_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string agent_hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string kernel_headers_source\n      16: string entrypoint_error\n      17: u8 entrypoint_info\n    }\n    41: msg collector_health_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string status\n      16: string status_detail\n    }\n    42: msg bpf_log_stats{\n      1: string module\n      2: u16 shard\n      3: string version\n      4: string cloud\n      5: string env\n      6: string role\n      7: string az\n      8: string node_id\n      9: string kernel_version\n      10: u16 client_type\n      11: string hostname\n      12: string os\n      13: string os_version\n      14: u64 time_ns\n      15: string filename\n      16: string line\n      17: string code\n      18: string arg0\n      19: string arg1\n      20: string arg2\n    }\n    43: msg server_stats{\n      1: string module\n      2: u64 connection_counter\n      3: u64 disconnect_counter\n      4: u64 time_ns\n    }\n  }\n} /* app logging */\n\n/******************************************************************************\n * METRICS\n * CAUTION: If you change these, make sure you update server/copy_metrics.h\n ******************************************************************************/\nmetric tcp_metrics {\n  u32 active_sockets\n  u32 sum_retrans\n  u64 sum_bytes\n  u64 sum_srtt\n  u64 sum_delivered\n  u32 active_rtts\n  u32 syn_timeouts\n  u32 new_sockets\n  u32 tcp_resets\n}\n\nmetric udp_metrics {\n  u32 active_sockets\n  u32 addr_changes\n  u32 packets\n  u64 bytes\n  u32 drops\n}\n\nmetric http_metrics {\n  u32 active_sockets\n  u32 sum_code_200\n  u32 sum_code_400\n  u32 sum_code_500\n  u32 sum_code_other\n  u64 sum_total_time_ns\n  u64 sum_processing_time_ns\n}\n\nmetric dns_metrics {\n  u32 active_sockets\n  u32 requests_a\n  u32 requests_aaaa\n  u32 responses\n  u32 timeouts\n  u64 sum_total_time_ns\n  u64 sum_processing_time_ns\n}\n\n"
  },
  {
    "path": "renderc/.gitignore",
    "content": "src-gen/\nxtend-gen/\n.gradle/\nbuild/\n\n# suggested by https://github.com/vorburger/eclipse-typescript-xtext/blob/master/.gitignore\nbin\ntarget\ngenerated\ngenerated-sources\n.antlr-generator-*.jar\nDS_Store\n"
  },
  {
    "path": "renderc/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nset(\n  RENDER_COMPILER\n    \"${CMAKE_CURRENT_BINARY_DIR}/io.opentelemetry.render.standalone/build/libs/io.opentelemetry.render.standalone-1.0.0-SNAPSHOT-all.jar\"\n)\n\n# Prepare directories\nset(\n  COMPILER_DIRECTORIES\n    \"io.opentelemetry.render\"\n    \"io.opentelemetry.render.standalone\"\n)\nset(COMPILER_DIRECTORIES_ABS)\nforeach(FIL ${COMPILER_DIRECTORIES})\n  get_filename_component(FIL_DST_ABS \"${FIL}\" ABSOLUTE BASE_DIR \"${CMAKE_CURRENT_BINARY_DIR}\")\n  add_custom_command(\n    OUTPUT\n      ${FIL}\n    COMMAND\n      ${CMAKE_COMMAND} -E make_directory ${FIL_DST_ABS}\n    COMMENT\n      \"Preparing build (creating directory ${FIL})\"\n  )\n  list(APPEND COMPILER_DIRECTORIES_ABS \"${FIL_DST_ABS}\")\nendforeach()\nadd_custom_target(\n  render_compiler_directories\n  DEPENDS\n    ${COMPILER_DIRECTORIES_ABS}\n)\n\n# Prepare links\nset(\n  COMPILER_FILES\n    \"build.gradle\"\n    \"gradle\"\n    \"gradlew\"\n    \"settings.gradle\"\n    \"io.opentelemetry.render/src\"\n    \"io.opentelemetry.render/META-INF\"\n    \"io.opentelemetry.render/build.gradle\"\n    \"io.opentelemetry.render.standalone/src\"\n    \"io.opentelemetry.render.standalone/build.gradle\"\n    \"io.opentelemetry.render.standalone/plugin.properties\"\n)\nset(COMPILER_FILES_ABS)\nforeach(FIL ${COMPILER_FILES})\n  get_filename_component(FIL_SRC_ABS \"${FIL}\" ABSOLUTE BASE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\n  get_filename_component(FIL_DST_ABS \"${FIL}\" ABSOLUTE BASE_DIR \"${CMAKE_CURRENT_BINARY_DIR}\")\n  add_custom_command(\n    OUTPUT\n      ${FIL}\n    COMMAND\n      ${CMAKE_COMMAND} -E create_symlink ${FIL_SRC_ABS} ${FIL_DST_ABS}\n    DEPENDS\n      render_compiler_directories\n    COMMENT\n      \"Preparing build (linking ${FIL})\"\n  )\n  list(APPEND COMPILER_FILES_ABS \"${FIL_DST_ABS}\")\nendforeach()\n\nadd_custom_command(\n  OUTPUT\n    ${RENDER_COMPILER}\n  COMMAND\n    ./gradlew --console=plain\n    :io.opentelemetry.render.standalone:shadowJar\n  COMMAND\n    touch --no-create \"${RENDER_COMPILER}\"\n  DEPENDS\n    ./build.gradle\n    ./gradle/source-layout.gradle\n    ./settings.gradle\n    ./io.opentelemetry.render/build.gradle\n    ./io.opentelemetry.render/src/io/opentelemetry/render/scoping/RenderScopeProvider.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/RenderRuntimeModule.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/RenderStandaloneSetup.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/GenerateRender.mwe2\n    ./io.opentelemetry.render/src/io/opentelemetry/render/validation/RenderValidator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/formatting/RenderFormatter.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/AppExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/SpanExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/FieldExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/FieldTypeExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/MessageExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/UtilityExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/extensions/XPackedMessageExtensions.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/RenderGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/MetricsGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/AppPacker.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/MessageGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/AppGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/HashGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/PerfectHash.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/ConnectionGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/ProtocolGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/TransformBuilderGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/BpfGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/RustEncoderGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/RustMessageGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/RustCargoGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/WriterGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/SpanGenerator.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/generator/SpanAutoDependencies.xtend\n    ./io.opentelemetry.render/src/io/opentelemetry/render/Render.xtext\n    ./io.opentelemetry.render.standalone/build.gradle\n    ./io.opentelemetry.render.standalone/plugin.properties\n    ./io.opentelemetry.render.standalone/src/Main.xtend\n    ${COMPILER_FILES_ABS}\n  COMMENT\n    \"Making Render compiler\"\n)\nadd_custom_target(\n  render_compiler\n  ALL\n  DEPENDS\n    ${RENDER_COMPILER}\n)\nset_property(\n  TARGET\n    render_compiler\n  PROPERTY\n    RENDER_COMPILER_PATH\n      \"${RENDER_COMPILER}\"\n)\n\nadd_subdirectory(test)\n"
  },
  {
    "path": "renderc/build.gradle",
    "content": "buildscript {\n  apply from: \"${rootDir}/gradle/repositories.gradle\"\n  dependencies {\n    classpath 'org.xtext:xtext-gradle-plugin:4.0.0'\n  }\n}\n\nsubprojects {\n  ext.xtextVersion = '2.40.0'\n\n  apply plugin: 'java'\n  apply plugin: 'org.xtext.xtend'\n  apply from: \"${rootDir}/gradle/repositories.gradle\"\n  apply from: \"${rootDir}/gradle/source-layout.gradle\"\n  apply plugin: 'eclipse'\n  apply plugin: 'idea'\n\n  group = 'io.opentelemetry.render'\n  version = '1.0.0-SNAPSHOT'\n\n  sourceCompatibility = '1.8'\n  targetCompatibility = '1.8'\n\n  configurations.all {\n    exclude group: 'asm'\n  }\n}\n"
  },
  {
    "path": "renderc/gradle/repositories.gradle",
    "content": "def mavenUrl = System.getenv(\"MAVEN_REPOSITORY_URL\")\n\ndef repos = {\n  if (mavenUrl != null) {\n    maven { url mavenUrl }\n  } else {\n    mavenCentral()\n    gradlePluginPortal()\n  }\n}\n\nproject.buildscript.repositories(repos)\n\nrepositories(repos)\n\next.mavenUrl = mavenUrl\n"
  },
  {
    "path": "renderc/gradle/source-layout.gradle",
    "content": "if (name.endsWith(\".tests\")) {\n  sourceSets {\n    main {\n      java.srcDirs = []\n      resources.srcDirs = []\n    }\n    test {\n      java.srcDirs = ['src', 'src-gen', 'xtend-gen']\n      resources.srcDirs = ['src', 'src-gen']\n      xtend.outputDir = 'xtend-gen'\n    }\n  }\n} else {\n  sourceSets {\n    main {\n      java.srcDirs = ['src', 'src-gen', 'xtend-gen']\n      resources.srcDirs = ['src', 'src-gen']\n      xtend.outputDir = 'xtend-gen'\n    }\n    test {\n      java.srcDirs = []\n      resources.srcDirs = []\n    }\n  }\n}\n\nsourceSets.all {\n  resources.exclude '**/*.g', '**/*.mwe2', '**/*.xtend', '**/*._trace'\n}\n\ntasks.configureEach { task ->\n  if (task.name == 'processResources' && tasks.findByName('generateXtextLanguage') != null) {\n    task.dependsOn tasks.named('generateXtextLanguage')\n  }\n}\n\njar {\n  from('model') {\n    into('model')\n  }\n  manifest {\n    attributes 'Bundle-SymbolicName': project.name\n  }\n}\n\nplugins.withId('war') {\n  webAppDirName = \"WebRoot\"\n}\n\nplugins.withId('org.xtext.idea-plugin') {\n  assembleSandbox.metaInf.from('META-INF')\n}\n"
  },
  {
    "path": "renderc/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=file\\:/usr/local/lib/gradle.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "renderc/gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "renderc/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBundle-ManifestVersion: 2\nBundle-Name: io.opentelemetry.render\nBundle-Version: 1.0.0.qualifier\nBundle-SymbolicName: io.opentelemetry.render; singleton:=true\nBundle-ActivationPolicy: lazy\nRequire-Bundle: org.eclipse.xtext,\n org.eclipse.xtext.xbase,\n org.eclipse.equinox.common;bundle-version=\"3.5.0\",\n org.eclipse.emf.ecore,\n org.eclipse.xtext.xbase.lib;bundle-version=\"2.13.0\",\n org.antlr.runtime,\n org.eclipse.xtext.util,\n org.eclipse.emf.common,\n org.eclipse.xtend.lib;bundle-version=\"2.13.0\"\nBundle-RequiredExecutionEnvironment: JavaSE-1.8\nExport-Package: io.opentelemetry.render,\n io.opentelemetry.render.generator,\n io.opentelemetry.render.render,\n io.opentelemetry.render.services,\n io.opentelemetry.render.scoping,\n io.opentelemetry.render.render.impl,\n io.opentelemetry.render.parser.antlr.internal,\n io.opentelemetry.render.parser.antlr,\n io.opentelemetry.render.serializer,\n io.opentelemetry.render.validation,\n io.opentelemetry.render.render.util\nImport-Package: org.apache.log4j\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/build.gradle",
    "content": "configurations {\n  mwe2 {\n    extendsFrom implementation\n  }\n}\n\ndependencies {\n  mwe2 \"org.eclipse.emf:org.eclipse.emf.mwe2.launch:2.23.0\"\n  mwe2 \"org.eclipse.xtext:org.eclipse.xtext.common.types:${xtextVersion}\"\n  mwe2 \"org.eclipse.xtext:org.eclipse.xtext.xtext.generator:${xtextVersion}\"\n  mwe2 \"org.eclipse.xtext:xtext-antlr-generator:[2.1.1, 3)\"\n  implementation \"org.eclipse.xtext:org.eclipse.xtext:${xtextVersion}\"\n  implementation \"org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}\"\n}\n\ntask generateXtextLanguage(type: JavaExec) {\n  main = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher'\n  classpath = configurations.mwe2\n  inputs.file \"src/io/opentelemetry/render/GenerateRender.mwe2\"\n  inputs.file \"src/io/opentelemetry/render/Render.xtext\"\n  outputs.dir \"src-gen\"\n  args += \"src/io/opentelemetry/render/GenerateRender.mwe2\"\n  args += \"-p\"\n  args += \"rootPath=/${projectDir}/..\"\n}\n\ngenerateXtext.dependsOn(generateXtextLanguage)\nclean.dependsOn(cleanGenerateXtextLanguage, cleanGenerateXtext)\neclipse.classpath.plusConfigurations += [configurations.mwe2]\n//this is an eclipse plugin project\neclipseClasspath.enabled=false\ncleanEclipseClasspath.enabled=false\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/GenerateRender.mwe2",
    "content": "module io.opentelemetry.render.GenerateRender\n\nimport org.eclipse.xtext.xtext.generator.*\nimport org.eclipse.xtext.xtext.generator.model.project.*\n\nvar rootPath = \"..\"\n\nWorkflow {\n  component = XtextGenerator {\n    configuration = {\n      project = StandardProjectConfig {\n        baseName = \"io.opentelemetry.render\"\n        rootPath = rootPath\n        runtimeTest = {\n          enabled = false\n        }\n        eclipsePlugin = {\n          enabled = false\n        }\n        eclipsePluginTest = {\n          enabled = false\n        }\n        createEclipseMetaData = false\n      }\n      code = {\n        encoding = \"UTF-8\"\n        lineDelimiter = \"\\n\"\n        fileHeader = \"/*\\n * generated by Xtext \\${version}\\n */\"\n      }\n    }\n    language = StandardLanguage {\n      name = \"io.opentelemetry.render.Render\"\n      fileExtensions = \"render\"\n      serializer = {\n        generateStub = false\n      }\n      validator = {\n        // composedCheck = \"org.eclipse.xtext.validation.NamesAreUniqueValidator\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/Render.xtext",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\ngrammar io.opentelemetry.render.Render with org.eclipse.xtext.common.Terminals\n\ngenerate render \"https://open-telemetry.github.io/opentelemetry-ebpf/render\"\n\nDocument:\n  package=PackageDefinition\n  namespace = Namespace\n  apps += App*\n  metrics += Metric*\n  spans += Span*;\n\n\n/*****************************************************************************\n * Package:\n */\nPackageDefinition:\n  'package' name=QualifiedName;\n\n\n/*****************************************************************************\n * Namespace:\n */\n/**\n * Namespace maps module-specific rpc ids to global rpc ids\n */\nNamespace:\n  {Namespace} 'namespace' '{' mappings+=AppRpcMap* '}';\n\n/**\n * Map of app to range of rpc_id's\n */\nAppRpcMap:\n  app=[App] ':' ranges+=RpcIdRange (',' ranges+=RpcIdRange)*;\n\nRpcIdRange:\n  start=INT (hasEnd ?= '-' end=INT)?;\n\n\n/*****************************************************************************\n * App:\n */\nApp:\n  'app' name=ID '{'\n    (jit ?= 'jit')?\n    spans += Span*\n  '}'\n  /* internal */\n   ('internal' '{{'\n    '-' 'c_name'            c_name=STRING\n    '-' 'jb_h'              jb_h=STRING\n    '-' 'jsrv_h'            jsrv_h=STRING\n    '-' 'descriptor_h'      descriptor_h=STRING\n    '-' 'bpf_h'             bpf_h=STRING\n   '}}')?\n   ;\n\n/*****************************************************************************\n * Message:\n */\nMessage:\n  id=INT ':' type=MessageType name=ID\n  (referenceEmbedded ?= 'ref' reference_field=[Field])?\n    '{' (hasDescription?='description' description=STRING)?\n        (hasSeverity?='severity' severity=INT)?\n        (noAuthorizationNeeded?='no_authorization_needed')?\n        (pipelineOnly?='pipeline_only')?\n        fields+=Field* '}'\n  /* internal */\n  ('internal' '{{'\n    '-' 'wire_msg' wire_msg=XPackedMessage\n    '-' 'parsed_msg' parsed_msg=XPackedMessage\n  '}}')?;\n\nenum MessageType:\n  start | log | end | msg;\n\n/* internal PackedMessage */\nXPackedMessage:\n  '{{'\n    '-' 'size'             size=INT\n    '-' 'rpc_id'           rpc_id=INT\n    '-' 'attr_name'        attr_name=STRING\n    '-' 'packedStrings'    ((packedStrings?='true') | 'false')\n    '-' 'struct_name'      struct_name=STRING\n    '-' 'descriptor_name'  descriptor_name=STRING\n    '-' 'descriptor'       (descriptor += INT)*\n    '-' 'dynamic_size'     ((dynamic_size?='true') | 'false')\n    '-' 'fields'           (fields += [Field])*\n    '-' 'last_blob_field'  (last_blob_field=[Field])?\n  '}}'\n;\n\n/*****************************************************************************\n * Metric:\n */\nMetric:\n  'metric' name=ID '{' fields+=MetricField* '}';\n\nMetricField:\n  type=FieldType name=ID (\n    ('method' method=AggregationMethod)?\n  )?;\n\nenum AggregationMethod:\n  rate | gauge | counter | tdigest;\n\n/*****************************************************************************\n * Spans:\n */\nSpan:\n  'span' name=ID ('impl' impl=STRING)? ('include' include=STRING)? '{'\n    ('pool_size' pool_size_=INT)?\n    (index=Index)?\n    ((isSingleton ?= 'singleton') | (conn_hash_ ?= 'conn_hash'))?\n    (isProxy ?= 'proxy' remoteApp=[App | ID] '.' remoteSpan=[Span | ID] (sharding=Sharding)?)?\n    aggs+=Aggregation*\n    definitions+=Definition*\n    messages += Message*\n  '}';\n\n/**\n * An Index makes the span available through a hash table by using the given\n *   fields/references\n */\nIndex:\n  'index' '(' keys += [Definition | ID] (',' keys += [Definition | ID])* ')';\n\nSharding:\n  'shard_by' '(' keys += [Field | ID] (',' keys += [Field | ID])* ')';\n\nAggregation:\n  'aggregate' name=ID '('\n    (\n      (isRoot ?= 'root')? &\n      ('type' type=[Metric]) &\n      ('interval' interval=INT) &\n      ('slots' slots=INT)\n    )\n  ')'\n  ('{'\n    updates += AggregationUpdate*\n    rollups += Rollup*\n  '}')?;\n\nAggregationUpdate:\n  'update' ref=[Reference | ID] '.' agg=[Aggregation | ID];\n\nDefinition:\n  Field | Reference;\n\nField:\n  (id=INT ':')? type=FieldType name=ID (isArray ?= '[' array_size=INT ']')?\n  /* internal */\n  ('{{' 'wire_pos' wire_pos=INT 'parsed_pos' parsed_pos=INT '}}')?\n  ;\n\nFieldType:\n  (isShortString ?= 'string<' size=INT '>') |\n  enum_type=FieldTypeEnum;\n\nenum FieldTypeEnum:\n  s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64 | s128 | u128 | string;\n\nRollup:\n  'rollup' rollup_count=INT;\n\n/**\n * Keep a reference to another span.\n *\n * A reference has these modes:\n *   - manual: the user sets the handle, and the framework ensures putting the\n *     reference when the span's lifetime is complete.\n *\n *   - auto: the reference's key is directly computed from span fields and\n *     references. The framework re-binds the reference as the fields are set.\n *\n *   - cached: The framework caches the values used to get the reference, and\n *     upon access to the reference checks those and re-binds if needed. This\n *     mode allows the reference to depend on fields or handles in other spans\n *     (through references).\n */\nReference:\n  'reference<' target=[Span | QualifiedName] '>' name=ID\n  (((isAuto ?= 'auto') | (isCached ?= 'cached')) '{' bindings+= ReferenceBinding* '}')?;\n\n/**\n * Each ReferenceBinding clause binds a named index variable in the reference\n *   target to a field or reference reachable from the span.\n *\n * Examples:\n *  - that_key = my_u32\n *  - that_key = my_referenence_to_X.field_of_X\n *\n * Implementation follows pattern from Christian Dietrich:\n *   https://christiandietrich.wordpress.com/2013/05/18/xtext-and-dot-expressions/\n */\nReferenceBinding:\n  key=[Definition] '=' value=ReferenceBindingValue;\n\nReferenceBindingValue returns ReferenceBindingRef:\n  ReferenceBindingRoot ({ReferenceBindingValue.ref=current} '.' tail=[Definition])*;\n\nReferenceBindingRoot returns ReferenceBindingRef:\n  {ReferenceBindingRoot} entity=[Definition];\n\nQualifiedName:\n  ID ('.' ID)*;\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/RenderRuntimeModule.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render\n\nimport com.google.inject.Provider\n\nimport org.eclipse.xtext.formatting2.IFormatter2\n\nimport io.opentelemetry.render.formatting.RenderFormatter\n\nclass RenderRuntimeModule extends AbstractRenderRuntimeModule {\n  def Provider<IFormatter2> provideIFormatter2() {\n    return new RenderFormatter.Factory()\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/RenderStandaloneSetup.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render\n\nclass RenderStandaloneSetup extends RenderStandaloneSetupGenerated {\n  def static void doSetup() {\n    new RenderStandaloneSetup().createInjectorAndDoEMFRegistration()\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/AppExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Document\n\nclass AppExtensions {\n\n  static def hashName(App app) {\n    app.name + \"_hash\"\n  }\n\n  static def hashFunctor(App app) {\n    app.name + \"_hasher_t\"\n  }\n\n  static def hashSize(App app) {\n    app.hashName.toUpperCase + \"_SIZE\"\n  }\n\n  static def pkg(App app) {\n    (app.eContainer as Document).package\n  }\n\n  static def metrics(App app) {\n    (app.eContainer as Document).metrics\n  }\n\n  static def remoteApps(App app) {\n    return app.spans.filter[isProxy].map[remoteApp].toSet\n  }\n\n  static def messages(App app) {\n    app.spans.flatMap[messages].toList\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/FieldExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.Field\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.*\n\nclass FieldExtensions {\n\n  /**\n   * Get field size, taking array size into account\n   */\n  static def size(Field field, boolean packedStrings) {\n    // num_elems is array_size, or 1 if not an array\n    val num_elems = if (field.isArray) field.array_size else 1\n    return field.type.size(packedStrings) * num_elems\n  }\n\n  /**\n   * Returns the \"[size]\" string for array fields, otherwise \"\"\n   */\n  static def arraySuffix(Field field) {\n    if (field.isArray)\n      '''[«field.array_size»]'''\n    else\n      \"\"\n  }\n\n  static def cType(Field field) {\n    return field.type.cType(field.isIsArray ? field.array_size : -1);\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/FieldTypeExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.FieldType\nimport io.opentelemetry.render.render.FieldTypeEnum\n\nclass FieldTypeExtensions {\n\n  static def cType(FieldType fieldType, boolean packedStrings) {\n    switch(fieldType.enum_type) {\n    case FieldTypeEnum.U8:  \"uint8_t\"\n    case FieldTypeEnum.U16:  \"uint16_t\"\n    case FieldTypeEnum.U32:  \"uint32_t\"\n    case FieldTypeEnum.U64:  \"uint64_t\"\n    case FieldTypeEnum.U128: \"unsigned __int128\"\n    case FieldTypeEnum.S8:  \"int8_t\"\n    case FieldTypeEnum.S16:  \"int16_t\"\n    case FieldTypeEnum.S32:  \"int32_t\"\n    case FieldTypeEnum.S64:  \"int64_t\"\n    case FieldTypeEnum.S128:  \"__int128\"\n    case FieldTypeEnum.STRING :\n      if (packedStrings)\n        \"uint16_t\"\n      else\n        \"struct jb_blob\"\n    }\n  }\n\n  // |arraySize| is -1 if not an array\n  static def cType(FieldType type, int arraySize) {\n    val nonArrayType =\n      if (type.isShortString) {\n        '''short_string<«type.size»>'''\n      } else {\n        type.enum_type.literal\n      }\n\n    if (arraySize >= 0) {\n      '''std::array<«nonArrayType»,«arraySize»>'''\n    } else {\n      nonArrayType\n    }\n  }\n\n  static def wireCType(FieldType fieldType) {\n    fieldType.cType(true)\n  }\n\n  static def parsedCType(FieldType fieldType) {\n    fieldType.cType(false)\n  }\n\n  static def size(FieldType fieldType, boolean packedStrings) {\n    if (fieldType.isIsShortString)\n      fieldType.size\n    else switch(fieldType.enum_type) {\n      case FieldTypeEnum.U8:  1\n      case FieldTypeEnum.U16:  2\n      case FieldTypeEnum.U32:  4\n      case FieldTypeEnum.U64:  8\n      case FieldTypeEnum.U128:  16\n      case FieldTypeEnum.S8:  1\n      case FieldTypeEnum.S16:  2\n      case FieldTypeEnum.S32:  4\n      case FieldTypeEnum.S64:  8\n      case FieldTypeEnum.S128:  16\n      case FieldTypeEnum.STRING :\n        if (packedStrings)\n          2\n        else\n          16\n    }\n  }\n\n  static def wireSize(FieldType fieldType) {\n    fieldType.size(true)\n  }\n\n  static def parsedSize(FieldType fieldType) {\n    fieldType.size(false)\n  }\n\n  /**\n   * Get field alignment\n   */\n  static def alignment(FieldType fieldType, boolean packedStrings) {\n    if (fieldType.isIsShortString)\n      1\n    else switch(fieldType.enum_type) {\n      case FieldTypeEnum.U8:  1\n      case FieldTypeEnum.U16:  2\n      case FieldTypeEnum.U32:  4\n      case FieldTypeEnum.U64:  8\n      case FieldTypeEnum.U128:  16\n      case FieldTypeEnum.S8:  1\n      case FieldTypeEnum.S16:  2\n      case FieldTypeEnum.S32:  4\n      case FieldTypeEnum.S64:  8\n      case FieldTypeEnum.S128:  16\n      case FieldTypeEnum.STRING:\n        if (packedStrings)\n          2\n        else\n          8\n    }\n  }\n\n  static def wireAlignment(FieldType fieldType) {\n    fieldType.alignment(true)\n  }\n\n  static def parsedAlignment(FieldType fieldType) {\n    fieldType.alignment(false)\n  }\n\n  static def isSigned(FieldType fieldType) {\n    switch (fieldType.enum_type) {\n    case FieldTypeEnum.S8,\n    case FieldTypeEnum.S16,\n    case FieldTypeEnum.S32,\n    case FieldTypeEnum.S64,\n    case FieldTypeEnum.S128:\n      true\n    default:\n      false\n    }\n  }\n\n  static def isInt(FieldType fieldType) {\n    return (!fieldType.isShortString) &&\n        (fieldType.enum_type != FieldTypeEnum.STRING)\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/MessageExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.MessageType\nimport io.opentelemetry.render.render.Span\n\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.arraySuffix\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.parsedCType\n\nclass MessageExtensions {\n\n  private static def prependCommaIfNotEmpty(String s) {\n    if (s == \"\")\n      s\n    else\n      \", \" + s\n  }\n\n  static def prototype(Message msg) {\n    val fields = msg.fields.sortBy[id]\n    val strs = fields.map[type.isShortString\n      ? '''const char «name»[«type.size»]«arraySuffix»'''\n      : '''const «type.parsedCType» «name»«arraySuffix»''']\n    strs.join(\", \")\n  }\n\n  static def commaPrototype(Message msg) {\n    prependCommaIfNotEmpty(msg.prototype)\n  }\n\n  static def callPrototype(Message msg) {\n    msg.fields.sortBy[id].map[name].join(\", \")\n  }\n\n  static def commaCallPrototype(Message msg) {\n    prependCommaIfNotEmpty(msg.callPrototype)\n  }\n\n  static def norefPrototype(Message msg) {\n    val fields = msg.fields.filter[field | field !== msg.reference_field].sortBy[id]\n    val strs = fields.map['''const «type.parsedCType» «name»«arraySuffix»''']\n    strs.join(\", \")\n  }\n\n  static def norefCommaPrototype(Message msg) {\n    val fields = msg.fields.filter[field | field !== msg.reference_field].sortBy[id]\n    val strs = fields.map['''const «type.parsedCType» «name»«arraySuffix»''']\n    val str = strs.join(\", \")\n    prependCommaIfNotEmpty(str)\n  }\n\n  static def norefCommaCallPrototype(Message msg) {\n    val fields = msg.fields.filter[field | field !== msg.reference_field].sortBy[id]\n    val strs = fields.map[type.isShortString ? '''«name».data()''' : name]\n    val str = strs.join(\", \")\n    prependCommaIfNotEmpty(str)\n  }\n\n  static def span(Message msg) {\n    msg.eContainer as Span\n  }\n\n  // Names of errors that handling of this message can trigger.\n  //\n  static def errors(Message msg) {\n    if (msg.type == MessageType.START) {\n      #{\"span_alloc_failed\", \"span_insert_failed\", \"span_pool_full\", \"duplicate_ref\"}\n    } else if (msg.type == MessageType.END) {\n      #{\"span_find_failed\", \"span_erase_failed\"}\n    } else if (!msg.span.isSingleton) {\n      #{\"span_find_failed\"}\n    } else {\n      #{}\n    }\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/MetricFieldExtension.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.MetricField\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.*\n\nclass MetricFieldExtensions {\n\n  static def cType(MetricField field) {\n    // using -1 for non-array type\n    return field.type.cType(-1);\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/SpanExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.MessageType\n\nimport static extension io.opentelemetry.render.extensions.UtilityExtensions.toCamelCase\n\nclass SpanExtensions {\n\n  static def getBaseClassName(Span span) {\n      toCamelCase(span.name) + \"SpanBase\"\n  }\n  static def getClassTypeName(Span span) {\n      span.name + \"_type\"\n  }\n  static def getInstanceName(Span span) {\n      span.name + \"__instance\"\n  }\n  static def fixedHashName(Span span) {\n      span.name + \"__hash\"\n  }\n  static def fixedHashTypeName(Span span) {\n      span.name + \"__hash_t\"\n  }\n  static def fixedHashHasherName(Span span) {\n      span.name + \"__hasher_t\"\n  }\n\n  static def proxyStartMessage(Span span) {\n    val msg = span.remoteSpan.messages.findFirst[(type == MessageType.START) && referenceEmbedded]\n    if (msg === null) {\n      throw new RuntimeException(\"proxy: no viable start message: \" + span.remoteSpan)\n    }\n    return msg\n  }\n\n  static def proxyEndMessage(Span span) {\n    val msg = span.remoteSpan.messages.findFirst[(type == MessageType.END) && (fields.length == 1) && referenceEmbedded]\n    if (msg === null) {\n      throw new RuntimeException(\"proxy: no viable end message: \" + span.remoteSpan)\n    }\n    return msg\n  }\n\n  static def proxyLogMessages(Span span) {\n    span.remoteSpan.messages.filter[((type == MessageType.LOG) || (type == MessageType.MSG)) && referenceEmbedded]\n  }\n\n  static def app(Span span) {\n    span.eContainer as App\n  }\n\n  static def referenceType(Span span) {\n    val message_with_ref = span.messages.findFirst[referenceEmbedded]\n    if (message_with_ref === null) {\n      throw new RuntimeException(\"referenceType(span): span does not have any messages with reference_field\")\n    }\n    message_with_ref.reference_field.type\n  }\n\n  static def pool_size(Span span) {\n    if (span.pool_size_ > 0) {\n      return span.pool_size_\n    } else {\n      return 4096;\n    }\n  }\n\n  static def conn_hash(Span span) {\n    if (span.conn_hash_) {\n      return span.conn_hash_\n    }\n\n    return (span.messages.length > 0) && !span.isSingleton\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/UtilityExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport com.google.common.base.CaseFormat\n\nclass UtilityExtensions {\n\n  static def toCamelCase(String s) {\n      CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, s)\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/extensions/XPackedMessageExtensions.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.extensions\n\nimport io.opentelemetry.render.render.XPackedMessage\nimport io.opentelemetry.render.render.FieldType\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.cType\n\nclass XPackedMessageExtensions {\n\n  static def cType(XPackedMessage packedMsg, FieldType fieldType) {\n    fieldType.cType(packedMsg.packedStrings)\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/formatting/RenderFormatter.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.formatting\n\nimport com.google.inject.Provider\n\nimport org.eclipse.emf.ecore.EObject\nimport org.eclipse.xtext.formatting2.AbstractFormatter2\nimport org.eclipse.xtext.formatting2.IFormattableDocument\nimport org.eclipse.xtext.formatting2.IFormatter2\n\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.AppRpcMap\nimport io.opentelemetry.render.render.ReferenceBindingValue\n\nclass RenderFormatter extends AbstractFormatter2 {\n\n  static class Factory implements Provider<IFormatter2> {\n    override RenderFormatter get() {\n      return new RenderFormatter\n    }\n  }\n\n  /**\n   * How to default-format the internal part of an EObject\n   */\n  def void formatInternal(EObject p, extension IFormattableDocument doc) {\n    p.regionFor.keyword('internal').surround[oneSpace]\n    p.regionFor.keywords('-').forEach[prepend[setNewLines(1,1,2)]]\n    interior(\n      p.regionFor.keyword('{{'),\n      p.regionFor.keyword('}}').prepend[setNewLines(1,1,2)],\n      [indent]\n    )\n  }\n\n  /**\n   * How to default-format the non-internal part of an EObject\n   */\n  def void formatSingleBrace(EObject p, extension IFormattableDocument doc) {\n    interior(\n      p.regionFor.keyword('{').append[setNewLines(1,1,2)],\n      p.regionFor.keyword('}'),\n      [indent]\n    )\n    p.append[setNewLines(1,1,2)]\n  }\n\n  /**\n   * Format children recursively\n   */\n  def void formatChildren(EObject p, extension IFormattableDocument doc) {\n    for (pp : p.eContents)\n      format(pp, doc)\n  }\n\n  /**\n   * Complete default recursive formatting\n   */\n  def void defaultFormat(EObject p, extension IFormattableDocument doc) {\n    formatSingleBrace(p, doc)\n    formatInternal(p, doc)\n    formatChildren(p, doc)\n  }\n\n  /**\n   * Apply the default formatting if there isn't a special case\n   */\n  override dispatch void format(EObject p, extension IFormattableDocument doc) {\n    defaultFormat(p, doc)\n  }\n\n  /******************\n   * Special cases\n   ******************/\n\n  def dispatch void format(AppRpcMap p, extension IFormattableDocument doc) {\n    /* do nothing */\n  }\n\n  def dispatch void format(ReferenceBindingValue p, extension IFormattableDocument doc) {\n    /* do nothing */\n  }\n\n  def dispatch void format(Message p, extension IFormattableDocument doc)\n  {\n    p.regionFor.keywords('description', 'severity').forEach[prepend[setNewLines(1,1,2)]]\n    defaultFormat(p, doc)\n  }\n\n  def dispatch void format(Field p, extension IFormattableDocument doc) {\n    formatSingleBrace(p, doc)\n    p.regionFor.keyword('{{').surround[oneSpace]\n    p.regionFor.keyword('}}').append[setNewLines(1,1,2)]\n    p.prepend[setNewLines(1,1,2)]\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/AppGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\n\nclass AppGenerator {\n\n  BpfGenerator bpfGenerator = new BpfGenerator()\n  SpanGenerator spanGenerator = new SpanGenerator()\n  HashGenerator hashGenerator = new HashGenerator()\n  WriterGenerator writerGenerator = new WriterGenerator()\n  RustMessageGenerator rustMessageGenerator = new RustMessageGenerator()\n  RustEncoderGenerator rustEncoderGenerator = new RustEncoderGenerator()\n  RustCargoGenerator rustCargoGenerator = new RustCargoGenerator()\n  MessageGenerator messageGenerator = new MessageGenerator()\n  ProtocolGenerator protocolGenerator = new ProtocolGenerator()\n  ConnectionGenerator connectionGenerator = new ConnectionGenerator()\n  TransformBuilderGenerator transformBuilderGenerator = new TransformBuilderGenerator()\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    bpfGenerator.doGenerate(app, fsa)\n    spanGenerator.doGenerate(app, fsa)\n    hashGenerator.doGenerate(app, fsa)\n    writerGenerator.doGenerate(app, fsa)\n    rustMessageGenerator.doGenerate(app, fsa)\n    rustEncoderGenerator.doGenerate(app, fsa)\n    rustCargoGenerator.doGenerateApp(app, fsa)\n    messageGenerator.doGenerate(app, fsa)\n    protocolGenerator.doGenerate(app, fsa)\n    connectionGenerator.doGenerate(app, fsa)\n    transformBuilderGenerator.doGenerate(app, fsa)\n  }\n\n  static def outputPath(App app, String fileName) {\n    app.pkg.name + \"/\" + app.name + \"/\" + fileName\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/AppPacker.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.util.LinkedList\nimport java.util.Vector\nimport io.opentelemetry.render.render.Document\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.MessageType\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.XPackedMessage\nimport io.opentelemetry.render.render.impl.RenderFactoryImpl\nimport org.eclipse.xtend.lib.annotations.Accessors\nimport org.eclipse.emf.ecore.util.EcoreUtil\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.*\n\n/**\n * Represents a message together with field placement\n */\n@Accessors(PUBLIC_GETTER, PROTECTED_SETTER)\nclass AppPacker {\n\n  // RPC ID needs to fit in a u16.\n  // Upper half of the u16 range is reserved for special messages and future use.\n  public static val rpcIdRangeMin = 0\n  public static val rpcIdRangeMax = 32767\n\n  public static val pulseMessageRpcId = 65535\n  public static val pulseMessageName = \"pulse\"\n\n  static def populate(App app) {\n    val factory = new RenderFactoryImpl\n\n    app.spans.add(makePulseSpan(factory))\n\n    for (msg : app.messages) {\n      makeMessage(factory, msg, true)\n      makeMessage(factory, msg, false)\n    }\n\n    annotateClass(app)\n\n    return app\n  }\n\n  private static def makePulseSpan(RenderFactoryImpl factory) {\n    val span = factory.createSpan()\n\n    span.name = \"__pulsar\"\n    span.isSingleton = true\n\n    val pulseMessage = factory.createMessage()\n\n    pulseMessage.id = pulseMessageRpcId\n    pulseMessage.name = pulseMessageName\n    pulseMessage.type = MessageType.LOG\n    pulseMessage.pipelineOnly = true\n\n    span.messages.add(pulseMessage)\n\n    return span\n  }\n\n  private static def createField(RenderFactoryImpl factory, int id, String name, FieldTypeEnum type) {\n    var field = factory.createField()\n\n    field.id = id\n    field.name = name\n    field.type = factory.createFieldType()\n    field.type.enum_type = type\n    field.isArray = false\n\n    return field\n  }\n\n  // Clones the given Field to a new Field object with the given ID\n  private static def copyField(Field field) {\n    val result = EcoreUtil.copy(field)\n    return result\n  }\n\n  private static def makeMessage(RenderFactoryImpl factory, Message msg, boolean packedStrings) {\n    var packedMsg = factory.createXPackedMessage()\n\n    if (msg.type == MessageType.MSG) {\n      // special start and end messages\n      if (msg.name == \"_start\") {\n        msg.name = msg.span.name + \"_start\"\n        msg.type = MessageType.START;\n\n        if (msg.span.index !== null) {\n          var field_ids = msg.fields.map[id]\n          var field_id = if (field_ids.empty) 1 else (field_ids.max + 1)\n          for (key_field : msg.span.index.keys.filter(Field)) {\n            var field = copyField(key_field)\n            field.id = field_id++\n            msg.fields.add(field)\n          }\n        }\n      } else if (msg.name == \"_end\") {\n        msg.name = msg.span.name + \"_end\"\n        msg.type = MessageType.END;\n      }\n\n      // add reference field if not already there\n      if (msg.referenceEmbedded == false) {\n        var field = createField(factory, 0, \"_ref\", FieldTypeEnum.U64);\n        msg.fields.add(field)\n        msg.reference_field = field\n        msg.referenceEmbedded = true\n      }\n    }\n\n    if (packedStrings) {\n      msg.wire_msg = packedMsg\n    } else {\n      msg.parsed_msg = packedMsg\n    }\n\n    packedMsg.packedStrings = packedStrings\n\n    packMessageFields(packedMsg, packedStrings)\n    annotateMessage(packedMsg, packedStrings)\n    makeMessageDescriptors(packedMsg, packedStrings)\n\n    return packedMsg\n  }\n\n  /**\n   * Packs the given Render message\n   * @param msg: the message to pack\n   * @param packedStrings: true if strings and their lengths should be packed\n   *   tightly with total length encoded at the beginning, and strings at the\n   *   end of the message; false to add char * and lengths for every string\n   */\n  private static def packMessageFields(XPackedMessage packedMsg, boolean packedStrings) {\n    val msg = packedMsg.eContainer as Message\n    val fields = new LinkedList(msg.fields)\n\n    /* rpc ID is at pos 0, start at pos 2 */\n    var pos = 2\n\n    if (packedStrings && msg.fields.exists[type.enum_type == FieldTypeEnum.STRING]) {\n      /**\n       * if there are blobs, we have a 'total_length' field after the\n       * rpc_id and we do not encode the last blob's length (it is what\n       * remains after subtracting the other fields and blobs)\n       */\n\n      /* leave space for the length field */\n      pos += 2\n\n      /* remove the last blob, so it doesn't get its own length field */\n      fields.remove(fields.findLast[type.enum_type == FieldTypeEnum.STRING])\n    }\n\n    /* group fields by their alignment */\n    val by_alignment = fields.groupBy[type.alignment(packedStrings)]\n\n    // how many fields do we have to position?\n    var remaining = by_alignment.values().map[size].reduce[p1, p2| p1 + p2] ?: 0\n\n    while (remaining > 0) {\n      val start_pos = pos\n\n      // try the largest value that is already aligned\n      val exact_alignment =\n        by_alignment\n          // should be exactly aligned\n          .filter[align , flds | start_pos.bitwiseAnd(align - 1) == 0]\n          // fields should not be empty\n          .filter[align, flds | !flds.empty]\n\n      val align =\n        if (!exact_alignment.empty) {\n          // get the largest exact alignment\n          exact_alignment.keySet.max\n        } else {\n          // no alignment is right, just get the smallest alignment\n          by_alignment.filter[align, flds | !flds.empty].keySet.min\n        }\n\n      val field = by_alignment.get(align).remove(0)\n      val aligned_pos = (pos + align - 1).bitwiseAnd((align - 1).bitwiseNot)\n\n      packedMsg.fields.add(field)\n\n      if (packedStrings) {\n        field.wire_pos = aligned_pos\n      } else {\n        field.parsed_pos = aligned_pos\n      }\n\n      pos = aligned_pos + field.size(packedStrings)\n\n      remaining--\n    }\n\n    // pos now points after the last serialized byte, which is the size\n    packedMsg.size = pos\n  }\n\n  private static def annotateMessage(XPackedMessage packedMsg, boolean packedStrings) {\n    val msg = packedMsg.eContainer as Message\n    val app = msg.eContainer.eContainer as App\n    val document = app.eContainer as Document\n\n    // find rpc_id\n    var rpcIdFound = false\n    if (msg.id > rpcIdRangeMax) {\n      // messages with IDs outside normal RPC ID range are special messages\n      packedMsg.rpc_id = msg.id\n      rpcIdFound = true\n    } else {\n      val namespace = document.namespace\n      val appMap = namespace.mappings.findFirst[x | x.app == app ]\n      var rpcIndex = msg.id\n      for (range : appMap.ranges) {\n        val rangeEnd = if (range.hasEnd) range.end + 1 else range.start + 1\n        val rangeSize = rangeEnd - range.start\n        if (!rpcIdFound && (rpcIndex < rangeSize)) {\n          packedMsg.rpc_id = range.start + rpcIndex\n          rpcIdFound = true\n        }\n        rpcIndex -= rangeSize\n      }\n    }\n\n    if (!rpcIdFound) {\n      throw new RuntimeException(\"could not find rpc_id for message \" + msg)\n    }\n\n    val c_package = app.name.split(\"\\\\.\").join(\"_\")\n\n    packedMsg.attr_name = if (packedStrings) \"jb\" else \"jsrv\"\n    packedMsg.struct_name = packedMsg.attr_name + '_' + c_package + '__' + msg.name\n    packedMsg.descriptor_name = c_package + '__' + msg.name + (if (packedStrings) '_descriptor' else '__ext_descriptor')\n\n    if (msg.fields.exists[type.enum_type == FieldTypeEnum.STRING]) {\n      packedMsg.dynamic_size = true\n      packedMsg.last_blob_field = msg.fields.findLast[type.enum_type == FieldTypeEnum.STRING]\n      packedMsg.last_blob_field.wire_pos = 0x7fffffff\n    } else {\n      packedMsg.dynamic_size = false\n    }\n  }\n\n  private static def annotateClass(App app) {\n    val basename = (app.pkg.name + '/' + app.name + '/').replace('.','/')\n\n    app.c_name = app.name.replace('.','_')\n    app.jb_h = basename + \"wire_message.h\"\n    app.jsrv_h = basename + \"parsed_message.h\"\n    app.descriptor_h = basename + \"descriptor.h\"\n    app.bpf_h = basename + \"bpf.h\"\n  }\n\n  private static def makeMessageDescriptors(XPackedMessage packedMsg, boolean packedStrings) {\n    val flags = 0\n    val rpcId = packedMsg.rpc_id\n\n    /* get all fields, including the last blob field */\n    val fields = new Vector<Field>(packedMsg.fields)\n    if (packedStrings && packedMsg.dynamic_size) {\n      fields.add(packedMsg.last_blob_field)\n    }\n\n    val fields_u16 = new Vector<Integer>\n    val arrays_u16 = new Vector<Integer>\n\n    for (field : fields) {\n      val int isArr = if (field.isArray) 1 else 0\n\n      val ftype = switch(field.type.enum_type) {\n        case FieldTypeEnum.U8:  0\n        case FieldTypeEnum.U16:  1\n        case FieldTypeEnum.U32:  2\n        case FieldTypeEnum.U64:  3\n        case FieldTypeEnum.U128:  5\n        case FieldTypeEnum.S8:  0\n        case FieldTypeEnum.S16:  1\n        case FieldTypeEnum.S32:  2\n        case FieldTypeEnum.S64:  3\n        case FieldTypeEnum.S128:  5\n        case FieldTypeEnum.STRING: 4\n      }\n\n      fields_u16.add((isArr << 15) + (ftype << 12) + field.id)\n\n      if (field.isIsArray) {\n        arrays_u16.add(field.array_size)\n      }\n    }\n\n    val descriptor = new Vector<Integer>\n    descriptor.add(flags)\n    descriptor.add(rpcId)\n    descriptor.add(fields_u16.size)\n    descriptor.add(arrays_u16.size)\n    descriptor.addAll(fields_u16)\n    descriptor.addAll(arrays_u16)\n\n    packedMsg.descriptor.addAll(descriptor)\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/BpfGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Message\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\n\nclass BpfGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"bpf.h\"), generateBpfH(app))\n  }\n\n  private static def generateBpfH(App app) {\n    val messages = app.messages\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <linux/stddef.h>\n\n    #include \"jitbuf/jb.h\"\n\n    #include \"wire_message.h\"\n\n    #ifdef __cplusplus\n    extern \"C\" {\n    #endif /* __cplusplus */\n\n    /******************************\n     * MSG FILLER FUNCTIONS\n     ******************************/\n    «FOR msg : messages»\n      «fillerFunction(msg, app)»\n\n    «ENDFOR»\n\n\n    /******************************\n     * BPF MESSAGE STRUCTS\n     ******************************/\n    «FOR msg : messages»\n      «bpfMessageStruct(msg, app)»\n\n    «ENDFOR»\n\n    /******************************\n     * BPF FILLER FUNCTIONS\n     ******************************/\n    «FOR msg : messages»\n      «bpfFillerFunction(msg, app)»\n\n    «ENDFOR»\n    #ifdef __cplusplus\n    }\n    #endif /* __cplusplus */\n    '''\n  }\n\n  private static def fillerFunctionName(Message msg, App app) {\n    val jmsg = msg.wire_msg\n    return jmsg.attr_name + '_fill_' + app.name + '__' + msg.name\n  }\n\n  private static def bpfStructName(Message msg, App app) {\n    return 'bpf_' + app.name + '__' + msg.name\n  }\n\n  private static def fillerFunction(Message msg, App app) {\n    val jmsg = msg.wire_msg\n    val filler_function_name = fillerFunctionName(msg, app)\n    '''\n    static inline void «filler_function_name»( struct «jmsg.struct_name» *msg «msg.commaPrototype»)\n    {\n      msg->_rpc_id = «jmsg.rpc_id»;\n\n      /* copy all non-string fields */\n      «FOR field : msg.fields.filter[type.enum_type != FieldTypeEnum.STRING]»\n        «IF field.isArray»\n          bpf_probe_read(msg->«field.name», «field.size(true)», «field.name»);\n        «ELSE»\n          msg->«field.name» = «field.name»;\n        «ENDIF»\n      «ENDFOR»\n      «IF jmsg.dynamic_size»\n\n        /* handle dynamic string lengths */\n        u32 __consumed = «jmsg.size»;\n        «FOR field : jmsg.fields.filter[type.enum_type == FieldTypeEnum.STRING]»\n            /* not the last field: length is in wire message */\n            msg->«field.name» = «field.name».len;\n            __consumed += «field.name».len;\n        «ENDFOR»\n        /* last field: gets the rest of the message */\n        __consumed += «jmsg.last_blob_field.name».len;\n\n        /* check that we don't write dynamic message >= 1<<16 */\n        msg->_len = (__consumed <= 0xffff) ? (u16)__consumed : (u16)0xffff;\n      «ENDIF»\n    }\n\n    /* check that filler_prototype and commaCallPrototype match */\n    static inline void __jitbuf_check_filler_prototype_«app.name»__«msg.name»(int __dummy «msg.commaPrototype») {\n      struct «jmsg.struct_name» *__msg;\n      «filler_function_name»(__msg «msg.commaCallPrototype»);\n    }\n    '''\n  }\n\n  private static def bpfMessageStruct(Message msg, App app) {\n    val jmsg = msg.wire_msg\n    val bpf_struct_name = bpfStructName(msg, app)\n    '''\n    /************************************\n     * «msg.name»\n     ************************************/\n    struct «bpf_struct_name» {\n      u32 dummy; // needed for 64 bit aligned\n        u32 unpadded_size;\n        u64 timestamp;\n        struct «jmsg.struct_name» jb;\n      u8 __pad[«(8 - (jmsg.size % 8)) % 8»];\n    };\n    static const uint32_t «bpf_struct_name»__data_size = «jmsg.size» + 16;\n    static const uint32_t «bpf_struct_name»__perf_size = «((jmsg.size + 8 + 7) / 8) * 8 + 4»;\n\n    /* static asserts that memory layout of message «msg.name» conforms to jitbuf's assumptions */\n    #ifdef __cplusplus\n        #define JB_ASSERT(name, predicate) static_assert(predicate, #name \": \" #predicate)\n    #else\n        #define JB_ASSERT(name, predicate) _Static_assert(predicate, #name \": \" #predicate)\n    #endif\n    JB_ASSERT(«bpf_struct_name»__has_correct_offset,offsetof(struct «bpf_struct_name»,jb) == 16);\n    JB_ASSERT(«bpf_struct_name»_has_correct_sizeof,((sizeof(struct «bpf_struct_name») + 1) & ~1) >= «jmsg.size»+16);\n    #undef JB_ASSERT\n    '''\n  }\n\n  private static def bpfFillerFunction(Message msg, App app) {\n    val jmsg = msg.wire_msg\n    val bpf_struct_name = bpfStructName(msg, app)\n    val filler_function_name = fillerFunctionName(msg, app)\n    '''\n    /* «msg.name» */\n    static inline void bpf_fill_«app.name»__«msg.name»( struct «bpf_struct_name» *__msg, u64 __now  «msg.commaPrototype»)\n    {\n      __msg->timestamp = __now;\n      «filler_function_name»(&__msg->jb «msg.commaCallPrototype»);\n      «IF jmsg.dynamic_size»\n        __msg->unpadded_size = 8 + __msg->jb._len;\n      «ELSE»\n        __msg->unpadded_size = «jmsg.size + 8»;\n      «ENDIF»\n    }\n\n    /* check that filler_prototype and commaCallPrototype match */\n    static inline void __jitbuf_check_bpf_filler_prototype_«app.name»_«msg.name»(int __dummy «msg.commaPrototype») {\n      struct «bpf_struct_name» *__msg;\n      u64 __now;\n      bpf_fill_«app.name»__«msg.name»(__msg, __now «msg.commaCallPrototype»);\n    }\n\n    static inline int perf_submit_«app.name»__«msg.name»(struct pt_regs *ctx,\n      u64 __now «msg.commaPrototype»)\n    {\n      struct «bpf_struct_name» __msg = {};\n      bpf_fill_«app.name»__«msg.name»(&__msg, __now «msg.commaCallPrototype»);\n      return bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &__msg.unpadded_size, «bpf_struct_name»__perf_size);\n    }\n    '''\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/ConnectionGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.MessageType\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.SpanExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.*\n\nclass ConnectionGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"connection.h\"), generateConnectionH(app))\n    fsa.generateFile(outputPath(app, \"connection.cc\"), generateConnectionCc(app))\n  }\n\n  private static def generateConnectionH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"index.h\"\n    #include \"handles.h\"\n    #include \"weak_refs.h\"\n\n    #include <platform/types.h>\n    #include <util/fixed_hash.h>\n\n    «FOR app_span : app.spans.filter[include !== null]»\n      #include «app_span.include»\n    «ENDFOR»\n\n    namespace «app.pkg.name»::«app.name» {\n\n    class Protocol;\n\n    class Connection {\n    public:\n      Connection(Protocol &protocol, Index &index);\n      ~Connection();\n\n      void on_connection_authenticated();\n\n      // Singleton span accessors.\n      //\n      «FOR span : app.spans.filter[isSingleton]»\n        weak_refs::«span.name» «span.name»() const { return «span.instanceName».access(index_); }\n      «ENDFOR»\n\n      // Lookup functions for each span type.\n      //\n      «FOR span : app.spans.filter[conn_hash]»\n        «spanLookupDeclaration(span)»\n      «ENDFOR»\n\n      // Handlers for all the incoming messages.\n      //\n      «FOR span : app.spans»\n        «IF span.messages.length > 0»\n          // span «span.name»\n          «FOR msg : span.messages»\n            void «app.c_name»_«msg.name»(u64 timestamp, char *msg_buf);\n          «ENDFOR»\n        «ENDIF»\n      «ENDFOR»\n\n      // Hashers for each span type.\n      //\n      «FOR span : app.spans.filter[conn_hash]»\n        struct «fixedHashHasherName(span)» {\n          typedef std::size_t result_type;\n          result_type operator()(«span.referenceType.wireCType» const &s) const noexcept;\n        };\n      «ENDFOR»\n\n      // Hash table types for each span type.\n      //\n      «FOR span : app.spans.filter[conn_hash]»\n        using «fixedHashTypeName(span)» = FixedHash<«span.referenceType.wireCType», handles::«span.name», «span.pool_size», «fixedHashHasherName(span)»>;\n      «ENDFOR»\n\n      // Pools for each span type.\n      //\n      «FOR span : app.spans.filter[conn_hash]»\n        «fixedHashTypeName(span)» «fixedHashName(span)»;\n      «ENDFOR»\n\n      struct MessageStatistics {\n        MessageStatistics();\n\n        // For each message, calls `f(module_name, message_name, severity, count)`\n        void foreach(std::function<void(std::string_view, std::string_view, int, u64)> const& f);\n\n        // Message counts.\n        struct {\n          «FOR span : app.spans»\n            «FOR msg : span.messages»\n              u64 «app.c_name»_«msg.name»;\n            «ENDFOR»\n          «ENDFOR»\n        } counts;\n\n        // Message count change indications.\n        struct {\n          «FOR span : app.spans»\n            «FOR msg : span.messages»\n              unsigned int «app.c_name»_«msg.name» : 1;\n            «ENDFOR»\n          «ENDFOR»\n        } changed;\n\n      } message_stats;\n\n      struct MessageErrors {\n        MessageErrors();\n\n        // For each message, calls `f(module_name, message_name, error_name, count)`\n        void foreach(std::function<void(std::string_view, std::string_view, std::string_view, u64)> const &f);\n\n        // Error counts.\n        struct {\n          «FOR span : app.spans»\n            «FOR msg : span.messages»\n              «FOR error_name : msg.errors»\n                u64 «app.c_name»_«msg.name»_«error_name»;\n              «ENDFOR»\n            «ENDFOR»\n          «ENDFOR»\n        } counts;\n\n        // Error count change indications.\n        struct {\n          «FOR span : app.spans»\n            «FOR msg : span.messages»\n              «FOR error_name : msg.errors»\n                unsigned int «app.c_name»_«msg.name»_«error_name» : 1;\n              «ENDFOR»\n            «ENDFOR»\n          «ENDFOR»\n        } changed;\n\n      } message_errors;\n\n      Index &index() { return index_; }\n\n    private:\n      Protocol &protocol_;\n      Index &index_;\n\n      // Singleton spans maintain one instance per span.\n      //\n      «FOR span : app.spans.filter[isSingleton]»\n        handles::«span.name» «span.instanceName»;\n      «ENDFOR»\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateConnectionCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"connection.h\"\n    #include \"protocol.h\"\n    #include \"parsed_message.h\"\n    #include \"weak_refs.inl\"\n    #include \"containers.inl\"\n\n    #include <util/lookup3.h>\n    #include <util/render.h>\n\n    #include <algorithm>\n    #include <stdexcept>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    Connection::Connection(Protocol &protocol, Index &index)\n      : protocol_(protocol), index_(index)\n    {\n      «FOR span : app.spans.filter[isSingleton] SEPARATOR \"\\n\"»\n        if (auto ref = index_.«span.name».alloc(); ref.valid()) {\n          «span.instanceName» = ref.to_handle();\n        } else {\n          throw std::runtime_error(\"«app.pkg.name»::«app.name»::Connection - could not allocate «span.name» handle\");\n        }\n      «ENDFOR»\n\n      «FOR span : app.spans»\n        «FOR msg : span.messages»\n          «IF msg.noAuthorizationNeeded»\n            protocol_.add_handler(«msg.wire_msg.rpc_id», this, &dispatch_protocol_member_handler<Connection, &Connection::«app.c_name»_«msg.name»>);\n          «ENDIF»\n        «ENDFOR»\n      «ENDFOR»\n\n      // Only identity transformations are currently supported.\n      protocol_.insert_no_auth_identity_transforms();\n    }\n\n    Connection::~Connection()\n    {\n      // Clean up singleton spans.\n      //\n      «FOR span : app.spans.filter[isSingleton]»\n        «span.instanceName».put(index_);\n      «ENDFOR»\n\n      //\n      // Clean up span pools.\n      //\n\n      «FOR span : app.spans.filter[conn_hash] SEPARATOR \"\\n\"»\n        for (auto handle_loc : «fixedHashName(span)».allocated()) {\n          «IF span.impl !== null»\n            «FOR msg : span.messages.filter[type == MessageType.END && reference_field.name == \"_ref\"]»\n            {\n              struct «msg.parsed_msg.struct_name» msg = {\n                ._rpc_id = «msg.wire_msg.rpc_id»,\n                ._ref = handle_loc,\n              };\n              auto span_ref = «fixedHashName(span)»[handle_loc].access(index_);\n              span_ref.impl().«msg.name»(span_ref, 0, &msg);\n            }\n            «ENDFOR»\n          «ENDIF»\n          «fixedHashName(span)»[handle_loc].put(index_);\n        }\n      «ENDFOR»\n    }\n\n    void Connection::on_connection_authenticated()\n    {\n      «FOR span : app.spans»\n        «FOR msg : span.messages»\n          «IF !msg.noAuthorizationNeeded»\n            protocol_.add_handler(«msg.wire_msg.rpc_id», this, &dispatch_protocol_member_handler<Connection, &Connection::«app.c_name»_«msg.name»>);\n          «ENDIF»\n        «ENDFOR»\n      «ENDFOR»\n\n      // Only identity transformations are currently supported.\n      protocol_.insert_need_auth_identity_transforms();\n    }\n\n    ////////////////////////////////////////////////////////////////////////////////\n    // Span accessor function implementations.\n    //\n\n    «FOR span : app.spans.filter[conn_hash] SEPARATOR \"\\n\"»\n      «spanLookupImplementation(span)»\n    «ENDFOR»\n\n    ////////////////////////////////////////////////////////////////////////////////\n    // Handler function implementations.\n    //\n\n    «FOR span : app.spans»\n      «FOR msg : span.messages SEPARATOR \"\\n\"»\n        «handlerImplementation(span, msg, app)»\n      «ENDFOR»\n    «ENDFOR»\n\n    ////////////////////////////////////////////////////////////////////////////////\n    // Hasher implementations for spans.\n    //\n\n    «FOR span : app.spans.filter[conn_hash] SEPARATOR \"\\n\"»\n      typename Connection::«fixedHashHasherName(span)»::result_type\n      Connection::«fixedHashHasherName(span)»::operator()(«span.referenceType.wireCType» const &s) const noexcept\n      {\n        result_type val = 0xBFFB7A00;\n        «FOR field : #[span.messages.head.reference_field]»\n          «IF field.isArray && field.type.isShortString»\n          «ELSEIF field.type.isShortString»\n          «ELSEIF RenderGenerator::integerTypeSize(field.type.enum_type) % 4 == 0»\n            // «field.name» is a primitive type, is multiple of 4 bytes: will hash in 4-byte words.\n            val = (result_type)lookup3_hashword((u32 *)&s, «RenderGenerator::fieldSize(field) / 4», val + «RenderGenerator::fieldSize(field)»);\n          «ELSE»\n            // «field.name» is a plain variable: will hash individual bytes.\n            val = (result_type)lookup3_hashlittle((char *)&s, «RenderGenerator::fieldSize(field)», val + «RenderGenerator::fieldSize(field)»);\n          «ENDIF»\n        «ENDFOR»\n        return val;\n      }\n    «ENDFOR»\n\n    ////////////////////////////////////////////////////////////////////////////////\n    // Message statistics.\n    //\n\n    Connection::MessageStatistics::MessageStatistics()\n    {\n      «FOR span : app.spans»\n        «FOR msg : span.messages»\n          counts.«app.c_name»_«msg.name» = 0;\n        «ENDFOR»\n      «ENDFOR»\n\n      memset(&changed, 0, sizeof(changed));\n    }\n\n    void Connection::MessageStatistics::foreach(std::function<void(std::string_view, std::string_view, int, u64)> const& f)\n    {\n      «FOR span : app.spans»\n        «FOR msg : span.messages»\n          if (changed.«app.c_name»_«msg.name») {\n            f(\"«app.c_name»\", \"«msg.name»\", «msg.severity», counts.«app.c_name»_«msg.name»);\n          }\n        «ENDFOR»\n      «ENDFOR»\n\n      memset(&changed, 0, sizeof(changed));\n    }\n\n    ////////////////////////////////////////////////////////////////////////////////\n    // Message error counts.\n    //\n\n    Connection::MessageErrors::MessageErrors()\n    {\n      memset(&counts, 0, sizeof(counts));\n      memset(&changed, 0, sizeof(changed));\n    }\n\n    void Connection::MessageErrors::foreach(std::function<void(std::string_view, std::string_view, std::string_view, u64)> const &f)\n    {\n      «FOR span : app.spans»\n        «FOR msg : span.messages»\n          «FOR error_name : msg.errors»\n            if (changed.«app.c_name»_«msg.name»_«error_name») {\n              f(\"«app.c_name»\", \"«msg.name»\", \"«error_name»\", counts.«app.c_name»_«msg.name»_«error_name»);\n            }\n          «ENDFOR»\n        «ENDFOR»\n      «ENDFOR»\n\n      memset(&changed, 0, sizeof(changed));\n    }\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def spanLookupDeclaration(Span span) {\n    '''\n    weak_refs::«span.name» get_«span.name»(«span.referenceType.wireCType» key);\n    '''\n  }\n\n  private static def spanLookupImplementation(Span span) {\n    '''\n    weak_refs::«span.name» Connection::get_«span.name»(«span.referenceType.wireCType» key)\n    {\n      if (auto pos = «fixedHashName(span)».find(key); pos.index != «fixedHashName(span)».invalid) {\n        return pos.entry->access(index_);\n      } else {\n        return {index_, weak_refs::«span.name»::invalid};\n      }\n    }\n    '''\n  }\n\n  private static def handlerImplementation(Span span, Message msg, App app) {\n    val pmsg = msg.parsed_msg\n\n    '''\n    void Connection::«app.c_name»_«msg.name»(u64 t, char *msg_buf)\n    {\n      [[maybe_unused]] struct «pmsg.struct_name» *msg = (struct «pmsg.struct_name» *)msg_buf;\n\n      «IF msg.type == MessageType.START»\n        // \"start\" message: allocate/lookup and insert into the pool.\n        «IF span.index === null»\n          auto ref = index_.«span.name».alloc();\n        «ELSE»\n          keys::«span.name» key;\n          «FOR field : span.index.keys»\n            «IF (field instanceof Field) && (field as Field).isIsArray»\n              static_assert(sizeof(key.«field.name») == sizeof(msg->«field.name»));\n              std::copy_n(std::begin(msg->«field.name»), «(field as Field).array_size», std::begin(key.«field.name»));\n            «ELSE»\n              key.«field.name» = msg->«field.name»;\n            «ENDIF»\n          «ENDFOR»\n          auto ref = index_.«span.name».by_key(key);\n        «ENDIF»\n        if (!ref.valid()) {\n          // Could not allocate span.\n          message_errors.counts.«app.c_name»_«msg.name»_span_alloc_failed += 1;\n          message_errors.changed.«app.c_name»_«msg.name»_span_alloc_failed = 1;\n          return;\n        }\n\n        auto pos = «fixedHashName(span)».insert(msg->«msg.reference_field.name», std::move(ref));\n        if (pos.index == «fixedHashName(span)».invalid) {\n          if («fixedHashName(span)».full()) {\n            // Span handle pool full.\n            message_errors.counts.«app.c_name»_«msg.name»_span_pool_full += 1;\n            message_errors.changed.«app.c_name»_«msg.name»_span_pool_full = 1;\n          } else {\n            auto find_pos = «fixedHashName(span)».find(msg->«msg.reference_field.name»);\n            if (find_pos.index != «fixedHashName(span)».invalid) {\n              // Already exists.\n              message_errors.counts.«app.c_name»_«msg.name»_duplicate_ref += 1;\n              message_errors.changed.«app.c_name»_«msg.name»_duplicate_ref = 1;\n            } else {\n              // Pool insert failed.\n              message_errors.counts.«app.c_name»_«msg.name»_span_insert_failed += 1;\n              message_errors.changed.«app.c_name»_«msg.name»_span_insert_failed = 1;\n            }\n          }\n          // Ignoring message -- failed to allocate or lookup span.\n          return;\n        }\n      «ELSEIF (!span.isSingleton)»\n        // Get the destination span.\n        auto pos = «fixedHashName(span)».find(msg->«msg.reference_field.name»);\n        if (pos.index == «fixedHashName(span)».invalid) {\n          // Find failed.\n          message_errors.counts.«app.c_name»_«msg.name»_span_find_failed += 1;\n          message_errors.changed.«app.c_name»_«msg.name»_span_find_failed = 1;\n          return;\n        }\n      «ENDIF»\n\n      «IF span.impl !== null»\n      {\n        // Call the span's handler.\n        «IF span.isSingleton»\n        auto entry = &«span.instanceName»;\n        «ELSE»\n        auto entry = pos.entry;\n        «ENDIF»\n        auto span_ref = entry->access(index_);\n        span_ref.impl().«msg.name»(span_ref, t, msg);\n      }\n      «ENDIF»\n\n      «IF msg.type == MessageType.END»\n        // \"end\" message: remove from pool.\n        «fixedHashName(span)»[pos.index].put(index_);\n        bool erased = «fixedHashName(span)».erase(msg->«msg.reference_field.name»);\n        if (erased != true) {\n          // Erase failed.\n          message_errors.counts.«app.c_name»_«msg.name»_span_erase_failed += 1;\n          message_errors.changed.«app.c_name»_«msg.name»_span_erase_failed = 1;\n        }\n      «ENDIF»\n\n      // Update message statistics.\n      //\n      message_stats.counts.«app.c_name»_«msg.name» += 1;\n      message_stats.changed.«app.c_name»_«msg.name» = 1;\n    }\n    '''\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/HashGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.hashName\nimport static extension io.opentelemetry.render.extensions.AppExtensions.hashSize\nimport static extension io.opentelemetry.render.extensions.AppExtensions.hashFunctor\nimport static extension io.opentelemetry.render.extensions.AppExtensions.pkg\n\nclass HashGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    val keys = app.spans.flatMap[messages.map[wire_msg.rpc_id]].toList\n    val hash = new PerfectHash(keys, false)\n\n    fsa.generateFile(outputPath(app, \"hash.c\"), generateC(app, hash))\n    fsa.generateFile(outputPath(app, \"hash.h\"), generateH(app, hash))\n    // Rust port: generate perfect hash for rpc_id into src/hash.rs\n    fsa.generateFile(outputPath(app, \"src/hash.rs\"), generateRust(app, hash))\n  }\n\n  private def generateC(App app, PerfectHash hash) {\n    '''\n    «generatedCodeWarning()»\n\n    #include <platform/platform.h>\n\n    «hash.g_type» «app.hashName»_g_array[] = {\n        «hash.g_array.map[toString].join(\",\")»\n    };\n    '''\n  }\n\n  private def generateH(App app, PerfectHash hash) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/platform.h>\n\n    /**\n     * «app.hashName»\n     *\n     * g_type: «hash.g_type»\n     * g_size: «hash.g_size»\n     * g_shift: «hash.g_shift»\n     * hash_shift: «hash.hash_shift»\n     * hash_mask: «hash.hash_mask»\n     * n_keys: «hash.n_keys»\n     * multiplier: «hash.multiplier»\n     * hash_seed: «hash.hash_seed»\n     */\n\n    extern «hash.g_type» «app.hashName»_g_array[];\n\n    #define «app.hashSize» «hash.hash_mask + 1»\n\n    inline u32 «app.hashName»(u32 rpc_id) {\n      u32 k = (rpc_id ^ «hash.hash_seed») * «hash.multiplier»;\n      return ((k >> «hash.hash_shift») + «app.hashName»_g_array[k >> «hash.g_shift»]) & «hash.hash_mask»;\n    }\n\n    #ifdef __cplusplus\n    struct «app.hashFunctor» {\n      u32 operator()(u32 rpc_id) const { return «app.hashName»(rpc_id); }\n    };\n    #endif\n    '''\n  }\n\n  private def generateRust(App app, PerfectHash hash) {\n    '''\n    «generatedCodeWarning()»\n    // Perfect hash for RPC IDs for «app.pkg.name»::«app.name»\n    //\n    // g_type: «hash.g_type»\n    // g_size: «hash.g_size»\n    // g_shift: «hash.g_shift»\n    // hash_shift: «hash.hash_shift»\n    // hash_mask: «hash.hash_mask»\n    // n_keys: «hash.n_keys»\n    // multiplier: «hash.multiplier»\n    // hash_seed: «hash.hash_seed»\n\n    #[allow(dead_code)]\n    pub const «app.hashSize»: u32 = «hash.hash_mask + 1»u32;\n\n    #[allow(dead_code)]\n    pub static G_ARRAY: [«hash.g_type»; «hash.g_size»] = [\n        «hash.g_array.map[toString].join(\",\")»\n    ];\n\n    #[inline]\n    #[allow(dead_code)]\n    pub fn «app.hashName»(rpc_id: u32) -> u32 {\n        let k = (rpc_id ^ «hash.hash_seed»u32).wrapping_mul(«hash.multiplier»u32);\n        let g = G_ARRAY[(k >> «hash.g_shift») as usize] as u32;\n        (k >> «hash.hash_shift»).wrapping_add(g) & «hash.hash_mask»u32\n    }\n    '''\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/MessageGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.util.List\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Message\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.XPackedMessageExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\n\n/**\n * Generates message-related code (previously \"jitbuf\")\n */\nclass MessageGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    val messages = app.messages\n\n    fsa.generateFile(outputPath(app, \"wire_message.h\"), generateMessageH(messages, true))\n    fsa.generateFile(outputPath(app, \"parsed_message.h\"), generateMessageH(messages, false))\n\n    fsa.generateFile(outputPath(app, \"descriptor.h\"), generateDescriptorH(messages))\n    fsa.generateFile(outputPath(app, \"descriptor.cc\"), generateDescriptorCc(messages))\n\n    fsa.generateFile(outputPath(app, \"meta.h\"), generateMetaH(app, messages))\n  }\n\n  static def generateMessageH(List<Message> messages, boolean wire_message) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #ifdef KBUILD_MODNAME\n    # include <linux/stddef.h>\n    #else\n    # include <stddef.h>\n    #endif\n    #include \"jitbuf/jb.h\"\n\n    #ifdef __cplusplus\n    # include <util/raw_json.h>\n\n    # include <utility>\n    #endif /* __cplusplus */\n\n    «FOR msg : messages»\n    «val xmsg = if (wire_message) msg.wire_msg else msg.parsed_msg»\n    /************************************\n     * «msg.name»\n     ************************************/\n    #ifdef __cplusplus\n    extern \"C\" {\n    #endif /* __cplusplus */\n    struct «xmsg.struct_name» {\n      uint16_t _rpc_id;\n      «IF wire_message && xmsg.dynamic_size»\n        uint16_t _len;\n      «ENDIF»\n      «FOR field : xmsg.fields»\n        «IF field.type.isShortString»\n          char «field.name»[«field.type.size»]«field.arraySuffix»;\n        «ELSE»\n          «xmsg.cType(field.type)» «field.name»«field.arraySuffix»;\n        «ENDIF»\n      «ENDFOR»\n\n    #ifdef __cplusplus\n      void dump_json(std::ostream &out) const {\n        out << \"\\\"@msg\\\":\\\"«xmsg.struct_name»\\\"\";\n        «FOR field: xmsg.fields»\n          print_json_value(out << \",\\\"«field.name»\\\":\", «field.name»);\n        «ENDFOR»\n        out << '}';\n      }\n    #endif /* __cplusplus */\n    };\n    static const uint32_t «xmsg.struct_name»__data_size = «xmsg.size»;\n    #ifdef __cplusplus\n    } /* extern \"C\" */\n\n    template <typename Out>\n    Out &&operator <<(Out &&out, «xmsg.struct_name» const &what) {\n      what.dump_json(out);\n      return std::forward<Out>(out);\n    }\n    #endif /* __cplusplus */\n\n    /* static asserts that memory layout of message «msg.name» conforms to jitbuf's assumptions */\n    #ifdef __cplusplus\n        #define JB_ASSERT(name, predicate) static_assert(predicate, #name \": \" #predicate)\n    #else\n        #define JB_ASSERT(name, predicate) _Static_assert(predicate, #name \": \" #predicate)\n    #endif\n    «FOR field : xmsg.fields»\n      JB_ASSERT(«xmsg.struct_name»_«field.name»_has_correct_offset,offsetof(struct «xmsg.struct_name»,«field.name») == «if (wire_message) field.wire_pos else field.parsed_pos»);\n    «ENDFOR»\n    JB_ASSERT(«xmsg.struct_name»_has_correct_sizeof,((sizeof(struct «xmsg.struct_name») + 1) & ~1) >= «xmsg.size»);\n    #undef JB_ASSERT\n\n    #define «xmsg.struct_name»__rpc_id    «xmsg.rpc_id»\n\n    «ENDFOR»\n    '''\n  }\n\n  static def generateDescriptorH(List<Message> messages) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <stddef.h>\n    #include <string>\n\n    «FOR msg : messages»\n      /* JitbufDescriptor for message «msg.name» */\n      extern const std::string «msg.wire_msg.descriptor_name»;\n      /* JitbufExtDescriptor for message «msg.name» */\n      extern const std::string «msg.parsed_msg.descriptor_name»;\n    «ENDFOR»\n    '''\n  }\n\n  static def generateDescriptorCc(List<Message> messages) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"descriptor.h\"\n    #include <cstdint>\n\n    /***********************\n     * DESCRIPTORS\n     ***********************/\n    «FOR msg : messages»\n      «FOR xmsg : List.of(msg.wire_msg, msg.parsed_msg)»\n        static const uint16_t «xmsg.descriptor_name»_buffer[] = {«xmsg.descriptor.map[toString].join(',')»};\n        const std::string «xmsg.descriptor_name»((const char*)«xmsg.descriptor_name»_buffer, «xmsg.descriptor.size * 2»);\n      «ENDFOR»\n    «ENDFOR»\n    '''\n  }\n\n  static def generateMetaH(App app, List<Message> messages) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"parsed_message.h\"\n    #include \"wire_message.h\"\n    #include \"protocol.h\"\n    #include \"transform_builder.h\"\n\n    #include <util/meta.h>\n\n    #include <string_view>\n    #include <type_traits>\n\n    #include <cstdint>\n\n    namespace «app.pkg.name» { /* pkg */\n    namespace «app.name» { /* app */\n\n    «FOR msg : messages»\n      struct «msg.name»_message_metadata {\n        static constexpr std::uint16_t rpc_id = «msg.wire_msg.rpc_id»;\n        static constexpr std::string_view name = \"«msg.name»\";\n\n        using wire_message = «msg.wire_msg.struct_name»;\n        static constexpr std::size_t wire_message_size = «msg.wire_msg.size»;\n\n        using parsed_message = «msg.parsed_msg.struct_name»;\n        static constexpr std::size_t parsed_message_size = «msg.parsed_msg.size»;\n\n        «FOR field : msg.fields.indexed»\n          struct field_«field.value.name» {\n            using type = «msg.parsed_msg.cType(field.value.type)»«field.value.arraySuffix»;\n            static constexpr std::string_view name = \"«field.value.name»\";\n            static constexpr std::size_t index = «field.key»;\n            static constexpr auto const &get(void const *msg) {\n              return reinterpret_cast<parsed_message const *>(msg)->«field.value.name»;\n            }\n          };\n\n        «ENDFOR»\n        using fields = meta::list<«FOR field : msg.fields SEPARATOR \", \"»field_«field.name»«ENDFOR»>;\n\n        «IF msg.reference_field !== null»\n          static constexpr bool has_reference = true;\n          using reference = field_«msg.reference_field.name»;\n        «ELSE»\n          static constexpr bool has_reference = false;\n        «ENDIF»\n      };\n\n    «ENDFOR»\n    } // namespace «app.name» /* app */\n\n    class «app.name»_metadata {\n\n      «FOR msg : messages»\n        static «app.name»::«msg.name»_message_metadata message_metadata_for_impl(\n            «msg.wire_msg.struct_name» const &);\n        static «app.name»::«msg.name»_message_metadata message_metadata_for_impl(\n            «msg.parsed_msg.struct_name» const &);\n      «ENDFOR»\n\n    public:\n      using protocol = «app.name»::Protocol;\n      using transform_builder = «app.name»::TransformBuilder;\n\n      using messages = meta::list<«FOR msg : messages SEPARATOR \", \"»«app.name»::«msg.name»_message_metadata«ENDFOR»>;\n\n      template <typename MessageStruct>\n      using message_metadata_for = decltype(\n        message_metadata_for_impl(std::declval<MessageStruct>())\n      );\n    };\n\n    } // namespace «app.pkg.name» /* pkg */\n    '''\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/MetricsGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.emf.ecore.resource.Resource\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.AggregationMethod\nimport io.opentelemetry.render.render.Metric\nimport io.opentelemetry.render.render.MetricField\nimport io.opentelemetry.render.render.PackageDefinition\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.MetricFieldExtensions.cType\n\nclass MetricsGenerator {\n\n  def void doGenerate(Resource resource, IFileSystemAccess2 fsa) {\n    val pkgName = resource.allContents.filter(PackageDefinition).head.name\n    fsa.generateFile(pkgName + \"/metrics.h\", generateMetricsH(resource, pkgName))\n  }\n\n  private def generateMetricPointField(MetricField field) {\n    if (field.method == AggregationMethod.TDIGEST) {\n      '''double «field.name»;'''\n    } else {\n      '''«field.cType» «field.name»;'''\n    }\n  }\n\n  private def generateField(MetricField field) {\n    if (field.method == AggregationMethod.TDIGEST) {\n      '''::util::TDigest «field.name»;'''\n    } else if (field.method == AggregationMethod.GAUGE) {\n      '''::data::Gauge<«field.cType»> «field.name»;'''\n    } else if (field.method == AggregationMethod.COUNTER) {\n      '''::data::Counter<«field.cType»> «field.name»;'''\n    } else { // rate is default\n      '''«field.cType» «field.name»;'''\n    }\n  }\n\n  /***************************************************************************\n   * METRICS H\n   **************************************************************************/\n  private def generateMetricsH(Resource resource, String pkg_name) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n    #include <util/counter.h>\n    #include <util/gauge.h>\n    #include <util/tdigest.h>\n\n    namespace «pkg_name» {\n\n    namespace metrics {\n\n    /**\n     * «pkg_name»::metrics - Structs to hold aggregated telemetry data\n     */\n    «FOR metric : resource.allContents.filter(Metric).toIterable»\n      /**\n       * struct «metric.name»: holds aggregated data for metric «metric.name»\n       */\n      struct «metric.name» {\n        «FOR field : metric.fields»\n          «generateField(field)»\n        «ENDFOR»\n\n        void dump_json(std::ostream &out) const {\n          // dumps object in JSON format\n          «FOR field : metric.fields BEFORE \"out\" SEPARATOR \" << ','\" AFTER \";\"»\n            << \"\\\"«field.name»\\\":\\\"\" << «field.name» << '\"'\n          «ENDFOR»\n        }\n\n        friend std::ostream &operator <<(std::ostream &out, «metric.name» const &what) {\n          what.dump_json(out);\n          return out;\n        }\n      };\n      /**\n       * struct «metric.name»_point: holds one data point for metric «metric.name»\n       */\n      struct «metric.name»_point {\n        «FOR field : metric.fields»\n          «generateMetricPointField(field)»\n        «ENDFOR»\n\n        void dump_json(std::ostream &out) const {\n          // dumps object in JSON format\n          «FOR field : metric.fields BEFORE \"out\" SEPARATOR \" << ','\" AFTER \";\"»\n            << \"\\\"«field.name»\\\":\\\"\" << «field.name» << '\"'\n          «ENDFOR»\n        }\n\n        friend std::ostream &operator <<(std::ostream &out, «metric.name»_point const &what) {\n          what.dump_json(out);\n          return out;\n        }\n      };\n      /**\n       * struct «metric.name»_struct: used to accumulate data of metric «metric.name»\n       *        at the root of a aggregation tree.\n       */\n      struct «metric.name»_accumulator {\n        «metric.name» m;\n        «FOR td : metric.fields.filter[method == AggregationMethod::TDIGEST]»\n        ::util::TDigestAccumulator «td.name»;\n        «ENDFOR»\n\n        /**\n         * Constructs «metric.name»_accumulator object.\n         */\n        «metric.name»_accumulator()\n        «FOR td : metric.fields.filter[method == AggregationMethod::TDIGEST] BEFORE \": \" SEPARATOR \",\"»\n          «td.name»(m.«td.name»)\n        «ENDFOR»\n        {}\n\n        /**\n         * Returns enclosed «metric.name» metric object\n         */\n        «metric.name»& get_metrics() {\n        «FOR td : metric.fields.filter[method == AggregationMethod::TDIGEST]»\n          «td.name».flush();\n        «ENDFOR»\n          return m;\n        }\n\n        void dump_json(std::ostream &out) const {\n          // dumps object in JSON format\n          «FOR field : metric.fields BEFORE \"out\" SEPARATOR \" << ','\" AFTER \";\"»\n            «IF field.method == AggregationMethod::TDIGEST»\n              << \"\\\"«field.name»\\\":\\\"\" << «field.name» << '\"'\n            «ELSE»\n              << \"\\\"«field.name»\\\":\\\"\" << m.«field.name» << '\"'\n            «ENDIF»\n          «ENDFOR»\n        }\n\n        friend std::ostream &operator <<(std::ostream &out, «metric.name»_accumulator const &what) {\n          what.dump_json(out);\n          return out;\n        }\n      };\n\n    «ENDFOR»\n    } // namespace metrics\n\n    } // namespace «pkg_name»\n    '''\n  }\n\n}"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/PerfectHash.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.util.Collections\nimport java.util.HashSet\nimport java.util.LinkedList\nimport java.util.List\nimport java.util.Map\nimport java.util.Set\nimport java.util.TreeMap\nimport java.util.stream.LongStream\n\nimport com.google.common.collect.ImmutableList\n\nclass PerfectHash {\n\n  public final int n_keys\n\n  public final int n_bits\n  public final int g_size\n  public final int g_shift\n\n  public final int hash_bits\n  public final int hash_shift\n  public final int hash_mask\n\n  public long hash_seed\n  public long multiplier\n\n  public final String g_type\n  public final int[] g_array\n\n  private static def bit_length(int x) {\n    Integer.SIZE - Integer.numberOfLeadingZeros(x)\n  }\n\n  private static def isUnique(List<Long> lst) {\n    lst.toSet.size == lst.size\n  }\n\n  private def Map<Long, List<Long>> findSeed(List<Integer> int_keys) {\n    val KNUTH_A = 2654435761L\n    // the Knuth multiplier plus a bunch of random 32 bit primes\n    val MULTIPLIERS = ImmutableList.of(KNUTH_A, 3692456357L, 2257312319L, 3145181107L, 2857830571L, 3361085821L, 4185606979L, 2982027877L)\n\n    for (seed : LongStream.range(0, 1).toArray) {\n      for (mul : MULTIPLIERS) {\n        // hash the keys\n        val longMask = (1L << 32) - 1\n        val mapped_keys = int_keys.map[k | (k.longValue.bitwiseXor(seed) * mul).bitwiseAnd(longMask)]\n\n        // arrange keys into bins\n        val bins = mapped_keys.groupBy[k | k >> g_shift]\n\n        // make sure there are no collisions in (k >> hash_shift) & hash_mask\n        //  within each bin\n        val found_seed = bins.values.forall[isUnique(map[k | (k >> hash_shift).bitwiseAnd(hash_mask)])]\n\n        if (found_seed) {\n          hash_seed = seed\n          multiplier = mul\n          return bins\n        }\n      }\n    }\n\n    throw new RuntimeException(\"Could not find seed - check for duplicate RPC IDs or exhausted namespace allocations\")\n  }\n\n  /**\n   * Finds the g value for the bin, given already assigned slots\n   */\n  def find_gval(List<Long> bin, Set<Long> already_assigned_slots, boolean verbose) {\n    // search for proper value of g\n    for (var gval = 0; gval <= hash_mask; gval++) {\n      val gv = gval\n      // where bin keys would fall\n      val slots = bin.map[k | ((k >> hash_shift) + gv).bitwiseAnd(hash_mask)].toSet\n\n      // sanity check\n      if(slots.size != bin.size) {\n        throw new RuntimeException(\"find_gval: slots not unique\")\n      }\n\n      // would the keys fit nicely?\n      if (Collections.disjoint(slots,already_assigned_slots)) {\n        // update the taken slots with those occupied by the bin\n        already_assigned_slots.addAll(slots)\n        if (verbose) {\n          println(\"allocated slots\" + slots + \" with g \" + gv + \" (k >> g_shift) \" + (bin.get(0) >> g_shift))\n        }\n        return gval\n      }\n    }\n\n    throw new RuntimeException(\"could not find g value!\")\n  }\n\n  new(List<Integer> int_keys, boolean verbose) {\n    n_keys = int_keys.size()\n\n    // how many bits for g\n    n_bits = bit_length((n_keys + 4) / 5)\n    g_size = 1 << n_bits\n    g_shift = if (n_bits == 0) 0 else 32 - n_bits\n\n    // how to compute hash destination\n    hash_bits = bit_length((n_keys* 1.2 + 1).intValue)\n    hash_shift = 32 - n_bits - hash_bits\n    hash_mask = (1 << hash_bits) - 1\n\n    val bins_map = findSeed(int_keys)\n\n    // sort bins by size\n    val bins = bins_map.values.sortBy[size].reverse\n\n    // sanity check\n    if ((n_keys > 0) && (bins.map[size].reduce[p1, p2| p1 + p2] != int_keys.size)) {\n      throw new RuntimeException(\"size sanity check failed!\")\n    }\n\n    // map bins onto the key space\n    val g = new TreeMap<Integer, Integer>\n    val already_assigned_slots = new HashSet<Long>\n\n    for (bin : bins.filter[size > 1]) {\n      if (g.containsKey((bin.get(0) >> g_shift).intValue)) {\n        throw new RuntimeException(\"assertion failed\")\n      }\n      val gval = find_gval(bin, already_assigned_slots, verbose)\n      g.put((bin.get(0) >> g_shift).intValue, gval)\n    }\n\n    // now assign all bins with only one value\n    // we prefer low slots\n    val free_slots = new LinkedList<Integer>\n    for (var i = 0; i <= hash_mask; i++) {\n      if (!already_assigned_slots.contains(i.longValue)) {\n        free_slots.add(i)\n      }\n    }\n\n    for (bin : bins.filter[size == 1]) {\n      val slot = free_slots.pop()\n      val elem = bin.get(0)\n      g.put((elem >> g_shift).intValue, (slot - (elem >> hash_shift) + hash_mask + 1).bitwiseAnd(hash_mask).intValue)\n\n      if (verbose) {\n        println(\"allocated slot \" + slot + \" to \" + (elem >> g_shift) + \" elem \" + elem)\n      }\n    }\n\n    if (verbose) {\n      println(\"num length-1 bins: \" + bins.filter[size==1].size)\n      println(\"g: \" + g)\n    }\n\n    // sanity check: do we have enough g values?\n    if (g.size != bins.size) {\n      throw new RuntimeException(\"somehow didn't compute enough g values\")\n    }\n\n    // map all values and make sure there are no repetitions\n    val locs = bins.flatten.map[k | ((k >> hash_shift) + g.get((k >> g_shift).intValue)).bitwiseAnd(hash_mask)].toSet\n\n    if (verbose) {\n      println(\"len(locs) \" + locs.size + \" len(keys) \" + int_keys.size)\n    }\n\n    if (locs.size != int_keys.size) {\n      throw new RuntimeException(\"somehow locs doesn't contain enough vals\")\n    }\n\n    if (hash_mask + 1 <= 256) {\n      g_type = \"u8\"\n    } else if (hash_mask + 1 <= (1 << 16)) {\n      g_type = \"u16\"\n    } else {\n      g_type = \"u32\"\n    }\n\n    g_array = newIntArrayOfSize(g_size)\n    for (var i = 0; i < g_size; i++) {\n      g_array.set(i, g.getOrDefault(i, 0))\n    }\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/ProtocolGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\n\nclass ProtocolGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"protocol.h\"), generateProtocolH(app))\n    fsa.generateFile(outputPath(app, \"protocol.cc\"), generateProtocolCc(app))\n  }\n\n  private static def generateProtocolH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"hash.h\"\n    #include \"transform_builder.h\"\n\n    #include <jitbuf/perfect_hash.h>\n    «IF app.jit»\n      #include <jitbuf/transform_builder.h>\n    «ENDIF»\n    #include <platform/types.h>\n\n    #include <chrono>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    // The Protocol class handles messages for a single connection.\n    //\n    class Protocol {\n    public:\n      using transform_t = TransformBuilder::transform_t;\n      «IF app.jit»\n        using TransformRecordPtr = std::shared_ptr<jitbuf::TransformRecord>;\n      «ENDIF»\n\n      // Handler function signature.\n      typedef void (*handler_func_t)(void *context, u64 timestamp, char *msg_buf);\n\n      // Type returned by handle() and handle_multiple().\n      struct handle_result_t {\n        int result;\n        std::chrono::nanoseconds client_timestamp;\n      };\n\n      Protocol(TransformBuilder &builder);\n\n      // Handles one message.\n      //\n      // Returns the client's timestamp, as well as the message length on success or an error code.\n      //\n      // Error codes:\n      //   -ENOENT: message was not added\n      //   -EACCES: message was not authenticated\n      //   -EAGAIN: buffer is too small\n      //\n      // Note that handler function might throw.\n      //\n      handle_result_t handle(const char *msg, uint32_t len);\n\n      // Handles multiple consecutive messages.\n      //\n      // Returns the client's timestamp, as well as the length of successfully consumed messages\n      // if at least one message was processed, otherwise like handle().\n      //\n      handle_result_t handle_multiple(const char *msg, u64 len);\n\n      // Adds a handler function for the given RPC ID.\n      void add_handler(u16 rpc_id, void *context, handler_func_t handler_fn);\n\n      «IF app.jit»\n        // Inserts the transform for the given RPC ID.\n        void insert_transform(u16 rpc_id, transform_t transform_fn, u32 size, TransformRecordPtr &transform_record);\n      «ENDIF»\n\n      // Insert an identity transform for the given RPC ID.\n      void insert_identity_transform(u16 rpc_id);\n\n      // Inserts default identity transforms for no-auth messages.\n      void insert_no_auth_identity_transforms();\n\n      // Inserts default identity transforms for need-auth messages.\n      void insert_need_auth_identity_transforms();\n\n    private:\n      TransformBuilder &builder_;\n\n      // Registered handler functions.\n      struct HandlerFunc {\n        void *context;\n        handler_func_t handler_fn;\n      };\n      PerfectHash<HandlerFunc, «app.hashSize», «app.hashFunctor»> handler_funcs_;\n\n      // Registered handler function along with transform.\n      struct HandlerInfo {\n        void *context;\n        handler_func_t handler_fn;\n        transform_t transform_fn;\n        u32 size;\n        «IF app.jit»\n          TransformRecordPtr transform_record;\n        «ENDIF»\n      };\n      PerfectHash<HandlerInfo, «app.hashSize», «app.hashFunctor»> handlers_;\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateProtocolCc(App app) {\n    val messages = app.messages\n\n    /* compute an upper bound on parsed message size */\n    val max_message_size =\n      if (messages.size == 0)\n        0\n      else\n        messages.map[parsed_msg.size].max\n\n    val need_auth_msg = messages.filter[!noAuthorizationNeeded];\n\n    '''\n    «generatedCodeWarning()»\n\n    #include \"protocol.h\"\n    #include \"transform_builder.h\"\n    #include \"parsed_message.h\"\n    #include \"wire_message.h\"\n\n    #include <algorithm>\n    #include <iostream>\n    #include <stdexcept>\n    #include <string>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    Protocol::Protocol(TransformBuilder &builder)\n      : builder_(builder)\n    {}\n\n    Protocol::handle_result_t Protocol::handle(const char *msg, uint32_t len)\n    {\n      «IF app.spans.size == 0»\n        // No spans.\n        return {.result = -EINVAL, .client_timestamp = std::chrono::nanoseconds::zero()};\n      «ELSE»\n        // Size check: should have enough for timestamp and rpc_id.\n        if (len < sizeof(u64) + sizeof(u16)) {\n          /* not enough data to read headers */\n          return {.result = -EAGAIN, .client_timestamp = std::chrono::nanoseconds::zero()};\n        }\n\n        // Handle timestamps.\n        std::chrono::nanoseconds remote_timestamp{*(u64 const *)msg};\n\n        msg += sizeof(u64);\n        len -= sizeof(u64);\n\n        // Get the RPC ID.\n        uint16_t rpc_id = *(uint16_t *)msg;\n\n        // Find the handler for this RPC ID.\n        auto *handler = handlers_.find(rpc_id);\n\n        if (handler == nullptr) {\n          // Compile-time list of RPC IDs that need authentication.\n          constexpr std::size_t need_auth_rpc_ids_count = «need_auth_msg.length»;\n          constexpr u16 need_auth_rpc_ids[] = {«FOR rpc_id : need_auth_msg.map[wire_msg].map[rpc_id].sort SEPARATOR \", \"»«rpc_id»«ENDFOR»};\n\n          if (std::binary_search(need_auth_rpc_ids, need_auth_rpc_ids + need_auth_rpc_ids_count, rpc_id)) {\n            // Permission denied.\n            return {.result = -EACCES, .client_timestamp = remote_timestamp};\n          } else {\n            // Cannot find handler.\n            return {.result = -ENOENT, .client_timestamp = remote_timestamp};\n          }\n        }\n\n        // Safety check for message size.\n        if (len < handler->size) {\n          // Not enough data to read static payload.\n          return {.result = -EAGAIN, .client_timestamp = remote_timestamp};\n        }\n\n        // Apply message transform.\n        u64 dst_buffer[(«max_message_size» + 7) / 8]; /* 64-bit aligned dst */\n        uint16_t size = handler->transform_fn(msg, (char *)dst_buffer);\n\n        // If we didn't get all the dynamic sized part, request more bytes.\n        if (size > len) {\n          // Not enough data to read dynamic payload.\n          return {.result = -EAGAIN, .client_timestamp = remote_timestamp};\n        }\n\n        // Call the handler function.\n        handler->handler_fn(handler->context, remote_timestamp.count(), (char *)dst_buffer);\n\n        return {.result = static_cast<int>(size + sizeof(u64)), .client_timestamp = remote_timestamp};\n      «ENDIF»\n    }\n\n    Protocol::handle_result_t Protocol::handle_multiple(const char *msg, u64 len)\n    {\n      u64 processed = 0;\n      u64 remaining = len;\n      int ret = 0;\n      u16 count = 0;\n      auto client_timestamp = std::chrono::nanoseconds::zero();\n\n      while (len > processed) {\n        auto const handled = handle(msg + processed,\n            (remaining > ((u32)-1) ? ((u32)-1) : remaining));\n        ret = handled.result;\n        client_timestamp = handled.client_timestamp;\n        assert(ret != 0);\n        if (ret < 0) {\n          // Error while handling the message.\n          break;\n        }\n        assert ((u32)ret <= remaining);\n\n        // Sanity check, should not happen.\n        if (((u64)ret + processed > len) || (((u64)ret + processed) < processed)) {\n          throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::handle_multiple: possible overflow\");\n        }\n\n        processed += ret;\n        remaining -= ret;\n        ++count;\n      }\n\n      if (processed > 0) {\n        return {.result = static_cast<int>(processed), .client_timestamp = client_timestamp};\n      }\n\n      // Error, return code (or in edge case of len == 0, returns 0).\n      return {.result = ret, .client_timestamp = client_timestamp};\n    }\n\n    void Protocol::add_handler(u16 rpc_id, void *context, handler_func_t handler_fn)\n    {\n      auto *inserted = handler_funcs_.insert(rpc_id, HandlerFunc{context, handler_fn});\n      if (inserted == nullptr) {\n        throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::add_handler: unable to insert handler_fn for rpc_id=\" + std::to_string(rpc_id));\n      }\n    }\n\n    «IF app.jit»\n    void Protocol::insert_transform(u16 rpc_id, transform_t transform_fn, u32 size, TransformRecordPtr &transform_record)\n    {\n      auto *handler_func = handler_funcs_.find(rpc_id);\n      if (handler_func == nullptr) {\n        throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::insert_transform: handler not found for rpc_id=\" + std::to_string(rpc_id));\n      }\n\n      auto *inserted = handlers_.insert(rpc_id, HandlerInfo{\n          .context = handler_func->context,\n          .handler_fn = handler_func->handler_fn,\n          .transform_fn = transform_fn,\n          .size = size,\n          .transform_record = transform_record,\n      });\n      if (inserted == nullptr) {\n        throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::insert_transform: unable to insert transform for rpc_id=\" + std::to_string(rpc_id));\n      }\n    }\n    «ENDIF»\n\n    void Protocol::insert_identity_transform(u16 rpc_id)\n    {\n      auto *handler_func = handler_funcs_.find(rpc_id);\n      if (handler_func == nullptr) {\n        throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::insert_identity_transform: handler not found for rpc_id=\" + std::to_string(rpc_id));\n      }\n\n      auto *inserted = handlers_.insert(rpc_id, HandlerInfo{\n          .context = handler_func->context,\n          .handler_fn = handler_func->handler_fn,\n          .transform_fn = builder_.get_identity(rpc_id),\n          .size = builder_.get_identity_size(rpc_id),\n          «IF app.jit»\n            .transform_record = nullptr,\n          «ENDIF»\n      });\n      if (inserted == nullptr) {\n        throw std::runtime_error(\"«app.pkg.name»::«app.name»::Protocol::insert_identity_transform: unable to insert identity transform for rpc_id=\" + std::to_string(rpc_id));\n      }\n    }\n\n    void Protocol::insert_no_auth_identity_transforms()\n    {\n      «FOR msg : messages»\n        «IF msg.noAuthorizationNeeded»\n          // «msg.span.name».«msg.name»\n          insert_identity_transform(«msg.wire_msg.rpc_id»);\n        «ENDIF»\n      «ENDFOR»\n    }\n\n    void Protocol::insert_need_auth_identity_transforms()\n    {\n      «FOR msg : messages»\n        «IF !msg.noAuthorizationNeeded»\n          // «msg.span.name».«msg.name»\n          insert_identity_transform(«msg.wire_msg.rpc_id»);\n        «ENDIF»\n      «ENDFOR»\n    }\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/RenderGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.io.ByteArrayOutputStream\n\nimport org.eclipse.emf.ecore.resource.Resource\nimport org.eclipse.xtext.generator.AbstractGenerator\nimport org.eclipse.xtext.generator.IFileSystemAccess2\nimport org.eclipse.xtext.generator.IGeneratorContext\nimport org.eclipse.xtext.resource.SaveOptions\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport io.opentelemetry.render.render.PackageDefinition\n\n/**\n * Generates code from your model files on save.\n *\n * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation\n */\nclass RenderGenerator extends AbstractGenerator {\n\n  AppGenerator appGenerator = new AppGenerator()\n  MetricsGenerator metricsGenerator = new MetricsGenerator()\n  RustCargoGenerator rustCargoGenerator = new RustCargoGenerator()\n\n  override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {\n    val apps = resource.allContents.filter(App).toList()\n\n    // Pack messages.\n    apps.forEach[a | AppPacker.populate(a)]\n\n    // Output a file with the enriched model.\n    {\n      val pkgName = resource.allContents.filter(PackageDefinition).head.name\n      val resourceStream = new ByteArrayOutputStream()\n      val saveOptions = SaveOptions.newBuilder().format().getOptions()\n      resource.save(resourceStream, saveOptions.toOptionsMap())\n      fsa.generateFile(pkgName + \".render.packed\", resourceStream.toString)\n    }\n\n    for (app : apps) {\n      appGenerator.doGenerate(app, fsa)\n    }\n\n    // Generate per-package Rust aggregator crate (bundles all app crates)\n    rustCargoGenerator.doGeneratePackage(apps, fsa)\n\n    metricsGenerator.doGenerate(resource, fsa)\n  }\n\n  static def generatedCodeWarning() {\n    '''\n    /**********************************************\n     * !!! render-generated code, do not modify !!!\n     **********************************************/\n\n    '''\n  }\n\n  /***************************************************************************\n   * FIELD HELPER FUNCTIONS\n   **************************************************************************/\n  static def integerTypeSize(FieldTypeEnum enum_type) {\n    switch (enum_type) {\n        case FieldTypeEnum.S8,\n        case FieldTypeEnum.U8: 1\n        case FieldTypeEnum.S16,\n        case FieldTypeEnum.U16: 2\n        case FieldTypeEnum.S32,\n        case FieldTypeEnum.U32: 4\n        case FieldTypeEnum.S64,\n        case FieldTypeEnum.U64: 8\n        case FieldTypeEnum.S128,\n        case FieldTypeEnum.U128: 16\n        case FieldTypeEnum.STRING:\n          throw new RuntimeException(\"String not supported in hash\")\n    }\n  }\n\n  static def fieldSize(Field field) {\n    val non_array_size =\n      if (field.type.isShortString)\n        field.type.size\n      else\n        integerTypeSize(field.type.enum_type)\n\n    if (field.isArray)\n      non_array_size * field.array_size\n    else\n      non_array_size\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/RustCargoGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\n\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\n\n/**\n * Emits per-app Cargo crate metadata into the generated folder so CMake can\n * run `cargo build` directly from there.\n * Files:\n *  - Cargo.toml with unique package name (encoder_<pkg>_<app>) and rlib output\n *  - src/lib.rs that includes generated wire_messages.rs and encoder.rs\n */\nclass RustCargoGenerator {\n\n  def void doGenerateApp(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"Cargo.toml\"), appCargoToml(app))\n    fsa.generateFile(outputPath(app, \"src/lib.rs\"), appLibRs(app))\n  }\n\n  /**\n   * Emit a per-package aggregator crate that depends on all per-app crates.\n   * This allows linking a single Rust staticlib into C++ binaries that need\n   * multiple apps, avoiding duplicate Rust runtime symbols.\n   *\n   * Layout under the generated output root:\n   *   <pkg>/rust-agg/Cargo.toml\n   *   <pkg>/rust-agg/src/lib.rs\n   */\n  def void doGeneratePackage(Iterable<App> apps, IFileSystemAccess2 fsa) {\n    if (apps === null || apps.empty) return\n    val pkg = apps.head.pkg.name\n    val pathPrefix = pkg + \"/\"\n    fsa.generateFile(pathPrefix + \"Cargo.toml\", packageCargoToml(pkg, apps))\n    fsa.generateFile(pathPrefix + \"src/lib.rs\", packageLibRs(pkg, apps))\n  }\n\n  private static def appCargoToml(App app) {\n    val crate = 'encoder_' + app.pkg.name + '_' + app.name\n    '''\n    [package]\n    name = \"«crate»\"\n    version = \"0.1.0\"\n    edition = \"2021\"\n\n    [lib]\n    # Build as both rlib and staticlib\n    # - rlib: lets Rust binaries depend on this crate via Cargo and expose\n    #         the #[no_mangle] extern \"C\" encoder symbols during Rust linking.\n    # - staticlib: keeps support for direct C/C++ linking where needed.\n    crate-type = [\"rlib\", \"staticlib\"]\n\n    [dependencies]\n    render_parser = { workspace = true }\n    '''\n  }\n\n  private static def appLibRs(App app) {\n    '''\n    #![allow(non_camel_case_types)]\n    #![allow(non_snake_case)]\n    #![allow(unused_variables)]\n\n    use core::ffi::c_char;\n\n    #[repr(C)]\n    pub struct JbBlob {\n        pub buf: *const c_char,\n        pub len: u16,\n    }\n\n    // Modules use the standard Rust module system; files live under src/\n    #[allow(dead_code)]\n    pub mod wire_messages;\n    #[allow(dead_code)]\n    pub mod parsed_message;\n    pub mod encoder;\n    #[allow(dead_code)]\n    pub mod hash;\n    '''\n  }\n\n  private static def packageCargoToml(String pkg, Iterable<App> apps) {\n    val crate = 'encoder_' + pkg + '_all'\n    '''\n    [package]\n    name = \"«crate»\"\n    version = \"0.1.0\"\n    edition = \"2021\"\n\n    [lib]\n    # Aggregator remains a staticlib for C++ consumers; Rust binaries\n    # link per-app crates directly via rlib dependencies.\n    crate-type = [\"staticlib\"]\n\n    [dependencies]\n    «FOR a : apps»encoder_«pkg»_«a.name» = { path = \"«a.name»\" }\n    «ENDFOR»\n    '''\n  }\n\n  private static def packageLibRs(String pkg, Iterable<App> apps) {\n    val sb = new StringBuilder\n    sb.append('''\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(unused_extern_crates)]\n    ''')\n    for (a : apps) {\n      sb.append('''\n      extern crate encoder_«pkg»_«a.name» as _crate_«a.name»;\n      ''')\n    }\n    // Provide a stable symbol so the archive is never empty.\n    sb.append('''\n    #[no_mangle]\n    pub extern \"C\" fn __encoder_«pkg»_bundle_marker() {}\n    ''')\n    sb.toString\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/RustEncoderGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.FieldTypeEnum\n\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\n\n/**\n * Generates for every app:\n *  - Rust FFI implementation (encoder.rs) building the fixed header on a\n *    64-bit aligned stack buffer (like C++), then copying only the used\n *    bytes (timestamp + packed header without tail padding) to the channel\n *    buffer before appending dynamic string payloads.\n */\nclass RustEncoderGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"src/encoder.rs\"), generateRustEncoder(app))\n  }\n\n  private static def generateRustEncoder(App app) {\n    '''\n    // Auto-generated by Render: Rust FFI for «app.pkg.name»::«app.name»\n    #[allow(non_camel_case_types)]\n    #[allow(non_snake_case)]\n    #[allow(unused_variables)]\n\n    #[allow(unused_imports)]\n    use crate::JbBlob;\n    \n    use crate::wire_messages::*;\n\n    use core::slice;\n\n    «FOR msg : app.messages»\n      #[no_mangle]\n      pub extern \"C\" fn «app.pkg.name»_«app.name»_encode_«msg.name»(\n        __dest: *mut u8,\n        __dest_len: u32,\n        __tstamp: u64«FOR field : msg.fields.sortBy[id] BEFORE ',' SEPARATOR ','»\n          «rustParam(field)»\n        «ENDFOR»\n      ) {\n        assert!(!__dest.is_null(), \"dest must not be null\");\n\n        // Compute encoded length: fixed struct size + dynamic payload\n        «IF msg.wire_msg.dynamic_size»\n          let mut __consumed: u32 = «msg.wire_msg.size» as u32;\n          «FOR field : msg.wire_msg.fields.filter[type.enum_type == FieldTypeEnum.STRING]»\n            __consumed = __consumed.saturating_add(«field.name».len as u32);\n          «ENDFOR»\n          __consumed = __consumed.saturating_add(«msg.wire_msg.last_blob_field.name».len as u32);\n          assert!(__consumed <= 0xffff, \"encoded len must fit in u16\");\n        «ELSE»\n          let __consumed: u32 = «msg.wire_msg.size» as u32;\n        «ENDIF»\n\n        let __total_len = (8_u32).saturating_add(__consumed) as usize;\n        assert!(\n          __total_len == __dest_len as usize,\n          \"dest len must exactly match computed encoded len\"\n        );\n\n        // Prepare destination and input slices up front\n        let __dst: &mut [u8] = unsafe { slice::from_raw_parts_mut(__dest, __dest_len as usize) };\n        «FOR field : msg.fields.filter[type.enum_type == FieldTypeEnum.STRING]»\n          let __sl_«field.name»: &[u8] = unsafe { slice::from_raw_parts(«field.name».buf as *const u8, «field.name».len as usize) };\n        «ENDFOR»\n        «FOR field : msg.fields.filter[type.isShortString]»\n          let __sl_«field.name»: &[u8] = unsafe { slice::from_raw_parts(«field.name», «field.size(true)») };\n        «ENDFOR»\n        «FOR field : msg.fields.filter[isArray && !type.isShortString && type.enum_type != FieldTypeEnum.STRING]»\n          let __sl_«field.name»: &[«rustScalarType(field)»] = unsafe { slice::from_raw_parts(«field.name», «field.array_size») };\n        «ENDFOR»\n\n        // Build fixed header as a stack struct using an initializer expression\n        let __wire: «msg.wire_msg.struct_name» = «msg.wire_msg.struct_name» {\n          _rpc_id: «msg.wire_msg.rpc_id» as u16,\n          «IF msg.wire_msg.dynamic_size»\n            _len: __consumed as u16,\n          «ENDIF»\n          «FOR field : msg.wire_msg.fields SEPARATOR \",\"»\n            «IF field.isArray && field.type.isShortString»\n              «field.name»: {\n                let mut __tmp = [[0u8; «field.type.size»]; «field.array_size»];\n                let __elem_len = «field.type.size»;\n                for (__i, __chunk) in __sl_«field.name».chunks_exact(__elem_len).enumerate() {\n                  __tmp[__i].copy_from_slice(__chunk);\n                }\n                __tmp\n              }\n            «ELSEIF field.isArray || field.type.isShortString»\n              «field.name»: __sl_«field.name».try_into().unwrap()\n            «ELSEIF field.type.enum_type == FieldTypeEnum.STRING»\n              «field.name»: (__sl_«field.name».len() as u16)\n            «ELSE»\n              «field.name»\n            «ENDIF»\n          «ENDFOR»\n        };\n\n        // Copy timestamp and packed header (without struct tail padding)\n        let __fixed_len = 8usize + «msg.wire_msg.size» as usize;\n        __dst[..8].copy_from_slice(&__tstamp.to_ne_bytes());\n        let __src_struct: &[u8] = unsafe { slice::from_raw_parts(&__wire as *const _ as *const u8, «msg.wire_msg.size» as usize) };\n        __dst[8..__fixed_len].copy_from_slice(__src_struct);\n\n        // Append dynamic payloads sequentially\n        «IF msg.wire_msg.dynamic_size»\n          let mut __off = __fixed_len;\n          «FOR field : msg.wire_msg.fields.filter[type.enum_type == FieldTypeEnum.STRING] + #[msg.wire_msg.last_blob_field]»\n            if !__sl_«field.name».is_empty() {\n              let __len = __sl_«field.name».len();\n              let __dst_seg = &mut __dst[__off .. __off + __len];\n              __dst_seg.copy_from_slice(__sl_«field.name»);\n              __off += __len;\n            }\n          «ENDFOR»\n        «ENDIF»\n      }\n    «ENDFOR»\n    '''\n  }\n\n  private static def String rustParam(Field field) {\n    if (field.type.enum_type == FieldTypeEnum.STRING) {\n      return '''«field.name»: JbBlob'''\n    }\n    if (field.type.isShortString || field.isArray) {\n      val elem = rustScalarType(field)\n      return '''«field.name»: *const «elem»'''\n    }\n    return '''«field.name»: «rustScalarType(field)»'''\n  }\n\n  private static def String rustScalarType(Field field) {\n    switch (field.type.enum_type) {\n      case FieldTypeEnum.U8: 'u8'\n      case FieldTypeEnum.U16: 'u16'\n      case FieldTypeEnum.U32: 'u32'\n      case FieldTypeEnum.U64: 'u64'\n      case FieldTypeEnum.U128: 'u128'\n      case FieldTypeEnum.S8: 'i8'\n      case FieldTypeEnum.S16: 'i16'\n      case FieldTypeEnum.S32: 'i32'\n      case FieldTypeEnum.S64: 'i64'\n      case FieldTypeEnum.S128: 'i128'\n      case FieldTypeEnum.STRING: 'u16' // not used here\n    }\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/RustMessageGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.FieldTypeEnum\n\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\n\n/**\n * Generates both wire_messages.rs and parsed_message.rs for every app.\n * - wire_messages.rs: #[repr(C)] wire structs mirroring C++ wire_message.h,\n *   with simple layout tests and metadata() for render_parser.\n * - parsed_message.rs: ergonomic parsed structs (message-name identifiers)\n *   plus decode() from a wire body slice into owned Strings for dynamic fields.\n */\nclass RustMessageGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"src/wire_messages.rs\"), generateWireMessages(app))\n    fsa.generateFile(outputPath(app, \"src/parsed_message.rs\"), generateParsedMessages(app))\n  }\n\n  private static def generateWireMessages(App app) {\n    '''\n    «generatedCodeWarning()»\n    #[allow(non_camel_case_types)]\n    #[allow(non_snake_case)]\n    #[allow(dead_code)]\n\n    «FOR msg : app.messages»\n    «val xmsg = msg.wire_msg»\n      #[repr(C)]\n      #[derive(Copy, Clone)]\n      pub struct «xmsg.struct_name» {\n        pub _rpc_id: u16,\n        «IF xmsg.dynamic_size»\n        pub _len: u16,\n        «ENDIF»\n        «FOR field : xmsg.fields»\n          «IF field.isArray && field.type.isShortString»\n            pub «field.name»: [[u8; «field.type.size»]; «field.array_size»],\n          «ELSEIF field.type.isShortString»\n            pub «field.name»: [u8; «field.type.size»],\n          «ELSEIF field.isArray»\n            pub «field.name»: [«rustScalarType(field)»; «field.array_size»],\n          «ELSE»\n            pub «field.name»: «rustScalarType(field)»,\n          «ENDIF»\n        «ENDFOR»\n      }\n\n      impl «xmsg.struct_name» {\n        #[inline]\n        pub fn metadata() -> render_parser::MessageMetadata {\n          «IF xmsg.dynamic_size»\n            render_parser::MessageMetadata::new_dynamic(«xmsg.rpc_id»u16, «!msg.noAuthorizationNeeded»)\n          «ELSE»\n            render_parser::MessageMetadata::new_fixed(«xmsg.rpc_id»u16, «xmsg.size», «!msg.noAuthorizationNeeded»)\n          «ENDIF»\n        }\n      }\n\n      impl Default for «xmsg.struct_name» {\n        #[inline]\n        fn default() -> Self { unsafe { core::mem::zeroed() } }\n      }\n\n      pub const «msg.name.toUpperCase»_WIRE_SIZE: usize = «xmsg.size»;\n\n      #[cfg(test)]\n      mod «msg.name»_layout_tests {\n        use super::*;\n        use core::mem::{offset_of, align_of};\n        #[test]\n        fn struct_size() {\n          let size = size_of::<«xmsg.struct_name»>();\n          let align = align_of::<«xmsg.struct_name»>();\n          let padded_raw_size = («msg.name.toUpperCase»_WIRE_SIZE + align - 1) / align * align;\n          assert_eq!(size, padded_raw_size);\n        }\n        #[test]\n        fn field_offsets() {\n          assert_eq!(offset_of!(«xmsg.struct_name», _rpc_id), 0);\n          «IF xmsg.dynamic_size»\n            assert_eq!(offset_of!(«xmsg.struct_name», _len), 2);\n          «ENDIF»\n          «FOR field : xmsg.fields»\n            assert_eq!(offset_of!(«xmsg.struct_name», «field.name»), «field.wire_pos»usize);\n          «ENDFOR»\n        }\n      }\n    «ENDFOR»\n\n    #[inline]\n    pub fn all_message_metadata() -> ::std::vec::Vec<render_parser::MessageMetadata> {\n      ::std::vec![\n        «FOR msg : app.messages»\n          «msg.wire_msg.struct_name»::metadata(),\n        «ENDFOR»\n      ]\n    }\n\n    '''\n  }\n\n  private static def generateParsedMessages(App app) {\n    // Validate unsupported features at generation-time: arrays of dynamic strings\n    if (app.messages.exists[m | m.fields.exists[f | f.isArray && f.type.enum_type == FieldTypeEnum.STRING]]) {\n      throw new RuntimeException(\"arrays of dynamic strings are not supported in Rust parsed decode\")\n    }\n\n    '''\n    «generatedCodeWarning()»\n    #[allow(non_camel_case_types)]\n    #[allow(non_snake_case)]\n    #[allow(dead_code)]\n\n    // For slice -> array conversions in from_ne_bytes calls\n    #[allow(unused_imports)]\n    use core::convert::TryInto;\n\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    pub enum DecodeError {\n      BufferTooSmall,\n      InvalidRpcId { got: u16 },\n      InvalidLength { len: u16 },\n      Utf8 { field: &'static str },\n    }\n\n    «FOR msg : app.messages»\n      «val w = msg.wire_msg»\n      // Parsed struct for «msg.name»\n      pub struct «msg.name» {\n        pub _rpc_id: u16,\n        «FOR field : msg.fields.sortBy[id]»\n          «IF field.type.isShortString && field.isArray»\n            pub «field.name»: [[u8; «field.type.size»]; «field.array_size»],\n          «ELSEIF field.type.isShortString»\n            pub «field.name»: [u8; «field.type.size»],\n          «ELSEIF field.type.enum_type == FieldTypeEnum.STRING»\n            pub «field.name»: ::std::string::String,\n          «ELSEIF field.isArray»\n            pub «field.name»: [«rustScalarType(field)»; «field.array_size»],\n          «ELSE»\n            pub «field.name»: «rustScalarType(field)»,\n          «ENDIF»\n        «ENDFOR»\n      }\n\n      impl «msg.name» {\n        pub const RPC_ID: u16 = «w.rpc_id»u16;\n\n        #[inline]\n        pub fn decode(body: &[u8]) -> Result<Self, DecodeError> {\n          // Require rpc_id\n          if body.len() < 2 { return Err(DecodeError::BufferTooSmall); }\n          let mut b2 = [0u8;2];\n          b2.copy_from_slice(&body[0..2]);\n          let rpc = u16::from_ne_bytes(b2);\n          if rpc != Self::RPC_ID { return Err(DecodeError::InvalidRpcId { got: rpc }); }\n\n          «IF w.dynamic_size»\n            if body.len() < 4 { return Err(DecodeError::BufferTooSmall); }\n            let mut b2 = [0u8;2];\n            b2.copy_from_slice(&body[2..4]);\n            let __len = u16::from_ne_bytes(b2);\n            if __len < 4 { return Err(DecodeError::InvalidLength { len: __len }); }\n            if body.len() < __len as usize { return Err(DecodeError::BufferTooSmall); }\n          «ELSE»\n            if body.len() < «w.size»usize { return Err(DecodeError::BufferTooSmall); }\n          «ENDIF»\n\n          // Decode fixed header fields\n          «FOR field : msg.fields.sortBy[id]»\n            «IF field.type.enum_type == FieldTypeEnum.STRING»\n              // dynamic string; decode later from payload\n            «ELSEIF field.type.isShortString && field.isArray»\n              let «field.name» = {\n                let mut tmp = [[0u8; «field.type.size»]; «field.array_size»];\n                let elem = «field.type.size»usize;\n                let mut i = 0usize;\n                while i < «field.array_size»usize {\n                  let off = «field.wire_pos»usize + i * elem;\n                  tmp[i].copy_from_slice(&body[off .. off + elem]);\n                  i += 1;\n                }\n                tmp\n              };\n            «ELSEIF field.type.isShortString»\n              let «field.name» = {\n                let mut tmp = [0u8; «field.type.size»];\n                let off = «field.wire_pos»usize;\n                tmp.copy_from_slice(&body[off .. off + «field.type.size»usize]);\n                tmp\n              };\n            «ELSEIF field.isArray»\n              let «field.name» = {\n                let mut tmp = [«defaultValue(field)»; «field.array_size»];\n                let es = «elemSize(field)»usize;\n                let mut i = 0usize;\n                while i < «field.array_size»usize {\n                  let off = «field.wire_pos»usize + i * es;\n                  tmp[i] = «readScalar(field, 'off')»;\n                  i += 1;\n                }\n                tmp\n              };\n            «ELSE»\n              let «field.name» = «readScalar(field, field.wire_pos + \"usize\")»;\n            «ENDIF»\n          «ENDFOR»\n\n          // Decode dynamic payload strings\n          «IF w.dynamic_size»\n            let mut __off = «w.size»usize;\n            «FOR field : msg.fields.filter[type.enum_type == FieldTypeEnum.STRING].sortBy[id]»\n              «IF field != w.last_blob_field»\n                // length from header\n                let mut __b = [0u8;2];\n                __b.copy_from_slice(&body[«field.wire_pos»usize .. «field.wire_pos»usize + 2]);\n                let __l_«field.name» = u16::from_ne_bytes(__b) as usize;\n                if __off + __l_«field.name» > body.len() { return Err(DecodeError::BufferTooSmall); }\n                let «field.name» = if __l_«field.name» == 0 { ::std::string::String::new() } else { ::std::string::String::from_utf8_lossy(&body[__off .. __off + __l_«field.name»]).into_owned() };\n                __off += __l_«field.name»;\n              «ELSE»\n                let __tail = (__len as usize).saturating_sub(__off);\n                if __off + __tail > body.len() { return Err(DecodeError::BufferTooSmall); }\n                let «field.name» = if __tail == 0 { ::std::string::String::new() } else { ::std::string::String::from_utf8_lossy(&body[__off .. __off + __tail]).into_owned() };\n              «ENDIF»\n            «ENDFOR»\n          «ENDIF»\n\n          Ok(Self {\n            _rpc_id: rpc,\n            «FOR field : msg.fields.sortBy[id] SEPARATOR \",\"»\n              «field.name»: «field.name»\n            «ENDFOR»\n          })\n        }\n      }\n    «ENDFOR»\n    '''\n  }\n\n  private static def String rustScalarType(Field field) {\n    switch (field.type.enum_type) {\n      case FieldTypeEnum.U8: 'u8'\n      case FieldTypeEnum.U16: 'u16'\n      case FieldTypeEnum.U32: 'u32'\n      case FieldTypeEnum.U64: 'u64'\n      case FieldTypeEnum.U128: 'u128'\n      case FieldTypeEnum.S8: 'i8'\n      case FieldTypeEnum.S16: 'i16'\n      case FieldTypeEnum.S32: 'i32'\n      case FieldTypeEnum.S64: 'i64'\n      case FieldTypeEnum.S128: 'i128'\n      case FieldTypeEnum.STRING: 'u16' // not used for parsed; handled separately\n    }\n  }\n\n  private static def String defaultValue(Field field) {\n    switch (field.type.enum_type) {\n      case FieldTypeEnum.U8: '0u8'\n      case FieldTypeEnum.U16: '0u16'\n      case FieldTypeEnum.U32: '0u32'\n      case FieldTypeEnum.U64: '0u64'\n      case FieldTypeEnum.U128: '0u128'\n      case FieldTypeEnum.S8: '0i8'\n      case FieldTypeEnum.S16: '0i16'\n      case FieldTypeEnum.S32: '0i32'\n      case FieldTypeEnum.S64: '0i64'\n      case FieldTypeEnum.S128: '0i128'\n      case FieldTypeEnum.STRING: '0u16'\n    }\n  }\n\n  private static def String elemSize(Field field) {\n    // Element size in bytes for array elements in the wire header\n    if (field.type.isShortString) {\n      return field.type.size.toString\n    }\n    switch (field.type.enum_type) {\n      case FieldTypeEnum.U8,\n      case FieldTypeEnum.S8: '1'\n      case FieldTypeEnum.U16,\n      case FieldTypeEnum.S16: '2'\n      case FieldTypeEnum.U32,\n      case FieldTypeEnum.S32: '4'\n      case FieldTypeEnum.U64,\n      case FieldTypeEnum.S64: '8'\n      case FieldTypeEnum.U128,\n      case FieldTypeEnum.S128: '16'\n      case FieldTypeEnum.STRING: '2'\n    }\n  }\n\n  private static def String readScalar(Field field, Object offExpr) {\n    // Generates an expression to read a scalar of the field's element type from body at offset offExpr\n    switch (field.type.enum_type) {\n      case FieldTypeEnum.U8: '''body[«offExpr»]'''\n      case FieldTypeEnum.S8: '''(body[«offExpr»] as i8)'''\n      case FieldTypeEnum.U16: '''u16::from_ne_bytes(body[«offExpr» .. «offExpr» + 2].try_into().unwrap())'''\n      case FieldTypeEnum.S16: '''i16::from_ne_bytes(body[«offExpr» .. «offExpr» + 2].try_into().unwrap())'''\n      case FieldTypeEnum.U32: '''u32::from_ne_bytes(body[«offExpr» .. «offExpr» + 4].try_into().unwrap())'''\n      case FieldTypeEnum.S32: '''i32::from_ne_bytes(body[«offExpr» .. «offExpr» + 4].try_into().unwrap())'''\n      case FieldTypeEnum.U64: '''u64::from_ne_bytes(body[«offExpr» .. «offExpr» + 8].try_into().unwrap())'''\n      case FieldTypeEnum.S64: '''i64::from_ne_bytes(body[«offExpr» .. «offExpr» + 8].try_into().unwrap())'''\n      case FieldTypeEnum.U128: '''u128::from_ne_bytes(body[«offExpr» .. «offExpr» + 16].try_into().unwrap())'''\n      case FieldTypeEnum.S128: '''i128::from_ne_bytes(body[«offExpr» .. «offExpr» + 16].try_into().unwrap())'''\n      case FieldTypeEnum.STRING: '''u16::from_ne_bytes(body[«offExpr» .. «offExpr» + 2].try_into().unwrap())'''\n    }\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/SpanAutoDependencies.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.util.Set\nimport java.util.Map\nimport java.util.List\nimport java.util.HashMap\nimport java.util.HashSet\nimport java.util.Vector\nimport java.util.LinkedList\n\nimport com.google.common.collect.Sets\n\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.Reference\nimport io.opentelemetry.render.render.Definition\nimport io.opentelemetry.render.render.ReferenceBindingRoot\nimport io.opentelemetry.render.render.ReferenceBindingValue\nimport io.opentelemetry.render.render.ReferenceBindingRef\n\nclass SpanAutoDependencies {\n\n  /**\n   * All prerequisites used by auto references.\n   */\n  public final Set<Definition> prereqs\n\n  /**\n   * All non-dynamic prerequisites (fields, manual references) used by auto references.\n   */\n  public final Set<Definition> nonDynamicPrereqs\n\n  /**\n   * The set of prereqs for each auto reference.\n   */\n  public final Map<Reference, Set<Definition>> refPrereqs\n\n  /**\n   * The set of non-dynamic prereqs for each auto reference.\n   */\n  public final Map<Reference, Set<Definition>> refNonDynamicPrereqs\n\n  /**\n   * A valid ordering of all references according to dependencies.\n   */\n  public final List<Reference> computeOrder;\n\n  new(Span span) {\n    prereqs = new HashSet<Definition>()\n    refPrereqs = new HashMap<Reference, Set<Definition>>()\n\n    /* first, collect all the prereqs of auto/cached references */\n    for (ref : span.definitions.filter(Reference).filter[isAuto || isIsCached]) {\n      val deps = new HashSet<Definition>()\n      /* for each binding, reach the root entity */\n      for (binding : ref.bindings) {\n        var ReferenceBindingRef traverse = binding.value\n        /* we want the ReferenceBindingRoot */\n        while (traverse instanceof ReferenceBindingValue) {\n          traverse = traverse.ref\n        }\n        deps.add((traverse as ReferenceBindingRoot).entity)\n      }\n\n      /* set the ref's prereqs */\n      refPrereqs.put(ref, deps)\n    }\n\n    /* find a topological order of computing auto/cached references: */\n\n    /* the graph of dependencies between references */\n    val topoGraph = refPrereqs.mapValues[filter(Reference).filter[isAuto || isCached].toSet]\n\n     /* in-degrees in the graph, used to find all sources */\n    val inDegrees = topoGraph.mapValues[size]\n\n    /* start with all sources in the original graph */\n    val queue = new LinkedList<Reference>(inDegrees.filter[k,v | v == 0].keySet())\n\n    val fullComputeOrder = new Vector<Reference>()\n\n    while (!queue.empty) {\n      /* dequeue */\n      val ref = queue.poll()\n\n      /* add to topological ordering */\n      fullComputeOrder.add(ref)\n\n      /* remove from graph: update inDegrees */\n      for (v : topoGraph.get(ref)) {\n        val newDegree = inDegrees.get(v) - 1\n\n        inDegrees.put(v, newDegree)\n\n        /* if v became a source, it can be queued */\n        if (newDegree == 0) {\n          queue.add(v)\n        }\n      }\n    }\n\n    /* check if there is a cycle */\n    if (!(inDegrees.values.filter[degree | degree != 0].empty)) {\n      throw new RuntimeException('''Dependencies in span \"«span.name»\" have a cycle;\n        refs:«FOR ref: inDegrees.filter[k,v | v != 0].keySet()» «ref.name»«ENDFOR»''')\n    }\n\n    /* for each reference, we compute a closure on all its dependencies */\n    for (ref : fullComputeOrder) {\n      /* get direct prereqs */\n      val deps = refPrereqs.get(ref)\n\n      /* which direct prereqs have prereqs of their own */\n      val otherReferences = deps.filter(Reference).filter[isAuto || isCached].toList\n\n      /* add our prereqs' prereqs. because we're iterating in topological\n       * order, this is sufficient to compute closure (the prereqs of\n       * references earlier in the ordering are already closed)\n       */\n      for (other : otherReferences) {\n        deps.addAll(refPrereqs.get(other))\n      }\n\n      refPrereqs.put(ref, deps)\n    }\n\n    /* can now union all prereqs of auto references to get all prereqs */\n    for (deps : refPrereqs.filter[k,v | k.isAuto].values()) {\n      prereqs.addAll(deps)\n    }\n\n    /* in the compute_order, we only need to compute auto references, or\n     * cached references that an auto reference depends on */\n    computeOrder = fullComputeOrder.filter[ref | ref.isAuto || prereqs.contains(ref)].toList\n\n    nonDynamicPrereqs = prereqs.filter[defn |\n        (defn instanceof Field) ||\n        ( (defn instanceof Reference) &&\n         !((defn as Reference).isAuto || (defn as Reference).isCached))\n    ].toSet\n\n    refNonDynamicPrereqs = refPrereqs.mapValues[v | Sets.intersection(v, nonDynamicPrereqs)]\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/SpanGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport java.util.Arrays\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.AggregationMethod\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.Reference\nimport io.opentelemetry.render.render.ReferenceBindingRef\nimport io.opentelemetry.render.render.ReferenceBindingRoot\nimport io.opentelemetry.render.render.ReferenceBindingValue\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Aggregation\nimport io.opentelemetry.render.render.Message\nimport static io.opentelemetry.render.generator.AppPacker.pulseMessageName\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static io.opentelemetry.render.generator.RenderGenerator.integerTypeSize\nimport static io.opentelemetry.render.generator.RenderGenerator.fieldSize\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\nimport static extension io.opentelemetry.render.extensions.SpanExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\n\n/**\n * Generates code from your model files on save.\n *\n * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation\n */\nclass SpanGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"index.h\"), generateIndexH(app))\n    fsa.generateFile(outputPath(app, \"index.cc\"), generateIndexCc(app))\n\n    fsa.generateFile(outputPath(app, \"containers.h\"), generateContainersH(app))\n    fsa.generateFile(outputPath(app, \"containers.inl\"), generateContainersInl(app))\n    fsa.generateFile(outputPath(app, \"containers.cc\"), generateContainersCc(app))\n\n    fsa.generateFile(outputPath(app, \"keys.h\"), generateKeysH(app))\n\n    fsa.generateFile(outputPath(app, \"handles.h\"), generateHandlesH(app))\n    fsa.generateFile(outputPath(app, \"handles.cc\"), generateHandlesCc(app))\n\n    fsa.generateFile(outputPath(app, \"auto_handles.h\"), generateAutoHandlesH(app))\n    fsa.generateFile(outputPath(app, \"auto_handles.cc\"), generateAutoHandlesCc(app))\n\n    fsa.generateFile(outputPath(app, \"weak_refs.h\"), generateWeakRefsH(app))\n    fsa.generateFile(outputPath(app, \"weak_refs.inl\"), generateWeakRefsInl(app))\n    fsa.generateFile(outputPath(app, \"weak_refs.cc\"), generateWeakRefsCc(app))\n\n    fsa.generateFile(outputPath(app, \"modifiers.h\"), generateModifiersH(app))\n    fsa.generateFile(outputPath(app, \"modifiers.cc\"), generateModifiersCc(app))\n\n    fsa.generateFile(outputPath(app, \"spans.h\"), generateSpansH(app))\n    fsa.generateFile(outputPath(app, \"spans.cc\"), generateSpansCc(app))\n\n    fsa.generateFile(outputPath(app, \"span_base.h\"), generateSpanBaseH(app))\n  }\n\n  /**\n   * Generates metric update code for aggregation node\n   *\n   * |agg|: metric aggregator\n   * |store_name|: the name of MetricStore object\n   * |loc|: location\n   * |t|: timestamp of the metric\n   * |metric|: the metric to be merged into MetricStore\n   * |is_root|: whether the update is for root.\n   */\n  static def generateMetricUpdate(Aggregation agg, String store_name, String loc, String t, String metric, boolean is_root) {\n    '''\n    auto it = «store_name».lookup(«loc», «t», true);\n    if (it.first == false) {\n      /* just enqueued, assign stats */\n      «IF is_root»\n        «FOR field : agg.type.fields»\n          «IF field.method == AggregationMethod::TDIGEST»\n            it.second.«field.name».add(«metric».«field.name»);\n          «ELSE»\n            it.second.m.«field.name» = «metric».«field.name»;\n          «ENDIF»\n        «ENDFOR»\n      «ELSE»\n        it.second = «metric»;\n      «ENDIF»\n      /* also increase the reference count */\n      map[«loc»].__refcount++;\n    } else {\n      «IF is_root»\n        «FOR field : agg.type.fields»\n          «IF field.method == AggregationMethod::TDIGEST»\n            it.second.«field.name».add(«metric».«field.name»);\n          «ELSE»\n            it.second.m.«field.name» += «metric».«field.name»;\n          «ENDIF»\n        «ENDFOR»\n      «ELSE»\n        «FOR field : agg.type.fields»\n          «IF field.method == AggregationMethod::TDIGEST»\n            it.second.«field.name».merge(«metric».«field.name»);\n          «ELSE»\n            it.second.«field.name» += «metric».«field.name»;\n          «ENDIF»\n        «ENDFOR»\n      «ENDIF»\n    }\n    '''\n  }\n\n  /***************************************************************************\n   * Generate _foreach function for metric stores in an aggregation node.\n   *\n   * |agg|: Aggregation struct\n   * |span_name|: name of current span.\n   * |is_rollup|: whether this is for time-based roll-up\n   * |rollup_count|: how many intervals is the roll-up\n   **************************************************************************/\n  static def generateMetricForeach(Aggregation agg, String span_name, boolean is_rollup, int rollup_count) {\n    var store_name = agg.name;\n    if (is_rollup) {\n      store_name = store_name + \"_\" + rollup_count;\n    }\n\n    '''\n    template<class FUNCTOR>\n    void «span_name»::«store_name»_foreach(u64 t, FUNCTOR &&f)\n    {\n      «IF is_rollup»\n        constexpr u64 interval = u64(«agg.interval» * 1e9) * «rollup_count»;\n      «ELSE»\n        constexpr u64 interval = u64(«agg.interval» * 1e9);\n      «ENDIF»\n\n      auto &store = «store_name»;\n      auto &queue = store.current_queue();\n\n      s16 relative_timeslot = store.relative_timeslot(t);\n\n      if (relative_timeslot <= 0) {\n        /* not ready */\n        return;\n      }\n\n      double slot_duration = store.slot_duration();\n      u64 metric_timestamp = t - (u64)(relative_timeslot * slot_duration);\n\n      while (!queue.empty()) {\n        /* get the next loc */\n        u32 loc = queue.peek();\n        /* get the metrics entry for that loc */\n        «IF agg.isRoot && !is_rollup»\n          auto &metrics = store.lookup_relative(loc, 0, false).second.get_metrics();\n        «ELSE»\n          auto &metrics = store.lookup_relative(loc, 0, false).second;\n        «ENDIF»\n        /* get a reference to the span with the metric */\n        auto span = at(loc);\n\n        /* call the functor */\n        f(metric_timestamp, span, metrics, interval);\n\n        «IF !is_rollup»\n        /* propagate updates on the aggregation tree */\n        «FOR update : agg.updates»\n          /* update «update.ref.name».«update.agg.name»: */\n          {\n            auto update_weak_ref = span.«update.ref.name»();\n            if (update_weak_ref.valid()) {\n              update_weak_ref.«update.agg.name»_update(metric_timestamp, metrics);\n            }\n          }\n        «ENDFOR»\n\n        «FOR rollup : agg.rollups»\n          {\n            /* time-based rollup to «rollup.rollup_count» times of interval */\n            auto loc = span.loc();\n            «generateMetricUpdate(agg, agg.name + \"_\" + rollup.rollup_count, \"loc\", \"t\", \"metrics\", false)»\n          }\n        «ENDFOR»\n        «ENDIF»\n\n        /* return the reference count for the metric */\n        put(loc);\n\n        queue.pop();\n      }\n\n      /* done. advance the current timeslot */\n      store.advance();\n    }\n    '''\n  }\n\n\n  /***************************************************************************\n   * Proxy methods\n   **************************************************************************/\n\n  static def proxyMethodDeclaration(Message msg) {\n    '''\n    void «msg.name»(«msg.norefPrototype»);\n    void «msg.name»_tstamp(u64 ts«msg.norefCommaPrototype»);\n    '''\n  }\n\n  static def proxyMethodDefinition(Span span, Message msg) {\n    '''\n\n    void «span.name»::«msg.name»(«msg.norefPrototype»)\n    {\n      «proxyMethodInvocation(span, msg, 'index_', 'loc_', 'span_ptr_->shard_id_')»\n    }\n\n    void «span.name»::«msg.name»_tstamp(u64 ts«msg.norefCommaPrototype»)\n    {\n      «proxyMethodTimestampInvocation(span, msg, 'index_', 'ts', 'loc_', 'span_ptr_->shard_id_')»\n    }\n    '''\n  }\n\n  static def proxyMethodInvocation(Span span, Message msg, String index, String ref, String shard_id) {\n    val remote_app_name = msg.span.app.name;\n    val writers = index + '.' + remote_app_name + '_writers_';\n\n    if (span.sharding !== null) {\n      '''\n      auto &writer = «writers»[«shard_id»];\n      writer.«msg.name»(«ref»«msg.norefCommaCallPrototype»);\n      '''\n    } else {\n      '''\n      for (auto &writer : «writers») {\n        writer.«msg.name»(«ref»«msg.norefCommaCallPrototype»);\n      }\n      '''\n    }\n  }\n\n  static def proxyMethodInvocation(Span span, Message msg, String index, String ref) {\n    proxyMethodInvocation(span, msg, index, ref, 'shard_id');\n  }\n\n  static def proxyMethodTimestampInvocation(Span span, Message msg, String index, String ts, String ref, String shard_id) {\n    val remote_app_name = msg.span.app.name;\n    val writers = index + '.' + remote_app_name + '_writers_';\n\n    if (span.sharding !== null) {\n      '''\n      auto &writer = «writers»[«shard_id»];\n      writer.«msg.name»_tstamp(«ts», «ref»«msg.norefCommaCallPrototype»);\n      '''\n    } else {\n      '''\n      for (auto &writer : «writers») {\n        writer.«msg.name»_tstamp(«ts», «ref»«msg.norefCommaCallPrototype»);\n      }\n      '''\n    }\n  }\n\n  /***************************************************************************\n   * FIELD HELPER FUNCTIONS\n   **************************************************************************/\n\n  static def generateField(Field field) {\n    generateField(field, \"\")\n  }\n\n  static def generateField(Field field, String variable_prefix) {\n    '''«field.cType» «variable_prefix»«field.name»;'''\n  }\n\n  static def generateFieldTypeInfo(Field field) {\n    '''typedef «field.cType» «field.name»_t;'''\n  }\n\n  /***************************************************************************\n   * HANDLE HELPER FUNCTIONS\n   **************************************************************************/\n  static def locationTypeForHandle(Span span) {\n    switch span.pool_size {\n      case span.pool_size < (1 << 16):   \"u16\"\n      default:  \"u32\"\n    }\n  }\n\n  static def invalidConstForHandle(Span span) {\n    switch span.pool_size {\n      case span.pool_size < (1 << 16):   ((1L << 16) - 1)\n      default:              ((1L << 32) - 1)\n    }\n  }\n\n  /***************************************************************************\n   * INDEX H\n   **************************************************************************/\n\n  static def indexConstructorSignature(App app) {\n    '''«FOR ran : app.remoteApps.map[name].sort SEPARATOR \", \"»std::vector<::«app.pkg.name»::«ran»::Writer> «ran»_writers«ENDFOR»'''\n  }\n\n  static def generateIndexH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"containers.h\"\n    «FOR remote_app : app.remoteApps»\n    #include <generated/«remote_app.pkg.name»/«remote_app.name»/writer.h>\n    «ENDFOR»\n\n    #include <functional>\n    #include <ostream>\n    #include <string>\n    #include <vector>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /**\n     * Index: a container for all types of spans, with reference counting.\n     *\n     * Each container for a type of span keeps constant-sized pools of that\n     * span, maps so spans can be found by keys, and metric stores for\n     * metrics associated with the span\n     */\n    class Index {\n    public:\n      /**\n       * C'tor\n       */\n      Index(«indexConstructorSignature(app)»);\n\n      «FOR span : app.spans»\n        /**\n         * Container for type «span.name»\n         */\n        containers::«span.name» «span.name»;\n      «ENDFOR»\n\n      /**\n       * Extract size statistics for each container type.\n       *\n       * The given functor is called with (span name, num_allocated_spans, pool_size)\n       */\n      using size_statistics_cb =\n        std::function<void(\n          std::string_view span_name,\n          std::size_t allocated,\n          std::size_t max_allocated,\n          std::size_t pool_size)>;\n      void size_statistics(size_statistics_cb f);\n\n      /**\n       * forbid copy constructor\n       */\n      Index(const Index&) = delete;\n\n      /**\n       * forbid assignment operator\n       */\n      Index& operator=(const Index&) = delete;\n\n      /**\n       * Send a heartbeat pulse to all downstream peers\n       */\n      void send_pulse();\n\n      «FOR remote_app_name : app.remoteApps.map[name].sort»\n        /* Writer for sending proxy span messages to «remote_app_name» app */\n        std::vector<::«app.pkg.name»::«remote_app_name»::Writer> «remote_app_name»_writers_;\n      «ENDFOR»\n\n      void dump_json(std::ostream &out) const;\n\n      friend std::ostream &operator <<(std::ostream &out, Index const &what) {\n        what.dump_json(out);\n        return out;\n      }\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * INDEX CC\n   **************************************************************************/\n\n  static def generateIndexCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"index.h\"\n\n    «app.pkg.name»::«app.name»::Index::Index(«indexConstructorSignature(app)»)\n    «FOR span : app.spans BEFORE \" : \" SEPARATOR \",\"»\n      «span.name»()\n    «ENDFOR»\n    «FOR ran : app.remoteApps.map[name].sort BEFORE \", \" SEPARATOR \",\"»\n      «ran»_writers_(std::move(«ran»_writers))\n    «ENDFOR»\n    {}\n\n    void «app.pkg.name»::«app.name»::Index::size_statistics(size_statistics_cb f)\n    {\n      «FOR span : app.spans»\n        f(\"«span.name»\", «span.name».size(), «span.name».max_size(), «span.pool_size»);\n      «ENDFOR»\n    }\n\n    void «app.pkg.name»::«app.name»::Index::send_pulse()\n    {\n      «FOR ran : app.remoteApps.map[name].sort»\n        for (auto &writer : «ran»_writers_) {\n          writer.«pulseMessageName»();\n        }\n      «ENDFOR»\n    }\n\n    void «app.pkg.name»::«app.name»::Index::dump_json(std::ostream &out) const\n    {\n      out << '{'\n        «FOR span: app.spans SEPARATOR \" << ','\"»\n          << \"\\\"«span.name»\\\": \" << «span.name»\n        «ENDFOR»\n        << '}';\n    }\n    '''\n  }\n\n\n  /***************************************************************************\n   * CONTAINERS H\n   **************************************************************************/\n\n  static def allocParamList(Span span) {\n    if (span.sharding !== null) {\n      '''«FOR field : span.sharding.keys SEPARATOR \", \"»«field.cType» «field.name»«ENDFOR»'''\n    } else {\n      \"\"\n    }\n  }\n\n  static def generateContainersH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"spans.h\"\n    #include \"keys.h\"\n    #include \"auto_handles.h\"\n\n    #include \"../metrics.h\"\n\n    #include <util/short_string.h>\n    #include <util/fixed_hash.h>\n    #include <util/metric_store.h>\n\n    #include <ostream>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace containers {\n\n    «FOR span : app.spans»\n      /**\n       * Container for span «span.name».\n       *\n       * The container maintains a constant-sized pool from which spans are\n       * allocated, and if the span is indexd, a map from the index key\n       * to the spans.\n       *\n       * Access to elements is performed through handles which keep a reference\n       * to an allocated span, or through a weak reference (\"weak_ref\"),\n       * which allows read and write access to span fields.\n       */\n      class «span.name» {\n      public:\n        /**\n         * pool_size: size of the constant-sized pool of spans available\n         */\n        static constexpr u32 pool_size = «span.pool_size»;\n\n        /**\n         * C'tor\n         */\n        «span.name»();\n\n        «IF span.index !== null»\n        /**\n         * Get a span handle from key.\n         *\n         * This span is indexed, so spans are accessed by their key.\n         * @see ::«app.pkg.name»::«app.name»::keys::«span.name»\n         */\n        auto_handles::«span.name» by_key(const ::«app.pkg.name»::«app.name»::keys::«span.name» &key, bool create_if_not_found = true);\n        «ELSE»\n        /**\n         * Allocate a new span.\n         *\n         * This span is unindexed. Use alloc() to get a new span that can\n         * then be modified using weak_ref::«span.name»::modify().\n         */\n        auto_handles::«span.name» alloc(«allocParamList(span)»);\n        «ENDIF»\n\n        /**\n         * get: increase reference count and get a handle for an existing span\n         */\n        ::«app.pkg.name»::«app.name»::auto_handles::«span.name» get(«locationTypeForHandle(span)» loc);\n\n        /**\n         * put: decrease reference count and deallocate span if new refcount is 0.\n         *\n         * @return true if this was the last reference, false otherwise.\n         */\n        bool put(«locationTypeForHandle(span)» loc);\n\n        /**\n         * Get a specific span\n         *\n         * @assumes: refcount for the span is positive.\n         */\n        ::«app.pkg.name»::«app.name»::weak_refs::«span.name» at(«locationTypeForHandle(span)» loc);\n\n        /**\n         * @return number of allocated spans of type «span.name»\n         */\n        std::size_t size() const;\n\n        /**\n         * @return maximum number of allocated spans of type «span.name»\n         */\n        std::size_t max_size() const;\n\n        /***********************\n         * Metrics\n         */\n        «FOR agg : span.aggs»\n        /**\n         * aggregator «agg.name»: update metrics\n         */\n          «IF agg.isRoot»\n          void «agg.name»_update(«locationTypeForHandle(span)» loc, u64 t, ::«app.pkg.name»::metrics::«agg.type.name»_point const &m);\n          «ELSE»\n          void «agg.name»_update(«locationTypeForHandle(span)» loc, u64 t, ::«app.pkg.name»::metrics::«agg.type.name» const &m);\n          «ENDIF»\n        «ENDFOR»\n\n        «FOR agg : span.aggs»\n          /**\n           * aggregator «agg.name»: is timeslot ready for output\n           *\n           * @returns true if the aggregation has a timeslot ready\n           */\n          bool «agg.name»_ready(u64 t);\n          «FOR rollup : agg.rollups»\n\n          /**\n           * «agg.name»_«rollup.rollup_count»: is timeslot ready for output.\n           */\n          bool «agg.name»_«rollup.rollup_count»_ready(u64 t);\n          «ENDFOR»\n        «ENDFOR»\n\n        «FOR agg : span.aggs»\n          /**\n           * aggregator «agg.name»: process timeslot with functor\n           *\n           * For each live entry in timeslot, calls functor.operator()\n           * with (t_of_timeslot, loc_in_index, span, metric),\n           * then advances the timeslot\n           */\n          template<class FUNCTOR>\n          void «agg.name»_foreach(u64 t, FUNCTOR &&f);\n\n          «FOR rollup : agg.rollups»\n          /**\n           * aggregator rollup «agg.name»_«rollup.rollup_count»: process timeslot with functor\n           *\n           * For each live entry in timeslot, calls functor.operator()\n           * with (t_of_timeslot, loc_in_index, span, metric),\n           * then advances the timeslot\n           */\n          template<class FUNCTOR>\n          void «agg.name»_«rollup.rollup_count»_foreach(u64 t, FUNCTOR &&f);\n          «ENDFOR»\n        «ENDFOR»\n\n        using span_t = ::«app.pkg.name»::«app.name»::spans::«span.name»;\n        «IF span.index !== null»\n        typedef ::«app.pkg.name»::«app.name»::keys::«span.name» key_t;\n        «ENDIF»\n\n        «IF span.sharding !== null»\n          struct sharding_key_t {\n            «FOR field : span.sharding.keys»\n              «field.cType» «field.name»;\n            «ENDFOR»\n          };\n\n          static inline std::size_t hash_sharding_key(sharding_key_t const &key);\n        «ENDIF»\n\n        /* getter for specific location */\n        friend ::«app.pkg.name»::«app.name»::weak_refs::«span.name»;\n        inline ::«app.pkg.name»::«app.name»::spans::«span.name» *at_ptr(«locationTypeForHandle(span)» loc);\n\n        «IF span.index !== null»\n          /* key hasher for map */\n          struct hasher_t {\n            typedef std::size_t result_type;\n            inline result_type operator()(key_t const &key) const noexcept;\n          };\n\n          /* key equality for map */\n          struct equals_t {\n            inline bool operator()(key_t const &lhs, key_t const &rhs) const noexcept;\n          };\n\n          /* map type */\n          using map_t = FixedHash<key_t, span_t, pool_size, hasher_t, equals_t>;\n\n          map_t map;\n        «ELSE»\n          /* pool */\n          Pool<span_t, pool_size> map;\n        «ENDIF»\n\n        /* metric stores */\n        «FOR agg: span.aggs»\n        «IF agg.isRoot»\n          MetricStore<::«app.pkg.name»::metrics::«agg.type.name»_accumulator, pool_size, «agg.slots»> «agg.name»;\n        «ELSE»\n          MetricStore<::«app.pkg.name»::metrics::«agg.type.name», pool_size, «agg.slots»> «agg.name»;\n        «ENDIF»\n        «FOR rollup: agg.rollups»\n          MetricStore<::«app.pkg.name»::metrics::«agg.type.name», pool_size, «agg.slots»> «agg.name»_«rollup.rollup_count»;\n        «ENDFOR»\n        «ENDFOR»\n\n        void dump_json(std::ostream &out) const;\n\n        friend std::ostream &operator <<(std::ostream &out, «span.name» const &what) {\n          what.dump_json(out);\n          return out;\n        }\n      };\n\n    «ENDFOR»\n    } // namespace containers\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * CONTAINERS INL\n   **************************************************************************/\n  static def generateContainersInl(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"containers.h\"\n    #include \"weak_refs.h\"\n    #include \"weak_refs.inl\"\n    #include \"modifiers.h\"\n    #include <util/container_of.h>\n    #include <util/lookup3.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace containers {\n\n      /*****************************************************************************\n       * hashers\n       ****************************************************************************/\n      «FOR span : app.spans.filter[index !== null]»\n        inline «span.name»::hasher_t::result_type\n        «span.name»::hasher_t::operator()(«span.name»::key_t const &key) const noexcept\n        {\n          «generateKeyHashingFuncImpl(span.index.keys.filter(Field), span.index.keys.filter(Reference))»\n        }\n      «ENDFOR»\n\n      «FOR span : app.spans.filter[sharding !== null]»\n        inline std::size_t «span.name»::hash_sharding_key(«span.name»::sharding_key_t const &key)\n        {\n          «generateKeyHashingFuncImpl(span.sharding.keys, #[])»\n        }\n      «ENDFOR»\n\n      /*****************************************************************************\n       * key equality\n       ****************************************************************************/\n      «FOR span : app.spans.filter[index !== null]»\n\n      bool «span.name»::equals_t::operator()(«span.name»::key_t const &lhs, «span.name»::key_t const &rhs) const noexcept\n      {\n        return\n          /* references and plain fields are XORed together */\n          ((0\n          «FOR defn : span.index.keys.filter(Reference)»\n          | (lhs.«defn.name» ^ rhs.«defn.name»)\n          «ENDFOR»\n          «FOR defn : span.index.keys.filter(Field).filter[!type.isShortString && !isArray]»\n          | (lhs.«defn.name» ^ rhs.«defn.name»)\n          «ENDFOR»\n          ) == 0)\n          /* strings and arrays have to be compared */\n          «FOR field : span.index.keys.filter(Field).filter[type.isShortString || isArray]»\n            && (lhs.«field.name» == rhs.«field.name»)\n          «ENDFOR»\n          ;\n      }\n      «ENDFOR»\n\n    /*****************************************************************************\n     * span getters\n     ****************************************************************************/\n    «FOR span : app.spans»\n    ::«app.pkg.name»::«app.name»::spans::«span.name» *«span.name»::at_ptr(«locationTypeForHandle(span)» loc)\n    {\n      return &map[loc];\n    }\n    «ENDFOR»\n\n    /*****************************************************************************\n     * metrics iterators\n     ****************************************************************************/\n    «FOR span : app.spans»\n      «FOR agg : span.aggs»\n        «generateMetricForeach(agg, span.name, false, 1)»\n\n        «FOR rollup : agg.rollups»\n          «generateMetricForeach(agg, span.name, true, rollup.rollup_count)»\n        «ENDFOR»\n      «ENDFOR»\n    «ENDFOR»\n\n    } // namespace containers\n\n    } // namespace «app.pkg.name»::«app.name»\n  '''\n  }\n\n  static def generateKeyHashingFuncImpl(Field[] fields, Reference[] references) {\n    '''\n    u32 val = 0x7AFBAF00;\n\n    /**** fields ****/\n    «FOR field : fields»\n      «IF field.isArray && field.type.isShortString»\n        /* «field.name» is an array of strings. hash each one. */\n        for (int i = 0; i < field.array_size; i++) {\n          val = lookup3_hashlittle(key.«field.name»[i].buf, key.«field.name»[i].len, val + key.«field.name»[i].len);\n        }\n      «ELSEIF field.type.isShortString»\n        /* «field.name» is a string */\n        val = lookup3_hashlittle(key.«field.name».buf, key.«field.name».len, val + key.«field.name».len);\n      «ELSEIF integerTypeSize(field.type.enum_type) % 4 == 0»\n        /* «field.name» is a primitive type, is multiple of 4 bytes. will hash in 4-byte words */\n        val = lookup3_hashword((u32 *)&key.«field.name», «fieldSize(field) / 4», val + «fieldSize(field)»);\n      «ELSE»\n        /* «field.name» is a plain variable: will hash individual bytes */\n        val = lookup3_hashlittle((char *)&key.«field.name», «fieldSize(field)», val + «fieldSize(field)»);\n      «ENDIF»\n    «ENDFOR»\n\n    «IF references.size > 0»\n      /**** references ****/\n      val = lookup3_hashword(key.references, «references.size», val + (4 * «references.size»));\n    «ENDIF»\n\n    return val;\n    '''\n  }\n\n  /***************************************************************************\n   * CONTAINERS CC\n   **************************************************************************/\n\n  static def generateContainersCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"containers.h\"\n    #include \"index.h\"\n    #include <util/container_of.h>\n    #include <util/fast_div.h>\n    #include \"containers.inl\"\n\n    «FOR remote_app : app.remoteApps»\n    #include \"../«remote_app.name»/writer.h\"\n    «ENDFOR»\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace containers {\n\n    «FOR span : app.spans»\n      /*****************************************************************************\n       * «span.name»\n       ****************************************************************************/\n      /* c'tor */\n      «span.name»::«span.name»()\n      «FOR agg: span.aggs BEFORE \" : \" SEPARATOR \",\"»\n        «agg.name»(fast_div(double(«agg.interval» * 1e9), 16))\n        «FOR rollup: agg.rollups BEFORE \" , \" SEPARATOR \",\"»\n          «agg.name»_«rollup.rollup_count»(fast_div(double(«agg.interval» * 1e9) * «rollup.rollup_count», 16))\n        «ENDFOR»\n      «ENDFOR»\n      {}\n\n      «IF span.index !== null»\n      ::«app.pkg.name»::«app.name»::auto_handles::«span.name»\n      «span.name»::by_key(const ::«app.pkg.name»::«app.name»::keys::«span.name» &key, bool create_if_not_found)\n      {\n        /* does the key already have a handle? */\n        auto pos = map.find(key);\n        if(pos.index == map_t::invalid) {\n          auto index_ptr = fp_container_of(this, &Index::«span.name»);\n\n          if (!create_if_not_found) {\n            return {*index_ptr};\n          }\n\n          /* could not find, insert */\n          pos = map.insert(key);\n          if (pos.index == map_t::invalid) {\n            /* could not insert into the map, it's probably full */\n            return {*index_ptr};\n          }\n\n          /* make the handle */\n          ::«app.pkg.name»::«app.name»::auto_handles::«span.name» handle(*index_ptr, pos.index);\n\n          «IF span.sharding !== null»\n            /* calculate the shard ID of this span */\n            size_t num_remote_instances = index_ptr->«span.remoteApp.name»_writers_.size();\n            assert(num_remote_instances <= std::numeric_limits<decltype(handle.span_ptr_->shard_id_)>::max());\n            auto shard_id = hash_sharding_key({«FOR field : span.sharding.keys SEPARATOR \", \"»key.«field.name»«ENDFOR»}) % num_remote_instances;\n            handle.span_ptr_->shard_id_ = shard_id;\n          «ENDIF»\n\n          /* set the key values in the entry */\n          handle.modify()\n            «FOR defn : span.index.keys»\n              «IF defn instanceof Reference»\n                .«defn.name»(index_ptr->«defn.target.name».get(key.«defn.name»))\n              «ELSE»\n                .«defn.name»(key.«defn.name»)\n              «ENDIF»\n            «ENDFOR»\n            ;\n\n          «IF span.isProxy»\n          {\n            /* call remote span start message */\n            «IF span.remoteSpan.index !== null»\n              /* the remote span is indexed; send key fields in the start message */\n              «FOR key_field : span.remoteSpan.index.keys»\n                «IF (key_field instanceof Field) && (key_field as Field).isArray»\n                  auto «key_field.name» = key.«key_field.name».data();\n                «ELSE»\n                  auto &«key_field.name» = key.«key_field.name»;\n                «ENDIF»\n              «ENDFOR»\n            «ENDIF»\n            «proxyMethodInvocation(span, span.proxyStartMessage, '(*index_ptr)', 'pos.index')»\n          }\n          «ENDIF»\n\n          /* have a valid value_type with refcount = 1 */\n          return handle;\n        } else {\n          /* found an existing handle. get a refcount */\n          return get(pos.index);\n        }\n      }\n\n      «ELSE»\n      /* new span allocator */\n      auto_handles::«span.name» «span.name»::alloc(«allocParamList(span)»)\n      {\n        auto index_ptr = fp_container_of(this, &Index::«span.name»);\n        auto loc = map.emplace().index;\n\n        if (loc == map.invalid) {\n          return {*index_ptr, loc};\n        }\n\n        auto_handles::«span.name» handle(*index_ptr, loc);\n\n        «IF span.sharding !== null»\n          /* calculate the shard ID of this span */\n          size_t num_remote_instances = index_ptr->«span.remoteApp.name»_writers_.size();\n          assert(num_remote_instances <= std::numeric_limits<decltype(handle.span_ptr_->shard_id_)>::max());\n          auto shard_id = hash_sharding_key({«FOR field : span.sharding.keys SEPARATOR \", \"»«field.name»«ENDFOR»}) % num_remote_instances;\n          handle.span_ptr_->shard_id_ = shard_id;\n\n          /* set the values in the sharding key */\n          handle.modify()\n            «FOR field : span.sharding.keys»\n              .«field.name»(«field.name»)\n            «ENDFOR»\n            ;\n        «ENDIF»\n\n        «IF span.isProxy»\n          /* call remote span start message */\n          «proxyMethodInvocation(span, span.proxyStartMessage, '(*index_ptr)', 'loc')»\n        «ENDIF»\n\n        return handle;\n      }\n\n      «ENDIF»\n      ::«app.pkg.name»::«app.name»::auto_handles::«span.name» «span.name»::get(«locationTypeForHandle(span)» loc)\n      {\n        auto index_ptr = fp_container_of(this, &Index::«span.name»);\n\n        if (loc == «invalidConstForHandle(span)») {\n          return {*index_ptr};\n        }\n\n        map[loc].__refcount++;\n\n        return {*index_ptr, loc};\n      }\n\n      /**\n       * Puts reference.\n       * @return true if this was the last reference, false otherwise.\n       */\n      bool «span.name»::put(«locationTypeForHandle(span)» loc)\n      {\n        «IF (span.definitions.filter(Reference).size > 0) || span.isProxy»\n        auto index_ptr = fp_container_of(this, &Index::«span.name»);\n        «ENDIF»\n        auto &val = map[loc];\n        u32 new_count = --val.__refcount;\n        if (new_count == 0) {\n          «IF span.index !== null»\n            key_t _key;\n            «FOR field : span.index.keys.filter(Field)»\n              _key.«field.name» = val.__«field.name»;\n            «ENDFOR»\n            «FOR defn : span.index.keys.filter(Reference)»\n              _key.«defn.name» = val.__«defn.name»;\n            «ENDFOR»\n          «ENDIF»\n          «IF span.definitions.filter(Reference).size > 0»\n            /* put references to the referenced indices */\n            «FOR defn : span.definitions.filter(Reference)»\n              if (val.__«defn.name» != «invalidConstForHandle(defn.target)») {\n                index_ptr->«defn.target.name».put(val.__«defn.name»);\n                val.__«defn.name» = «invalidConstForHandle(defn.target)»;\n              }\n            «ENDFOR»\n          «ENDIF»\n          «IF span.sharding !== null»\n            auto shard_id = val.shard_id_;\n          «ENDIF»\n          «IF span.index !== null»\n            map.erase(_key);\n          «ELSE»\n            map.remove(loc);\n          «ENDIF»\n          «IF span.isProxy»\n            /* call remote span end message */\n            «proxyMethodInvocation(span, span.proxyEndMessage, '(*index_ptr)', 'loc')»\n          «ENDIF»\n          return true;\n        }\n        return false;\n      }\n\n      ::«app.pkg.name»::«app.name»::weak_refs::«span.name» «span.name»::at(«locationTypeForHandle(span)» loc)\n      {\n        auto index_ptr = fp_container_of(this, &Index::«span.name»);\n        return {*index_ptr, loc};\n      }\n\n      std::size_t «span.name»::size() const {\n        return map.size();\n      }\n\n      std::size_t «span.name»::max_size() const {\n        return map.max_size();\n      }\n\n      /* metric aggregators */\n      «FOR agg : span.aggs»\n      «IF agg.isRoot»\n        void «span.name»::«agg.name»_update(«locationTypeForHandle(span)» loc,\n            u64 t, ::«app.pkg.name»::metrics::«agg.type.name»_point const &m)\n        {\n          «generateMetricUpdate(agg, agg.name, \"loc\", \"t\", \"m\", true)»\n        }\n      «ELSE»\n        void «span.name»::«agg.name»_update(«locationTypeForHandle(span)» loc,\n            u64 t, ::«app.pkg.name»::metrics::«agg.type.name» const &m)\n        {\n          «generateMetricUpdate(agg, agg.name, \"loc\", \"t\", \"m\", false)»\n        }\n      «ENDIF»\n      «ENDFOR»\n\n      /* metrics, is timeslot ready */\n      «FOR agg : span.aggs»\n        bool «span.name»::«agg.name»_ready(u64 t)\n        {\n          auto relative = «agg.name».relative_timeslot(t);\n          return (relative > 0);\n        }\n        «FOR rollup : agg.rollups»\n          bool «span.name»::«agg.name»_«rollup.rollup_count»_ready(u64 t)\n          {\n            auto relative = «agg.name»_«rollup.rollup_count».relative_timeslot(t);\n            return (relative > 0);\n          }\n        «ENDFOR»\n      «ENDFOR»\n\n      void «span.name»::dump_json(std::ostream &out) const\n      {\n        out << \"{\\\"spans\\\":[\";\n\n        bool first = true;\n        for (auto const i: map.allocated()) {\n          if (first) {\n            first = false;\n          } else {\n            out << ',';\n          }\n          out << \"{\\\"@ref\\\":\" << i << ',' << map[i] << '}';\n        }\n\n        out << \"]}\";\n      }\n    «ENDFOR»\n    } // namespace containers\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * KEYS H\n   **************************************************************************/\n  static def generateKeysH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n    #include <util/short_string.h>\n    #include <array>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace keys {\n\n    «FOR span : app.spans»\n      «IF span.index !== null»\n        /**\n         * Key struct for span «span.name»\n         *\n         * This struct is used as a key to the mapping that indexes the span\n         */\n        struct «span.name» {\n          «span.name»() = default;\n          «IF span.index.keys.filter(Field).length > 0»\n          «span.name»(«FOR field : span.index.keys.filter(Field) SEPARATOR \", \"»«field.cType» «field.name»«ENDFOR»«FOR ref : span.index.keys.filter(Reference) BEFORE (span.index.keys.filter(Field).empty ? \"\" : \", \") SEPARATOR \", \"»u32 «ref.name»«ENDFOR»):\n            «FOR field : span.index.keys.filter(Field) SEPARATOR \",\"»\n              «field.name»(«field.name»)\n            «ENDFOR»\n            «FOR ref : span.index.keys.filter(Reference) BEFORE (span.index.keys.filter(Field).empty ? \"\" : \",\") SEPARATOR \",\"»\n              «ref.name»(«ref.name»)\n            «ENDFOR»\n          {}\n          «ENDIF»\n\n          «span.name»(«span.name» const &) = default;\n          «span.name»(«span.name» &&) = default;\n          «span.name» &operator =(«span.name» const &) = default;\n          «span.name» &operator =(«span.name» &&) = default;\n\n          «FOR field : span.index.keys.filter(Field)»\n          /* field «field.name» */\n          using «field.name»_t = «field.cType»;\n          «generateField(field)»\n          «ENDFOR»\n\n          /**\n           * References to other spans are kept in u32.\n           *\n           * This allows for fast hashing.\n           */\n          union {\n            u32 references[«span.index.keys.filter(Reference).size»];\n            struct {\n              «FOR ref : span.index.keys.filter(Reference)»\n                u32 «ref.name»;\n              «ENDFOR»\n            };\n          };\n        };\n      «ENDIF»\n    «ENDFOR»\n    } // namespace keys\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * HANDLES H\n   **************************************************************************/\n  static def generateHandlesH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /* forward declarations */\n    class Index;\n    namespace containers {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace weak_refs {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace auto_handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n\n    namespace handles {\n\n    «FOR span : app.spans»\n      class «span.name» {\n      public:\n        using location_type = «locationTypeForHandle(span)»;\n        static constexpr location_type invalid = «invalidConstForHandle(span)»;\n\n        /**\n         * C'tor for an invalid handle.\n         *\n         * @note: a different constructor allows constructing a valid handle.\n         * that c'tor is used by containers to get references to spane.\n         * The c'tor is private to protect the consistency of reference counting.\n         */\n        «span.name»();\n\n        /**\n         * D'tor.\n         *\n         * Has an assert that the reference has been put or released.\n         */\n        ~«span.name»();\n\n        /**\n         * Get the offset into the span pool of this handle\n         */\n        location_type loc();\n\n        /**\n         * @returns true if this handle is valid\n         */\n        bool valid();\n\n        /**\n         * Puts the reference back\n         *\n         * @param ai: the Index that holds the span corresponding to the reference\n         */\n        bool put(Index &ai);\n\n        /**\n         * Gets the offset and moves the reference out of the handle.\n         *\n         * @imporant: it is the responsibility of the caller to put() the reference\n         */\n        location_type release();\n\n        /**\n         * Get a weak_ref to access the span associated with the handle\n         */\n        ::«app.pkg.name»::«app.name»::weak_refs::«span.name» access(Index &ai) const;\n\n        /**\n         * Move operator is allowed\n         */\n        «span.name»& operator=(«span.name» &&other);\n\n        /**\n         * Move constructor is allowed\n         */\n        «span.name»(«span.name» &&other);\n\n        /**\n         * Non-move copy constructor is forbidden\n         */\n        «span.name»(const «span.name»&) = delete;\n\n        /**\n         * Non-move assignment is forbidden\n         */\n        «span.name»& operator=(const «span.name»&) = delete;\n\n        /**\n         * Convert from an auto_handle to a handle.\n         */\n        «span.name»(::«app.pkg.name»::«app.name»::auto_handles::«span.name» &&auto_handle);\n\n      private:\n        friend class ::«app.pkg.name»::«app.name»::containers::«span.name»;\n        friend class ::«app.pkg.name»::«app.name»::weak_refs::«span.name»;\n        friend class ::«app.pkg.name»::«app.name»::auto_handles::«span.name»;\n\n        explicit «span.name»(location_type loc);\n        location_type loc_;\n      };\n\n    «ENDFOR»\n    } // namespace handles\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * HANDLES CC\n   **************************************************************************/\n  static def generateHandlesCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"handles.h\"\n    #include \"index.h\"\n    #include <assert.h>\n    #include \"containers.h\"\n    #include \"containers.inl\"\n    #include \"weak_refs.h\"\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace handles {\n\n    «FOR span : app.spans»\n    /********************************************\n     * «span.name»\n     ********************************************/\n    «span.name»::«span.name»()\n      : loc_(invalid)\n    {}\n\n    «span.name»::~«span.name»()\n    {\n      assert(!valid());\n    }\n\n    «span.name»::location_type «span.name»::loc()\n    {\n      return loc_;\n    }\n\n    bool «span.name»::valid()\n    {\n      return loc_ != invalid;\n    }\n\n    bool «span.name»::put(Index &ai) {\n      if (valid()) {\n        bool res = ai.«span.name».put(loc_);\n        loc_ = invalid;\n        return res;\n      }\n      return false;\n    }\n\n    «span.name»::location_type «span.name»::release()\n    {\n      auto ret = loc_;\n      loc_ = invalid;\n      return ret;\n    }\n\n    /* access the span associated with the handle */\n    ::«app.pkg.name»::«app.name»::weak_refs::«span.name» «span.name»::access(Index &ai) const\n    {\n      return ai.«span.name».at(loc_);\n    }\n\n    «span.name»& «span.name»::operator=(«span.name» &&other) {\n      assert(!valid());\n      loc_ = other.loc_;\n      other.loc_ = invalid;\n      return *this;\n    }\n\n    «span.name»::«span.name»(«span.name» &&other)\n    {\n      loc_ = other.loc_;\n      other.loc_ = invalid;\n    }\n\n    «span.name»::«span.name»(«span.name»::location_type loc)\n      : loc_(loc)\n    {}\n\n    «span.name»::«span.name»(::«app.pkg.name»::«app.name»::auto_handles::«span.name» &&auto_handle)\n    {\n      loc_ = auto_handle.release();\n    }\n\n    «ENDFOR»\n    } // namespace handles\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * AUTO HANDLES H\n   **************************************************************************/\n  static def generateAutoHandlesH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"weak_refs.h\"\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /* forward declarations */\n    class Index;\n    namespace containers {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n\n    namespace auto_handles {\n\n    «FOR span : app.spans»\n      /**\n       * Auto-handle for span «span.name».\n       *\n       * Like a weak_ref, but puts the reference after use\n       */\n      class «span.name» : public ::«app.pkg.name»::«app.name»::weak_refs::«span.name» {\n      public:\n        /**\n         * C'tor for an invalid handle.\n         *\n         * @note: a different constructor allows constructing a valid handle.\n         * that c'tor is used by containers to get references to spane.\n         * The c'tor is private to protect the consistency of reference counting.\n         */\n        «span.name»(Index &index);\n\n        /**\n         * D'tor.\n         *\n         * Frees the reference to the object\n         */\n        ~«span.name»();\n\n        /**\n         * Put the reference\n         */\n        void put();\n\n        /**\n         * Convert to handle for storage\n         *\n         * The reference moves to the handle, and this instance becomes\n         * invalid\n         */\n        ::«app.pkg.name»::«app.name»::handles::«span.name» to_handle();\n\n        /**\n         * Release the reference, return the loc\n         */\n        «locationTypeForHandle(span)» release();\n\n        /**\n         * Move operator is allowed\n         */\n        «span.name»& operator=(«span.name» &&other);\n\n        /**\n         * ..but weak_ref is incompatible (does not hold a reference)\n         */\n        «span.name»& operator=(::«app.pkg.name»::«app.name»::weak_refs::«span.name» &&other) = delete;\n\n        /**\n         * Move constructor is allowed\n         */\n        «span.name»(«span.name» &&other);\n\n        /**\n         * ..but weak_ref is incompatible (does not hold a reference)\n         */\n        «span.name»(::«app.pkg.name»::«app.name»::weak_refs::«span.name» &&other) = delete;\n\n        /**\n         * Non-move copy constructor is forbidden\n         */\n        «span.name»(const «span.name»&) = delete;\n\n        /**\n         * Non-move assignment is forbidden\n         */\n        «span.name»& operator=(const «span.name»&) = delete;\n\n      private:\n        friend class ::«app.pkg.name»::«app.name»::containers::«span.name»;\n\n        /**\n         * C'tor from a location into the index.\n         *\n         * @assumes caller has taken a reference. This reference becomes the\n         * responsibility of this instance\n         */\n        «span.name»(Index &index, location_type loc);\n      };\n\n    «ENDFOR»\n    } // namespace auto_handles\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * AUTO HANDLES CC\n   **************************************************************************/\n  static def generateAutoHandlesCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"auto_handles.h\"\n    #include \"index.h\"\n    #include <assert.h>\n    #include \"containers.h\"\n    #include \"containers.inl\"\n    #include \"weak_refs.h\"\n    #include \"handles.h\"\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace auto_handles {\n\n    «FOR span : app.spans»\n    /********************************************\n     * «span.name»\n     ********************************************/\n    «span.name»::«span.name»(Index &index)\n      : ::«app.pkg.name»::«app.name»::weak_refs::«span.name»(index, invalid)\n    {}\n\n    «span.name»::~«span.name»()\n    {\n      put();\n    }\n\n    void «span.name»::put()\n    {\n      if (valid()) {\n        index_.«span.name».put(loc_);\n        loc_ = invalid;\n        span_ptr_ = nullptr;\n      }\n    }\n\n    ::«app.pkg.name»::«app.name»::handles::«span.name» «span.name»::to_handle()\n    {\n      «locationTypeForHandle(span)» saved_loc = loc_;\n      loc_ = invalid;\n      span_ptr_ = nullptr;\n      return ::«app.pkg.name»::«app.name»::handles::«span.name»(saved_loc);\n    }\n\n    «locationTypeForHandle(span)» «span.name»::release()\n    {\n      «locationTypeForHandle(span)» saved_loc = loc_;\n      loc_ = invalid;\n      span_ptr_ = nullptr;\n      return saved_loc;\n    }\n\n    «span.name»& «span.name»::operator=(«span.name» &&other)\n    {\n      put();\n\n      loc_ = other.loc_;\n      span_ptr_ = other.span_ptr_;\n      other.loc_ = invalid;\n      other.span_ptr_ = nullptr;\n\n      return *this;\n    }\n\n    «span.name»::«span.name»(«span.name» &&other)\n      : ::«app.pkg.name»::«app.name»::weak_refs::«span.name»(other.index_, other.loc_)\n    {\n      other.loc_ = invalid;\n      other.span_ptr_ = nullptr;\n    }\n\n    «span.name»::«span.name»(Index &index, location_type loc)\n      : ::«app.pkg.name»::«app.name»::weak_refs::«span.name»(index, loc)\n    {}\n\n    «ENDFOR»\n    } // namespace auto_handles\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * WEAK REFS H\n   **************************************************************************/\n  static def String generateImplFwdDeclaration(String[] namespaces) {\n    '''\n    «IF namespaces.length === 1»\n      class «namespaces.get(0)»;\n    «ELSE»\n      namespace «namespaces.get(0)» {\n        «generateImplFwdDeclaration(Arrays.copyOfRange(namespaces, 1, namespaces.length))»\n      } /* «namespaces.get(0)» */\n    «ENDIF»\n    '''\n  }\n\n  static def generateWeakRefsH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n    #include <util/short_string.h>\n    #include <jitbuf/jb.h>\n\n    #include <array>\n\n    /* Span implementation classes */\n    «FOR span : app.spans.filter[impl !== null]»\n      «generateImplFwdDeclaration(span.impl.split(\"::\"))»\n    «ENDFOR»\n\n    /* forward declarations */\n    namespace «app.pkg.name»::metrics {\n    «FOR metric : app.metrics»\n        class «metric.name»;\n        class «metric.name»_point;\n    «ENDFOR»\n    }\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /* forward declarations */\n    class Index;\n    namespace handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace auto_handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace modifiers {\n    «FOR span : app.spans»\n      class «span.name»;\n    «ENDFOR»\n    }\n    namespace spans {\n    «FOR span : app.spans»\n      class «span.name»;\n    «ENDFOR»\n    }\n\n    namespace impl {\n    /* internal utility class: accessor used in the implementation */\n    class __accessor;\n    }\n\n    namespace weak_refs {\n\n    /* forward declarations */\n    «FOR span : app.spans»\n      class «span.name»;\n    «ENDFOR»\n\n    «FOR span : app.spans»\n      /**\n       * Weak reference to the span.\n       *\n       * @assumes the referenced span is either invalid or has a handle that\n       * holds a reference.\n       */\n      class «span.name» {\n      public:\n        using location_type = «locationTypeForHandle(span)»;\n        static constexpr location_type invalid = «invalidConstForHandle(span)»;\n\n        /**\n         * C'tor.\n         * @note: it might be preferable to obtain a weak_ref through\n         * the index, from another weak_ref, or via a handle.\n         *\n         * @param index: the index that allocated the span\n         * @param loc: the offset of the span in the span's pool\n         */\n        inline «span.name»(Index &index, location_type loc);\n\n        /**\n         * Move from an auto-handle is prohibited.\n         */\n        «span.name»(::«app.pkg.name»::«app.name»::auto_handles::«span.name» &&auto_handle) = delete;\n\n        /**\n         * @returns true if reference is valid\n         */\n        inline bool valid() const\n        {\n          return loc_ != invalid;\n        }\n\n        /**\n         * Get the offset into the span pool of this handle\n         */\n        location_type loc() const\n        {\n          return loc_;\n        }\n\n        /**\n         * Get the reference count of the underlying span\n         */\n        u32 refcount() const;\n\n        /**\n         * Get the index object this span belongs to\n         */\n        Index &index()\n        {\n          return index_;\n        }\n\n        /**\n         * get: increase reference count and get a handle to the span\n         */\n        ::«app.pkg.name»::«app.name»::auto_handles::«span.name» get();\n\n        «FOR field : span.definitions.filter(Field) SEPARATOR \"\\n\"»\n        /**\n         * Getter for field «field.name»\n         *\n         * @assumes valid() == true\n         */\n        using «field.name»_t = «field.cType»;\n        «field.name»_t &«field.name»();\n        «ENDFOR»\n\n        «FOR ref : span.definitions.filter(Reference)»\n        /**\n         * Getter for reference «ref.name».\n         *\n         * @returns: a weak_ref to the referenced span\n         *\n         * @assumes valid() == true\n         *\n         * @note using fully qualified return value in this definition is\n         * necessary because reference names can be equal to the targets\n         */\n        ::«app.pkg.name»::«app.name»::weak_refs::«ref.target.name» «ref.name»();\n        «ENDFOR»\n\n        /**\n         * Obtains a modifier for the span.\n         *\n         * The modifier allows changing fields and references, and automatically\n         * updates auto references when destructed.\n         */\n        ::«app.pkg.name»::«app.name»::modifiers::«span.name» modify();\n\n        «FOR agg : span.aggs»\n        /**\n         * Update aggregator «agg.name» of metric «agg.type.name» associated\n         * with this span.\n         *\n         * @param t: time of the telemetry\n         * @param m: the values of the metric to be aggreagated\n         */\n        «IF agg.isRoot»\n          void «agg.name»_update(u64 t, ::«app.pkg.name»::metrics::«agg.type.name»_point const &m);\n        «ELSE»\n          void «agg.name»_update(u64 t, ::«app.pkg.name»::metrics::«agg.type.name» const &m);\n        «ENDIF»\n        «ENDFOR»\n\n        «IF span.isProxy»\n          /* Proxy methods */\n          «FOR msg : span.proxyLogMessages»\n            «proxyMethodDeclaration(msg)»\n          «ENDFOR»\n        «ENDIF»\n\n        «IF span.impl !== null»\n          /* Accessor for the span's message handler instance */\n          ::«span.impl» &impl();\n        «ENDIF»\n\n        void dump_json(std::ostream &out) const;\n\n        friend std::ostream &operator <<(std::ostream &out, «span.name» const &what) {\n          what.dump_json(out);\n          return out;\n        }\n\n      protected:\n        /* allow spans to follow weak_refs using span_ptr_ */\n        friend class impl::__accessor;\n\n        Index &index_;\n        location_type loc_;\n        ::«app.pkg.name»::«app.name»::spans::«span.name» *span_ptr_;\n      };\n\n    «ENDFOR»\n    } // namespace weak_refs\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * WEAK REFS INL\n   **************************************************************************/\n  static def generateWeakRefsInl(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"weak_refs.h\"\n    #include \"index.h\"\n    #include <util/container_of.h>\n    #include <util/lookup3.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace weak_refs {\n\n    «FOR span : app.spans»\n      inline «span.name»::«span.name»(Index &index, «span.name»::location_type loc)\n        : index_(index)\n        , loc_(loc)\n        , span_ptr_( (loc == invalid) ? nullptr : index.«span.name».at_ptr(loc_))\n      {}\n\n    «ENDFOR»\n    } // namespace weak_refs\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * WEAK REFS CC\n   **************************************************************************/\n  static def generateWeakRefsCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"weak_refs.h\"\n    #include \"spans.h\"\n    #include \"keys.h\"\n    #include \"index.h\"\n    #include \"modifiers.h\"\n    #include \"auto_handles.h\"\n    #include \"weak_refs.inl\"\n    #include \"containers.inl\"\n\n    «FOR remote_app : app.remoteApps»\n    #include \"../«remote_app.name»/writer.h\"\n    «ENDFOR»\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace weak_refs {\n\n    «FOR span : app.spans»\n      /*******************************\n       * «span.name»\n       *******************************/\n      /**\n       * Get a reference to the node\n       */\n      ::«app.pkg.name»::«app.name»::auto_handles::«span.name» «span.name»::get()\n      {\n        assert(valid());\n        return index_.«span.name».get(loc_);\n      }\n\n      /* field getters */\n      «FOR field : span.definitions.filter(Field)»\n        «field.cType» &«span.name»::«field.name»()\n        {\n          return span_ptr_->__«field.name»;\n        }\n      «ENDFOR»\n\n      /* reference getters */\n      «FOR ref : span.definitions.filter(Reference)»\n        ::«app.pkg.name»::«app.name»::weak_refs::«ref.target.name»\n        «span.name»::«ref.name»()\n        {\n          «IF ref.isIsCached»\n            /* cached reference: refresh cache */\n            span_ptr_->refresh__«ref.name»(index_);\n          «ENDIF»\n          /* return weak_ref to referenced span */\n          return ::«app.pkg.name»::«app.name»::weak_refs::«ref.target.name»(index_, span_ptr_->__«ref.name»);\n        }\n      «ENDFOR»\n\n      u32 «span.name»::refcount() const\n      {\n        assert(valid());\n        return span_ptr_->__refcount;\n      }\n\n      ::«app.pkg.name»::«app.name»::modifiers::«span.name» «span.name»::modify()\n      {\n        return {span_ptr_, index_};\n      }\n\n      /* metric updaters */\n      «FOR agg : span.aggs»\n      «IF agg.isRoot»\n      void «span.name»::«agg.name»_update(u64 t, ::«app.pkg.name»::metrics::«agg.type.name»_point const &m)\n      «ELSE»\n      void «span.name»::«agg.name»_update(u64 t, ::«app.pkg.name»::metrics::«agg.type.name» const &m)\n      «ENDIF»\n      {\n        return index_.«span.name».«agg.name»_update(loc_, t, m);\n      }\n      «ENDFOR»\n\n      «IF span.isProxy»\n        /* Proxy methods */\n        «FOR msg : span.proxyLogMessages»\n          «proxyMethodDefinition(span, msg)»\n        «ENDFOR»\n      «ENDIF»\n\n      «IF span.impl !== null»\n        /* Get a ref to the span's accessor */\n        ::«span.impl» &«span.name»::impl() { return span_ptr_->impl_; };\n      «ENDIF»\n\n      void «span.name»::dump_json(std::ostream &out) const\n      {\n        if (valid()) {\n          assert(span_ptr_);\n          out << *span_ptr_;\n        } else {\n          out << \"null\";\n        }\n      }\n\n    «ENDFOR»\n    } // namespace weak_refs\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n\n  /***************************************************************************\n   * MODIFIERS H\n   **************************************************************************/\n  static def generateModifiersH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n    #include <util/short_string.h>\n    #include <array>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /* forward declarations */\n    class Index;\n    namespace handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace auto_handles {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace containers {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace weak_refs {\n    «FOR span : app.spans»\n      class «span.name»;\n    «ENDFOR»\n    }\n    namespace spans {\n    «FOR span : app.spans»\n      class «span.name»;\n    «ENDFOR»\n    }\n\n    namespace modifiers {\n\n    «FOR span : app.spans»\n      /**\n       * Modifier for span «span.name»\n       *\n       * Allows a user to change fields of the span.\n       *\n       * @note: if the span is indexed, fields that are part of the key are\n       * only settable from the container, specifically the by_key() method,\n       * so the fields are always consistent with the mapping. These fields\n       * are later used to remove the span from the index when the refcount\n       * for the span reaches 0.\n       */\n      class «span.name» {\n      public:\n        /* note: c'tor is private (only available through the weak_ref's modify()) */\n\n        /**\n         * D'tor\n         *\n         * The d'tor is responsible for recomputing auto references once all\n         * other updates have been applied\n         */\n        ~«span.name»();\n\n        /**\n         * Non-move copy constructor is forbidden\n         */\n        «span.name»(const «span.name»&) = delete;\n\n        /**\n         * Non-move assignment is forbidden\n         */\n        «span.name»& operator=(const «span.name»&) = delete;\n\n        /***************************************************\n         * Field types\n         ***************************************************/\n        «FOR field : span.definitions.filter(Field)»\n          «generateFieldTypeInfo(field)»\n        «ENDFOR»\n\n        /***************************************************\n         * Field setters\n         ***************************************************/\n        «FOR field : span.definitions.filter(Field)»\n          «IF !((span.index !== null) && (span.index.keys.contains(field)))»\n            /**\n             * Setter for field «field.name»\n             */\n            «span.name» &«field.name»(const «field.cType» &_«field.name»);\n          «ENDIF»\n        «ENDFOR»\n        /***************************************************\n         * Manual references\n         ***************************************************/\n        «FOR ref : span.definitions.filter(Reference).filter[!isAuto && !isCached]»\n          «IF !((span.index !== null) && (span.index.keys.contains(ref)))»\n            /**\n             * Setter for reference «ref.name» to span «ref.target.name»\n             */\n            «span.name» &«ref.name»(::«app.pkg.name»::«app.name»::handles::«ref.target.name» &&other);\n            /**\n             * Setter for reference «ref.name» to span «ref.target.name»\n             */\n            «span.name» &«ref.name»(::«app.pkg.name»::«app.name»::auto_handles::«ref.target.name» &&other);\n          «ENDIF»\n        «ENDFOR»\n      private:\n        /***************************************************\n         * fields in index (read-only by users)\n         ***************************************************/\n        «FOR field : span.definitions.filter(Field)»\n          «IF (span.index !== null) && (span.index.keys.contains(field))»\n            /**\n             * Setter for field «field.name»\n             *\n             * This field is indexed on, so the setter is private\n             */\n            «span.name» &«field.name»(const «field.cType» &_«field.name»);\n          «ENDIF»\n        «ENDFOR»\n        /***************************************************\n         * manual references in index (read-only by users)\n         ***************************************************/\n        «FOR ref : span.definitions.filter(Reference).filter[!isAuto && !isCached]»\n          «IF (span.index !== null) && (span.index.keys.contains(ref))»\n            /**\n             * Setter for reference «ref.name» to span «ref.target.name»\n             *\n             * This reference is indexed on, so the setter is private\n             */\n            «span.name» &«ref.name»(::«app.pkg.name»::«app.name»::handles::«ref.target.name» &&other);\n            /**\n             * Setter for reference «ref.name» to span «ref.target.name»\n             *\n             * This reference is indexed on, so the setter is private\n             */\n            «span.name» &«ref.name»(::«app.pkg.name»::«app.name»::auto_handles::«ref.target.name» &&other);\n          «ENDIF»\n        «ENDFOR»\n\n        /* allow the by_key() method to set the keys */\n        friend class ::«app.pkg.name»::«app.name»::containers::«span.name»;\n\n        /* allow the weak_ref to construct instances */\n        friend class ::«app.pkg.name»::«app.name»::weak_refs::«span.name»;\n\n        /**\n         * Private c'tor\n         *\n         * @param span_ptr: pointer to the span we're modifying\n         * @param index: the index holding the span. used to get and put\n         * references.\n         */\n        «span.name»(::«app.pkg.name»::«app.name»::spans::«span.name» *span_ptr, Index &index);\n\n        ::«app.pkg.name»::«app.name»::spans::«span.name» *span_ptr_;\n        Index &index_;\n        u64 modified_mask_;\n      };\n\n    «ENDFOR»\n    } // namespace modifiers\n\n    } // namespace «app.pkg.name»«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * Modifiers CC\n   **************************************************************************/\n  static def generateModifiersCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"modifiers.h\"\n    #include \"spans.h\"\n    #include \"keys.h\"\n    #include \"index.h\"\n    #include \"containers.h\"\n    #include \"handles.h\"\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace modifiers {\n\n    «FOR span : app.spans»\n      «generateModifierImpl(app, span)»\n\n    «ENDFOR»\n    } // namespace modifiers\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  static def generateModifierImpl(App app, Span span) {\n    val deps = new SpanAutoDependencies(span)\n\n    if (deps.nonDynamicPrereqs.size > 64)\n      throw new RuntimeException(\n         '''span «span.name» has «deps.nonDynamicPrereqs.size» prereqs, only 64 supported''')\n\n    '''\n    /*******************************\n     * «span.name» modifier masks\n     *******************************/\n    namespace {\n      struct «span.name»__masks {\n        /* enum of all possible prereqs */\n        enum class prereqs { «deps.nonDynamicPrereqs.map[name].sort().join(', ')» };\n        /* mask of each prereq */\n        «FOR defn : deps.nonDynamicPrereqs.sortBy[name]»\n          static constexpr u64 «defn.name» = (1ull << static_cast<int>(prereqs::«defn.name»));\n        «ENDFOR»\n        /* mask for each computed reference, what are its dependencies */\n        «FOR ref : deps.computeOrder»\n          static constexpr u64 «ref.name» = «deps.refNonDynamicPrereqs.get(ref).map[name].sort().join(' | ')»;\n        «ENDFOR»\n      };\n    }\n\n    /*******************************\n     * «span.name»\n     *******************************/\n    «span.name»::«span.name»(::«app.pkg.name»::«app.name»::spans::«span.name» *span_ptr, Index &index)\n      : span_ptr_(span_ptr),\n        index_(index),\n        modified_mask_(0)\n    {\n      assert(span_ptr_ != nullptr);\n    }\n\n    «span.name»::~«span.name»()\n    {\n      /* compute order: «deps.computeOrder.map[name].join(\", \")» */\n      «FOR ref: deps.computeOrder»\n        if (modified_mask_ & «span.name»__masks::«ref.name») {\n          span_ptr_->refresh__«ref.name»(index_);\n        }\n      «ENDFOR»\n    }\n\n    /* fields */\n    «FOR field : span.definitions.filter(Field)»\n      «span.name» &«span.name»::«field.name»(const «field.cType» &_«field.name»)\n      {\n        span_ptr_->__«field.name» = _«field.name»;\n        «IF deps.nonDynamicPrereqs.contains(field)»\n          modified_mask_ |= «span.name»__masks::«field.name»;\n        «ENDIF»\n        return *this;\n      }\n    «ENDFOR»\n    /* manual references */\n    «FOR ref : span.definitions.filter(Reference).filter[!isAuto && !isCached]»\n      «FOR handle_type: Arrays.asList(\"handles\", \"auto_handles\")»\n      «span.name» &«span.name»::«ref.name»(::«app.pkg.name»::«app.name»::«handle_type»::«ref.target.name» &&other)\n      {\n        if (span_ptr_->__«ref.name» != «invalidConstForHandle(ref.target)») {\n          index_.«ref.target.name».put(span_ptr_->__«ref.name»);\n        }\n        span_ptr_->__«ref.name» = other.release();\n        «IF deps.nonDynamicPrereqs.contains(ref)»\n          modified_mask_ |= «span.name»__masks::«ref.name»;\n        «ENDIF»\n        return *this;\n      }\n      «ENDFOR»\n    «ENDFOR»\n\n    '''\n  }\n\n  /***************************************************************************\n   * SPANS H\n   **************************************************************************/\n  static def generateSpansH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include <platform/types.h>\n    #include <util/short_string.h>\n    #include <array>\n\n    /* Span implementation classes */\n    «FOR app_span : app.spans.filter[include !== null]»\n      #include «app_span.include»\n    «ENDFOR»\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /* forward declarations */\n    class Index;\n    namespace keys {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace weak_refs {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace modifiers {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n    namespace containers {\n      «FOR span : app.spans»\n        class «span.name»;\n      «ENDFOR»\n    }\n\n    namespace impl {\n    /* internal utility class: accessor used in the implementation */\n    class __accessor;\n    }\n\n    namespace spans {\n\n    «FOR span : app.spans»\n    class «span.name» {\n    public:\n      /**\n       * C'tor\n       */\n      «span.name»();\n\n      /**\n       * Field types.\n       */\n      «FOR field : span.definitions.filter(Field)»\n        «generateFieldTypeInfo(field)»\n      «ENDFOR»\n\n      void dump_json(std::ostream &out) const;\n\n      friend std::ostream &operator <<(std::ostream &out, «span.name» const &what) {\n        what.dump_json(out);\n        return out;\n      }\n\n    private:\n      /* allow getters from the weak_ref */\n      friend class ::«app.pkg.name»::«app.name»::weak_refs::«span.name»;\n\n      /* allow modification from modifier */\n      friend class ::«app.pkg.name»::«app.name»::modifiers::«span.name»;\n\n      /* allow access to the accessor for compute_key__<...> methods */\n      friend class impl::__accessor;\n\n      /* allow refcounting */\n      friend class ::«app.pkg.name»::«app.name»::containers::«span.name»;\n\n      /**\n       * compute key for auto and cached references\n       *\n       * @returns: true if key is valid, false otherwise\n       */\n      «FOR ref : span.definitions.filter(Reference).filter[isAuto || isCached]»\n        bool compute_key__«ref.name»(Index &index, ::«app.pkg.name»::«app.name»::keys::«ref.target.name» &key);\n      «ENDFOR»\n\n      /**\n       * refresh auto and cached references, after recomputing key\n       */\n      «FOR ref : span.definitions.filter(Reference).filter[isAuto || isCached]»\n        void refresh__«ref.name»(Index &index);\n      «ENDFOR»\n\n      /* reference count */\n      u32 __refcount;\n\n      «IF span.sharding !== null»\n        /* remote span's shard */\n        u8 shard_id_;\n      «ENDIF»\n\n      /* fields */\n      «FOR field : span.definitions.filter(Field)»\n        «generateField(field, \"__\")»\n      «ENDFOR»\n\n      /* references */\n      «FOR ref : span.definitions.filter(Reference)»\n        «locationTypeForHandle(ref.target)» __«ref.name»;\n      «ENDFOR»\n\n      «IF span.impl !== null»\n      /* span impl */\n      ::«span.impl» impl_;\n      «ENDIF»\n    };\n\n    «ENDFOR»\n    } // namespace spans\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * SPANS CC\n   **************************************************************************/\n  static def generateSpansCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"spans.h\"\n    #include \"weak_refs.h\"\n    #include \"keys.h\"\n    #include \"index.h\"\n    #include \"containers.inl\"\n\n    #include <util/raw_json.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    namespace impl {\n\n    class __accessor {\n      public:\n      «FOR span : app.spans»\n        «FOR field : span.definitions.filter(Field)»\n          inline static «field.cType»& «span.name»__«field.name»(weak_refs::«span.name» ref) { return ref.span_ptr_->__«field.name»; }\n        «ENDFOR»\n        «FOR ref : span.definitions.filter(Reference)»\n          inline static «locationTypeForHandle(ref.target)» «span.name»__«ref.name»(weak_refs::«span.name» ref) { return ref.span_ptr_->__«ref.name»; };\n        «ENDFOR»\n      «ENDFOR»\n    };\n\n    } // namespace impl\n\n    namespace spans {\n\n    «FOR span : app.spans»\n    /********************************************\n     * «span.name»\n     ********************************************/\n    «span.name»::«span.name»()\n      : __refcount(1)\n      «IF span.sharding !== null»\n      , shard_id_(0)\n      «ENDIF»\n      «FOR ref: span.definitions.filter(Reference) BEFORE \", \" SEPARATOR \", \"»\n        __«ref.name»(«invalidConstForHandle(ref.target)»)\n      «ENDFOR»\n    {}\n\n    /* key getters for auto, cached references */\n    «FOR ref : span.definitions.filter(Reference).filter[isAuto || isCached]»\n      bool «span.name»::compute_key__«ref.name»(Index &index, ::«app.pkg.name»::«app.name»::keys::«ref.target.name» &key)\n      {\n        «FOR binding : ref.bindings»\n        /* find key '«binding.key.name»' */\n        {\n          «generateReferenceRef(binding.value, \"key.\" + binding.key.name, 0)»\n        }\n        «ENDFOR»\n        return true;\n      }\n    «ENDFOR»\n\n    /* refresh references */\n    «FOR ref : span.definitions.filter(Reference).filter[isAuto || isCached]»\n      void «span.name»::refresh__«ref.name»(Index &index)\n      {\n        /* auto reference '«ref.name»' */\n        auto prev_reference = __«ref.name»;\n\n        /* compute the key from dependencies */\n        ::«app.pkg.name»::«app.name»::keys::«ref.target.name» key;\n        bool valid = compute_key__«ref.name»(index, key);\n\n        /* get the reference, if valid */\n        if (valid) {\n          __«ref.name» = index.«ref.target.name».by_key(key).release();\n        } else {\n          __«ref.name» = «invalidConstForHandle(ref.target)»;\n        }\n\n        /* put the previous reference if it was valid */\n        if (prev_reference != «invalidConstForHandle(ref.target)»)\n          index.«ref.target.name».put(prev_reference);\n      }\n    «ENDFOR»\n\n    void «span.name»::dump_json(std::ostream &out) const\n    {\n      out << \"\\\"@refcount\\\":\" << __refcount;\n      «FOR field : span.definitions.filter(Field)»\n        print_json_value(out << \",\\\"«field.name»\\\":\", __«field.name»);\n      «ENDFOR»\n      «FOR ref : span.definitions.filter(Reference)»\n        out << \",\\\"#«ref.name»\\\":\" << __«ref.name»;\n      «ENDFOR»\n    }\n\n    «ENDFOR»\n    } // namespace spans\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /***************************************************************************\n   * SPAN BASE H\n   **************************************************************************/\n  static def generateSpanBaseH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"parsed_message.h\"\n    #include \"weak_refs.h\"\n\n    #include <platform/types.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    /******************************************************************************\n     * Span handlers\n     ******************************************************************************/\n\n    «FOR span : app.spans»\n    /**\n     * «span.baseClassName»\n     */\n    class «span.baseClassName» {\n    public:\n      /** handlers */\n      «FOR msg : span.messages»\n        void «msg.name»(\n          ::«app.pkg.name»::«app.name»::weak_refs::«span.name» span_ref,\n          u64 timestamp, «msg.parsed_msg.struct_name» *msg) {}\n      «ENDFOR»\n    };\n\n    «ENDFOR»\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  /**\n   * Resolve a reference binding for compute_key__<...>\n   */\n  static def CharSequence generateReferenceRef(ReferenceBindingRef ref, String into_var, Integer depth) {\n    switch ref {\n      ReferenceBindingRoot:\n        '''«into_var» = __«ref.entity.name»;'''\n      ReferenceBindingValue: {\n        val span = ref.tail.eContainer as Span\n        val ref_handle = '''__ref_handle_«depth»'''\n        '''\n        ««« first, prepare a variable to save the handle into\n        «locationTypeForHandle(span)» «ref_handle»;\n        ««« generate code recursively into ref_handle_«depth»\n        «generateReferenceRef(ref.ref, ref_handle, depth+1)»\n        ««« if handle is invalid, return false\n        if («ref_handle» == «invalidConstForHandle(span)») { return false; }\n        ««« resolve the reference and get the definition\n        «into_var» = impl::__accessor::«span.name»__«ref.tail.name»(index.«span.name».at(«ref_handle»));\n        '''\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/TransformBuilderGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.SpanExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\n\nclass TransformBuilderGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n    fsa.generateFile(outputPath(app, \"transform_builder.h\"), generateTransformerH(app))\n    fsa.generateFile(outputPath(app, \"transform_builder.cc\"), generateTransformerCc(app))\n  }\n\n  private static def generateTransformerH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"hash.h\"\n\n    #include <jitbuf/perfect_hash.h>\n    «IF app.jit»\n      #include <jitbuf/transform_builder.h>\n    «ENDIF»\n    #include <platform/types.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    class TransformBuilder «IF app.jit»: public ::jitbuf::TransformBuilder«ENDIF» {\n    public:\n      // Format-transformation function signature.\n      typedef uint16_t (*transform_t)(const char *src, char *dst);\n\n      «IF app.jit»\n        TransformBuilder(llvm::LLVMContext &context);\n      «ELSE»\n        TransformBuilder();\n      «ENDIF»\n\n      // Returns the size of the identity wire message for the given RPC ID.\n      u32 get_identity_size(u16 rpc_id);\n\n      // Returns identity transform function for the given RPC ID.\n      transform_t get_identity(u16 rpc_id);\n\n    private:\n      struct TransformInfo {\n        transform_t func;\n        u16 size;\n      };\n\n      PerfectHash<TransformInfo, «app.hashSize», «app.hashFunctor»> identity_transforms_;\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateTransformerCc(App app) {\n    val messages = app.messages\n    '''\n    «generatedCodeWarning()»\n\n    #include \"transform_builder.h\"\n\n    #include \"parsed_message.h\"\n    #include \"wire_message.h\"\n    #include \"descriptor.h\"\n\n    #include <stdexcept>\n\n    namespace {\n\n    // Identity transform implementations.\n\n    «FOR msg : messages SEPARATOR \"\\n\"»\n      «identityTransform(msg)»\n    «ENDFOR»\n\n    } // namespace\n\n    namespace «app.pkg.name»::«app.name» {\n\n    «IF app.jit»\n      TransformBuilder::TransformBuilder(llvm::LLVMContext &context) : jitbuf::TransformBuilder(context)\n    «ELSE»\n      TransformBuilder::TransformBuilder()\n    «ENDIF»\n    {\n      «IF app.jit»\n        // Add all local message descriptors for JIT.\n        «FOR msg : messages»\n          add_descriptor(«msg.parsed_msg.descriptor_name»);\n        «ENDFOR»\n      «ENDIF»\n\n      // Add identity transforms.\n      «FOR msg : messages»\n        identity_transforms_.insert(«msg.parsed_msg.rpc_id», TransformInfo{.func = «msg.identityTransformName», .size = «msg.wire_msg.size»});\n      «ENDFOR»\n    }\n\n    u32 TransformBuilder::get_identity_size(u16 rpc_id)\n    {\n      auto tranform_info = identity_transforms_.find(rpc_id);\n      if (tranform_info == nullptr) {\n        throw std::runtime_error(\"identity_size: rpc_id not found\");\n      }\n      return tranform_info->size;\n    }\n\n    TransformBuilder::transform_t TransformBuilder::get_identity(u16 rpc_id)\n    {\n      auto tranform_info = identity_transforms_.find(rpc_id);\n      if (tranform_info == nullptr) {\n        throw std::runtime_error(\"get_identity: rpc_id not found\");\n      }\n      return tranform_info->func;\n    }\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def identityTransformName(Message msg) {\n    val app = msg.span.app\n    '''«app.c_name»_«msg.name»_identity_handler'''\n  }\n\n  private static def identityTransform(Message msg) {\n    '''\n    uint16_t «identityTransformName(msg)»(const char *src, char *dst)\n    {\n      «IF !msg.wire_msg.dynamic_size»\n        /* simple message -- just needs a copy */\n        memcpy(dst, src, «msg.wire_msg.size»);\n        return «msg.wire_msg.size»;\n      «ELSE»\n        /* dynamic-size message */\n        auto src_msg = (const «msg.wire_msg.struct_name» *)src;\n        auto dst_msg = («msg.parsed_msg.struct_name» *)dst;\n\n        /* copy all non-string fields */\n        «FOR field : msg.fields.filter[type.enum_type != FieldTypeEnum.STRING]»\n          «IF field.isArray»\n            memcpy(&dst_msg->«field.name»[0], &src_msg->«field.name»[0], «field.size(true)»);\n          «ELSE»\n            dst_msg->«field.name» = src_msg->«field.name»;\n          «ENDIF»\n        «ENDFOR»\n\n        /* handle dynamic strings */\n        u16 consumed = «msg.wire_msg.size»;\n        «FOR field : msg.fields.filter[type.enum_type == FieldTypeEnum.STRING]»\n          dst_msg->«field.name».buf = &src[consumed];\n          «IF field != msg.wire_msg.last_blob_field»\n            /* not the last field: length is in original message */\n            dst_msg->«field.name».len = src_msg->«field.name»;\n            consumed += src_msg->«field.name»;\n          «ELSE»\n            /* last field: gets the rest of the message */\n            dst_msg->«field.name».len = src_msg->_len - consumed;\n          «ENDIF»\n        «ENDFOR»\n\n        dst_msg->_rpc_id = «msg.parsed_msg.rpc_id»;\n\n        return src_msg->_len;\n      «ENDIF»\n    }\n    '''\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/generator/WriterGenerator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.generator\n\nimport org.eclipse.xtext.generator.IFileSystemAccess2\n\nimport io.opentelemetry.render.render.App\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport static io.opentelemetry.render.generator.AppGenerator.outputPath\nimport static io.opentelemetry.render.generator.RenderGenerator.generatedCodeWarning\nimport static extension io.opentelemetry.render.extensions.AppExtensions.*\nimport static extension io.opentelemetry.render.extensions.FieldTypeExtensions.*\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.*\n\nclass WriterGenerator {\n\n  def void doGenerate(App app, IFileSystemAccess2 fsa) {\n      fsa.generateFile(outputPath(app, \"writer.h\"), generateWriterH(app))\n      fsa.generateFile(outputPath(app, \"writer.cc\"), generateWriterCc(app))\n\n      fsa.generateFile(outputPath(app, \"encoder.h\"), generateEncoderH(app))\n      fsa.generateFile(outputPath(app, \"encoder.cc\"), generateEncoderCc(app))\n  }\n\n  private static def generateWriterH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n\n    #include \"encoder.h\"\n    #include \"parsed_message.h\"\n    #include \"wire_message.h\"\n\n    #include <channel/ibuffered_writer.h>\n    #include <platform/types.h>\n\n    #include <functional>\n    #include <stdexcept>\n    #include <system_error>\n    #include <utility>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    class Writer {\n    public:\n      using clock_t = std::function<u64()>;\n\n      Writer(IBufferedWriter &buffer, clock_t clock, u64 time_adjustment = 0, Encoder *encoder = nullptr);\n\n      Writer(Writer const &);\n      Writer(Writer &&);\n\n      Writer &operator =(Writer const &) = delete;\n      Writer &operator =(Writer &&) = delete;\n\n      «FOR msg : app.messages»\n\n        /* «msg.name» */\n        template <bool ThrowOnError = true>\n        inline std::error_code «msg.name»(«msg.prototype») {\n          return «msg.name»_tstamp<ThrowOnError>(clock_()+time_adjustment_«msg.commaCallPrototype»);\n        }\n\n        template <bool ThrowOnError = true>\n        inline std::error_code «msg.name»_tstamp(u64 __tstamp«msg.commaPrototype») {\n          auto __result = encoder_->«msg.name»(buffer_, __tstamp«msg.commaCallPrototype»);\n\n          if constexpr (ThrowOnError) {\n            if (__result) {\n              throw std::system_error(__result, \"Writer::«msg.name» failed\");\n            }\n          }\n\n          return __result;\n        }\n      «ENDFOR»\n\n      bool is_writable() const {\n        return buffer_.is_writable();\n      }\n\n      std::error_code flush() {\n        if (is_writable()) {\n          return buffer_.flush();\n        }\n        return {};\n      }\n\n    private:\n      IBufferedWriter &buffer_;\n      Encoder default_encoder_;\n      Encoder *encoder_ = nullptr;\n      clock_t clock_;\n      u64 time_adjustment_;\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateWriterCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"writer.h\"\n\n    namespace «app.pkg.name»::«app.name» {\n\n    Writer::Writer(IBufferedWriter &buffer, clock_t clock, u64 time_adjustment, Encoder *encoder):\n      buffer_(buffer),\n      encoder_(encoder ? encoder : &default_encoder_),\n      clock_(std::move(clock)),\n      time_adjustment_(time_adjustment)\n    {}\n\n    Writer::Writer(Writer const &rhs):\n      buffer_(rhs.buffer_),\n      encoder_(rhs.encoder_ == &rhs.default_encoder_ ? &default_encoder_ : rhs.encoder_),\n      clock_(rhs.clock_),\n      time_adjustment_(rhs.time_adjustment_)\n    {}\n\n    Writer::Writer(Writer &&rhs):\n      buffer_(rhs.buffer_),\n      encoder_(rhs.encoder_ == &rhs.default_encoder_ ? &default_encoder_ : rhs.encoder_),\n      clock_(std::move(rhs.clock_)),\n      time_adjustment_(std::move(rhs.time_adjustment_))\n    {}\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateEncoderH(App app) {\n    '''\n    «generatedCodeWarning()»\n    #pragma once\n    \n    #include <generated/«app.pkg.name»/«app.name»/parsed_message.h>\n    #include <generated/«app.pkg.name»/«app.name»/wire_message.h>\n\n    #include <channel/ibuffered_writer.h>\n    #include <platform/types.h>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    class Encoder {\n    public:\n      Encoder();\n      virtual ~Encoder();\n\n      «FOR msg : app.messages»\n      virtual std::error_code «msg.name»(IBufferedWriter &__buffer, u64 __tstamp«msg.commaPrototype»);\n      «ENDFOR»\n    };\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def generateEncoderCc(App app) {\n    '''\n    «generatedCodeWarning()»\n\n    #include \"encoder.h\"\n    #include \"wire_message.h\"\n\n    #include <jitbuf/jb.h>\n    #include <platform/types.h>\n\n    #include <cassert>\n    #include <cstring>\n    #include <stdexcept>\n    #include <system_error>\n\n    namespace «app.pkg.name»::«app.name» {\n\n    «FOR msg : app.messages»\n      extern \"C\" void «app.pkg.name»_«app.name»_encode_«msg.name»(\n          uint8_t *__dest,\n          uint32_t __dest_len,\n          uint64_t __tstamp«FOR field : msg.fields.sortBy[id] BEFORE ',' SEPARATOR ','»\n            «ffiParamC(field)»\n          «ENDFOR»\n      );\n    «ENDFOR»\n\n    Encoder::Encoder() {}\n    Encoder::~Encoder() {}\n\n    «FOR msg : app.messages»\n      /* «msg.name» via Rust shim */\n      std::error_code Encoder::«msg.name»(IBufferedWriter &__buffer, u64 __tstamp«msg.commaPrototype») {\n        /* Compute encoded length (wire struct + dynamic payloads) */\n        u32 __consumed = «msg.wire_msg.size»;\n        «IF msg.wire_msg.dynamic_size»\n          /* handle dynamic string lengths */\n          «FOR field : msg.wire_msg.fields.filter[type.enum_type == FieldTypeEnum.STRING]»\n            __consumed += «field.name».len;\n          «ENDFOR»\n          __consumed += «msg.wire_msg.last_blob_field.name».len;\n          if (__consumed > 0xffff) {\n            throw std::runtime_error(\"Writer::«msg.name» tried to write dynamic message >= 1<<16\");\n          }\n        «ENDIF»\n\n        /* start write */\n        const u32 __total_len = sizeof(u64) + __consumed;\n        auto __allocated = __buffer.start_write(__total_len);\n        if (!__allocated) {\n          return __allocated.error();\n        }\n\n        /* Call Rust encoder */\n        «app.pkg.name»_«app.name»_encode_«msg.name»(\n            (uint8_t *)*__allocated,\n            __total_len,\n            __tstamp«FOR field : msg.fields.sortBy[id] BEFORE ',' SEPARATOR ','»\n              «ffiCallArg(field)»\n            «ENDFOR»\n        );\n\n        __buffer.finish_write();\n        return {};\n      }\n    «ENDFOR»\n\n    } // namespace «app.pkg.name»::«app.name»\n    '''\n  }\n\n  private static def String ffiParamC(io.opentelemetry.render.render.Field field) {\n    if (field.type.enum_type == FieldTypeEnum.STRING) {\n      return '''struct jb_blob «field.name»'''\n    }\n\n    if (field.type.isShortString || field.isArray) {\n      val elemC =\n        if (field.type.isShortString) 'uint8_t' else field.type.cType(true)\n      return '''const «elemC» *«field.name»'''\n    }\n\n    return '''«field.type.cType(false)» «field.name»'''\n  }\n\n  private static def String ffiCallArg(io.opentelemetry.render.render.Field field) {\n    if (field.type.enum_type == FieldTypeEnum.STRING) {\n      return field.name\n    }\n    if (field.type.isShortString) {\n      return '''(const uint8_t *)&«field.name»[0]'''\n    }\n    if (field.isArray) {\n      return '''&«field.name»[0]'''\n    }\n    return field.name\n  }\n\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/scoping/RenderScopeProvider.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.scoping\n\nimport org.eclipse.emf.ecore.EObject\nimport org.eclipse.emf.ecore.EReference\nimport org.eclipse.xtext.scoping.IScope\nimport org.eclipse.xtext.scoping.Scopes\nimport org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider\n\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.Reference\nimport io.opentelemetry.render.render.ReferenceBinding\nimport io.opentelemetry.render.render.ReferenceBindingRoot\nimport io.opentelemetry.render.render.ReferenceBindingValue\nimport io.opentelemetry.render.render.RenderPackage\nimport io.opentelemetry.render.render.AggregationUpdate\n\n/**\n * This class contains custom scoping description.\n *\n * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping\n * on how and when to use it.\n */\nclass RenderScopeProvider extends AbstractDeclarativeScopeProvider {\n\n  /**\n   * ReferenceBinding::value can follow references to other spans\n   *\n   * follows pattern from:\n   *   https://christiandietrich.wordpress.com/2013/05/18/xtext-and-dot-expressions/\n   */\n  def IScope scope_ReferenceBindingValue_tail(ReferenceBindingValue exp, EReference reference) {\n    val head = exp.ref\n    switch (head) {\n      ReferenceBindingRoot: {\n        val entity = head.entity\n        switch(entity) {\n          Field :    IScope::NULLSCOPE\n          Reference:  Scopes.scopeFor(entity.target.definitions)\n          default:  IScope::NULLSCOPE\n        }\n      }\n      ReferenceBindingValue: {\n        val tail = head.tail\n        switch (tail) {\n          Field:     IScope::NULLSCOPE\n          Reference:   Scopes.scopeFor(tail.target.definitions)\n          default:  IScope::NULLSCOPE\n        }\n      }\n    }\n  }\n\n  def IScope scope_AggregationUpdate_agg(AggregationUpdate exp, EReference reference) {\n    Scopes.scopeFor(exp.ref.target.aggs)\n  }\n\n  def IScope scope_Span_remoteSpan(Span exp, EReference reference) {\n    Scopes.scopeFor(exp.remoteApp.spans)\n  }\n\n  override getScope(EObject context, EReference reference) {\n    /* ReferenceBinding::key should be scoped inside Reference::target */\n    if (reference == RenderPackage.Literals.REFERENCE_BINDING__KEY) {\n      /*\n       * we can be called from Reference or ReferenceBinding context.\n       * In either case, find the Reference context.\n       */\n      val ref = switch(context) {\n        Reference: context\n        ReferenceBinding: context.eContainer() as Reference\n      }\n\n      /* get the Reference::target (which is an Index) */\n      val span = ref.target\n      val index = span.index\n\n      /* get all the Index::keys (which are references to Definition) */\n      return Scopes.scopeFor(index.keys)\n    }\n\n    /* if we didn't override, call the default implementation */\n    return super.getScope(context, reference)\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render/src/io/opentelemetry/render/validation/RenderValidator.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.validation\n\nimport org.eclipse.xtext.validation.Check\nimport io.opentelemetry.render.render.Field\nimport io.opentelemetry.render.render.FieldTypeEnum\nimport io.opentelemetry.render.render.Reference\nimport io.opentelemetry.render.render.Span\nimport io.opentelemetry.render.render.RenderPackage\nimport io.opentelemetry.render.render.Message\nimport io.opentelemetry.render.render.MessageType\nimport io.opentelemetry.render.render.RpcIdRange\nimport static io.opentelemetry.render.generator.AppPacker.rpcIdRangeMin\nimport static io.opentelemetry.render.generator.AppPacker.rpcIdRangeMax\nimport static extension io.opentelemetry.render.extensions.MessageExtensions.span\n\n/**\n * This class contains custom validation rules.\n *\n * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation\n */\nclass RenderValidator extends AbstractRenderValidator {\n\n  @Check\n  def void checkSpanDoesNotContainVariableStrings(Field field) {\n    if (field.type.enum_type != FieldTypeEnum.STRING)\n      return\n\n    var parent = field.eContainer()\n    switch (parent) {\n      Span:\n        error('Span should not contain variable-length strings',\n          RenderPackage.Literals.FIELD__TYPE\n        )\n    }\n  }\n\n  @Check\n  def void checkSingletonSpanDoesNotContainStartOrEnd(Message m) {\n    switch (m.type) {\n      case MessageType.START,\n      case MessageType.END:\n      /* need to check the containing span is not a singleton */\n        if ((m.eContainer() as Span).isSingleton)\n          error('Singleton Span should not contain start or end messages',\n            RenderPackage.Literals.MESSAGE__TYPE\n          )\n      default:\n        return\n    }\n  }\n\n  @Check\n  def void checkMessageHasReferenceField(Message msg) {\n    if (msg.type == MessageType.MSG) {\n      // messages declared with the 'msg' syntatic sugar will be\n      // added a special reference field automatically\n      return\n    }\n\n    if (!msg.span.isSingleton && (msg.referenceEmbedded == false)) {\n      error('''Message is missing a reference field''',\n        RenderPackage.Literals.MESSAGE__TYPE)\n    }\n  }\n\n  @Check\n  def void checkProxySpanKeyIsSupersetOfRemoteSpanKey(Span span) {\n    if (span.isProxy) {\n      if (span.remoteSpan.index === null) {\n        // remote span is not index-allocated\n        return\n      }\n\n      if (span.index === null) {\n        error('''Proxy span index key must be a superset of the remote span's key''',\n          RenderPackage.Literals.SPAN__INDEX)\n      }\n\n      val keys = span.index.keys.filter(Field)\n\n      for (remoteKey : span.remoteSpan.index.keys.filter(Field)) {\n        val localKey = keys.findFirst[name == remoteKey.name]\n\n        if (localKey === null) {\n          error('Proxy span index key is missing a field: ' + remoteKey.name,\n            RenderPackage.Literals.SPAN__INDEX)\n        }\n\n        if (localKey.type.enum_type != remoteKey.type.enum_type) {\n          error('Proxy span index key field is of wrong type: ' + localKey.name,\n            RenderPackage.Literals.SPAN__INDEX)\n        }\n      }\n    }\n  }\n\n  @Check\n  def void checkShardingKeyIsSubsetOfIndexKey(Span span) {\n    if (span.sharding === null) {\n      return\n    }\n\n    if (span.index === null) {\n      // non-indexed span\n      return\n    }\n\n    var indexKeyFields = span.index.keys.filter(Field).toSet()\n\n    for (field : span.sharding.keys) {\n      if (indexKeyFields.contains(field) == false) {\n        error(\"Sharding key field '\" + field.name + \"' must be part of the index key\",\n          RenderPackage.Literals.SPAN__REMOTE_SPAN)\n      }\n    }\n  }\n\n  @Check\n  def void checkRemoteSpanKeyHasNoReferences(Span proxySpan) {\n    // NOTE: given a span, there is no way to check whether it's a target\n    //       of some proxy span, so we have to check remote spans of all\n    //       proxy span\n    var span = proxySpan.remoteSpan\n\n    if (span === null) {\n      return\n    }\n\n    var refs = span.index.keys.filter(Reference)\n\n    if (refs.size > 0) {\n      error(\"Span '\" + span.name + \"' is a target of a proxy so it can't have reference fields in its key\",\n        RenderPackage.Literals.SPAN__REMOTE_SPAN)\n    }\n  }\n\n  @Check\n  def void checkRpcIdRange(RpcIdRange range) {\n    if (range.start < rpcIdRangeMin) {\n      error('''Invalid RPC ID range (start < «rpcIdRangeMin»)''',\n        RenderPackage.Literals.RPC_ID_RANGE__START)\n    }\n\n    if (range.start > rpcIdRangeMax) {\n      error('''Invalid RPC ID range (start > «rpcIdRangeMax»)''',\n        RenderPackage.Literals.RPC_ID_RANGE__START)\n    }\n\n    if (range.hasEnd) {\n      if (range.end < range.start) {\n        error(\"Invalid RPC ID range (end < start)\",\n          RenderPackage.Literals.RPC_ID_RANGE__END)\n      }\n\n      if (range.end > rpcIdRangeMax) {\n        error('''Invalid RPC ID range (end > «rpcIdRangeMax»)''',\n          RenderPackage.Literals.RPC_ID_RANGE__END)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "renderc/io.opentelemetry.render.standalone/build.gradle",
    "content": "plugins {\n  id 'application'\n  id 'com.github.johnrengelman.shadow' version '8.1.1' apply false\n}\n\napplication {\n  mainClassName = 'io.opentelemetry.render.standalone.Main'\n}\n\napply plugin: 'com.github.johnrengelman.shadow'\nshadowJar {\n  from(\"plugin.properties\")\n}\n\ndependencies {\n  implementation project(':io.opentelemetry.render')\n  implementation \"org.eclipse.xtext:org.eclipse.xtext.builder.standalone:${xtextVersion}\"\n  implementation \"args4j:args4j:2.37\"\n}\n\nclean.dependsOn(cleanGenerateXtext)\n"
  },
  {
    "path": "renderc/io.opentelemetry.render.standalone/plugin.properties",
    "content": "_UI_DiagnosticRoot_diagnostic=_UI_DiagnosticRoot_diagnostic"
  },
  {
    "path": "renderc/io.opentelemetry.render.standalone/src/Main.xtend",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\npackage io.opentelemetry.render.standalone\n\nimport java.io.File\nimport java.util.Set\n\nimport com.google.common.collect.ImmutableList\nimport com.google.common.collect.ImmutableSet\nimport com.google.inject.Guice\n\nimport org.eclipse.xtext.builder.standalone.ILanguageConfiguration\nimport org.eclipse.xtext.builder.standalone.LanguageAccessFactory\nimport org.eclipse.xtext.builder.standalone.StandaloneBuilder\nimport org.eclipse.xtext.builder.standalone.StandaloneBuilderModule\nimport org.eclipse.xtext.generator.IFileSystemAccess\nimport org.eclipse.xtext.generator.OutputConfiguration\n\nimport org.kohsuke.args4j.CmdLineParser\nimport org.kohsuke.args4j.ParserProperties\nimport org.kohsuke.args4j.CmdLineException\nimport org.kohsuke.args4j.Option\n\nclass Main {\n  @Option(name = \"-i\", usage=\"input directory\", required=true)\n  String inputDirectory\n\n  @Option(name = \"-o\", usage=\"output directory\", required=true)\n  String outputDirectory\n\n  def static void main(String[] args) {\n    new Main().doMain(args)\n  }\n\n  def void doMain(String[] args) {\n    val parser = new CmdLineParser(this, ParserProperties.defaults().withUsageWidth(80))\n\n    try {\n      parser.parseArgument(args)\n    } catch (CmdLineException e) {\n      System.err.println(\"Error: \" + e.message)\n      System.err.println(\"Usage:\")\n      parser.printUsage(System.err)\n      System.exit(-1)\n    }\n\n    val injector = Guice.createInjector(new StandaloneBuilderModule())\n    val builder = injector.getInstance(StandaloneBuilder)\n\n    builder.baseDir = new File(\".\").absolutePath\n    builder.sourceDirs = ImmutableList.<String>of(new File(inputDirectory).absolutePath)\n    builder.classPathEntries = ImmutableList.<String>of()\n\n    val languages = new LanguageAccessFactory().createLanguageAccess(\n      ImmutableList.of(new RenderLanguageConfiguration(outputDirectory)),\n      Main.getClassLoader())\n    builder.languages = languages\n\n    if (!builder.launch()) {\n      System.exit(-1);\n    }\n  }\n\n  static class RenderLanguageConfiguration implements ILanguageConfiguration {\n    String outputDirectory\n\n    new(String outputDir) {\n      outputDirectory = outputDir\n    }\n\n    override String getSetup() {\n      \"io.opentelemetry.render.RenderStandaloneSetup\"\n    }\n\n    override Set<OutputConfiguration> getOutputConfigurations() {\n      val config = new OutputConfiguration(IFileSystemAccess.DEFAULT_OUTPUT)\n      config.setOutputDirectory(outputDirectory)\n\n      return ImmutableSet.of(config)\n    }\n\n    override boolean isJavaSupport() {\n      return false\n    }\n  }\n}\n"
  },
  {
    "path": "renderc/settings.gradle",
    "content": "include 'io.opentelemetry.render'\ninclude 'io.opentelemetry.render.standalone'\n"
  },
  {
    "path": "renderc/test/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nget_property(\n  RENDER_COMPILER\n  TARGET\n    render_compiler\n  PROPERTY\n    RENDER_COMPILER_PATH\n)\nrender_compile(\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  PACKAGE\n    test\n  APPS\n    app1\n  COMPILER\n    ${RENDER_COMPILER}\n  OUTPUT_DIR\n    \"${CMAKE_BINARY_DIR}/generated\"\n  DEPENDS\n    render_compiler\n)\n\nadd_unit_test(render LIBS render_test_app1)\n"
  },
  {
    "path": "renderc/test/render_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <generated/test/app1/auto_handles.h>\n#include <generated/test/app1/containers.inl>\n#include <generated/test/app1/handles.h>\n#include <generated/test/app1/index.h>\n#include <generated/test/app1/modifiers.h>\n#include <generated/test/metrics.h>\n\n#include <gtest/gtest.h>\n\n#include <unordered_map>\n\n// Test auto handle, which hold references when they are in scope\nTEST(RenderTest, AutoHandle)\n{\n  test::app1::Index index;\n\n  {\n    auto span = index.simple_span.alloc();\n    ASSERT_TRUE(span.valid());\n\n    ASSERT_EQ(index.simple_span.size(), 1ul);\n  }\n\n  // When the auto handle goes out of scope, the span should be freed\n  ASSERT_EQ(index.simple_span.size(), 0ul);\n\n  {\n    auto span = index.simple_span.alloc();\n    ASSERT_TRUE(span.valid());\n\n    ASSERT_EQ(index.simple_span.size(), 1ul);\n\n    // Manually put the reference\n    span.put();\n    // `put()` should make the handle invalid to make later accesses fail, and facilitate debugging\n    ASSERT_FALSE(span.valid());\n\n    // The span should be freed after put even though the auto handle is still in scope\n    ASSERT_EQ(index.simple_span.size(), 0ul);\n  }\n}\n\n// Test handles, which are used for memory-efficient reference storage\nTEST(RenderTest, Handle)\n{\n  static constexpr u32 the_number = 42;\n\n  test::app1::Index index;\n\n  auto auto_handle = index.simple_span.alloc();\n  ASSERT_TRUE(auto_handle.valid());\n\n  // Set the integer field to some important number.\n  auto_handle.modify().number(the_number);\n  ASSERT_EQ(auto_handle.number(), the_number);\n\n  // Convert auto-handle to handle.\n  auto handle = auto_handle.to_handle();\n  ASSERT_TRUE(handle.valid());\n\n  // Auto-handle is released.\n  ASSERT_FALSE(auto_handle.valid());\n  \n  // The conversion to handle should not release the span\n  ASSERT_EQ(index.simple_span.size(), 1ul);\n\n  // Check that it's the same span.\n  ASSERT_EQ(handle.access(index).number(), the_number);\n\n  // Put()ing the reference should invalidate the handle and free the span\n  handle.put(index);\n  ASSERT_FALSE(handle.valid());\n  ASSERT_EQ(index.simple_span.size(), 0ul);\n}\n\n// Test moving auto-handles to handles, which is usually done when holding handles in containers.\nTEST(RenderTest, MovedToHandle)\n{\n  static constexpr u32 the_number = 42;\n\n  test::app1::Index index;\n\n  std::unordered_map<u32, test::app1::handles::simple_span> handles;\n\n  {\n    auto auto_handle = index.simple_span.alloc();\n    ASSERT_TRUE(auto_handle.valid());\n\n    // Move the reference from the auto-handle into the hashmap.\n    auto [it, inserted] = handles.try_emplace(the_number, std::move(auto_handle));\n    ASSERT_TRUE(inserted);\n\n    // Reference is moved-out, so this auto-handle no longer holds it.\n    ASSERT_FALSE(auto_handle.valid());\n\n    // Only one span is allocated.\n    ASSERT_EQ(index.simple_span.size(), 1ul);\n  }\n\n  {\n    auto auto_handle = index.simple_span.alloc();\n    ASSERT_TRUE(auto_handle.valid());\n\n    // Since this key already exists inside the hashmap, `try_emplace` will do nothing.\n    auto [it, inserted] = handles.try_emplace(the_number, std::move(auto_handle));\n    ASSERT_FALSE(inserted);\n\n    // Auto-handle still holds the reference.\n    ASSERT_TRUE(auto_handle.valid());\n\n    // In total two spans are allocated.\n    ASSERT_EQ(index.simple_span.size(), 2ul);\n  }\n\n  // This would fail with `test::app1::handles::simple_span::~simple_span(): Assertion `!valid()' failed.`\n  //\n  //{\n  //  auto auto_handle = index.simple_span.alloc();\n  //  handles.insert({the_number, auto_handle.to_handle()});\n  //}\n\n  // Put()-back all references in the hashmap.\n  for (auto &[key, handle] : handles) {\n    handle.put(index);\n  }\n\n  ASSERT_EQ(index.simple_span.size(), 0ul);\n}\n\n// Test lookup and reference counting of indexed spans\nTEST(RenderTest, IndexedSpan)\n{\n  static constexpr u32 key = 42;\n\n  test::app1::Index index;\n\n  {\n    // Allocate a span\n    auto ahandle = index.indexed_span.by_key(key);\n    ASSERT_TRUE(ahandle.valid());\n    ASSERT_EQ(ahandle.number(), key);\n\n    ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n    {\n      // Get another reference to the same span using lookup\n      auto another = index.indexed_span.by_key(key);\n      ASSERT_TRUE(another.valid());\n\n      // Still only one span is allocated.\n      ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n      // It's the same span.\n      ASSERT_EQ(ahandle.loc(), another.loc());\n    }\n\n    // The first reference is still in scope, 'another' should not free the span\n    ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n    {\n      auto different = index.indexed_span.by_key(key + 1);\n      ASSERT_TRUE(different.valid());\n\n      // Additional span has been allocated.\n      ASSERT_EQ(index.indexed_span.size(), 2ul);\n\n      // It's not the same span.\n      ASSERT_NE(ahandle.loc(), different.loc());\n    }\n  }\n\n  ASSERT_EQ(index.indexed_span.size(), 0ul);\n}\n\n// Test MetricStore updates and iteration, and its interaction with span reference counting\nTEST(RenderTest, MetricStore)\n{\n  using metrics_visitor_t =\n      std::function<void(u64, test::app1::weak_refs::metrics_span, test::metrics::some_metrics const &, u64)>;\n\n  static constexpr u64 timeslot_duration = 1'000'000'000;\n  u64 time_now = 1;\n\n  test::app1::Index index;\n\n  test::metrics::some_metrics_point input_metrics{\n      .active = 55,\n      .total = 100,\n  };\n\n  {\n    auto span = index.metrics_span.alloc();\n    ASSERT_TRUE(span.valid());\n\n    ASSERT_EQ(index.metrics_span.size(), 1ul);\n    ASSERT_EQ(span.refcount(), 1ul);\n\n    span.metrics_update(time_now, input_metrics);\n\n    ASSERT_EQ(index.metrics_span.size(), 1ul);\n\n    // Metric store is keeping a reference to this span.\n    ASSERT_EQ(span.refcount(), 2ul);\n  }\n\n  // Metric store is keeping the span allocated.\n  ASSERT_EQ(index.metrics_span.size(), 1ul);\n\n  // Metric slot should not be ready yet.\n  ASSERT_FALSE(index.metrics_span.metrics_ready(time_now));\n\n  // Advance the current time.\n  time_now += 2 * timeslot_duration;\n\n  // Metrics slot should be ready.\n  ASSERT_TRUE(index.metrics_span.metrics_ready(time_now));\n\n  // Get the metrics from the current slot.\n  int metric_counter{0};\n  test::metrics::some_metrics slot_metrics{0};\n  metrics_visitor_t on_metric = [&metric_counter, &slot_metrics](u64 timestamp, auto span, auto metrics, u64 interval) {\n    ++metric_counter;\n    slot_metrics = metrics;\n  };\n  index.metrics_span.metrics_foreach(time_now, on_metric);\n\n  // Only one metrics slot.\n  ASSERT_EQ(metric_counter, 1);\n\n  // Output metrics match input metrics.\n  ASSERT_EQ(slot_metrics.active, input_metrics.active);\n  ASSERT_EQ(slot_metrics.total, input_metrics.total);\n\n  // Metrics store should be cleared out.\n  ASSERT_TRUE(index.metrics_span.metrics.current_queue().empty());\n\n  // Metric store should no longer keep a reference to the span.\n  ASSERT_EQ(index.metrics_span.size(), 0ul);\n}\n\nTEST(RenderTest, ManualReference)\n{\n  test::app1::Index index;\n\n  auto span = index.span_with_manual_reference.alloc();\n  ASSERT_TRUE(span.valid());\n\n  auto simple_loc = span.manual_reference().loc();\n\n  // Currently the reference is an invalid reference.\n  ASSERT_EQ(simple_loc, span.manual_reference().invalid);\n\n  // No simple_span is allocated.\n  ASSERT_EQ(index.simple_span.size(), 0ul);\n\n  {\n    auto s = index.simple_span.alloc();\n    ASSERT_TRUE(s.valid());\n\n    // Save the location of this newly-allocated simple_span.\n    simple_loc = s.loc();\n\n    // Assign it as the reference.\n    span.modify().manual_reference(s.get());\n\n    ASSERT_EQ(index.simple_span.size(), 1ul);\n    ASSERT_EQ(span.manual_reference().refcount(), 2ul);\n  }\n\n  ASSERT_TRUE(span.manual_reference().valid());\n  ASSERT_EQ(span.manual_reference().refcount(), 1ul);\n\n  // It's the same simple_span.\n  ASSERT_EQ(simple_loc, span.manual_reference().loc());\n}\n\n// Test auto references, which are recomputed whenever relevant span fields are modified\nTEST(RenderTest, AutoReference)\n{\n  static constexpr u32 key_one = 11;\n  static constexpr u32 key_two = 22;\n\n  test::app1::Index index;\n\n  // Allocate 'key_one' directly on the index\n  auto indexed = index.indexed_span.by_key(key_one);\n  ASSERT_TRUE(indexed.valid());\n  ASSERT_EQ(indexed.number(), key_one);\n\n  // Only one indexed_span exists for now (namely indexed_span{key_one}).\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  auto span = index.span_with_auto_reference.alloc();\n  ASSERT_TRUE(span.valid());\n\n  // The auto-reference is not yet valid because the `number` field that is used in the reference key (see test.render)\n  // has not been assigned.\n  ASSERT_FALSE(span.auto_reference().valid());\n\n  // Still only one indexed_span exists (indexed_span{key_one}).\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  // Assign the field that is used in the auto-reference key.\n  span.modify().number(key_two);\n\n  // This caused the reference to be computed and a new indexed_span to be allocated (indexed_span{key_two}).\n  ASSERT_EQ(index.indexed_span.size(), 2ul);\n\n  // Now the reference is valid.\n  ASSERT_TRUE(span.auto_reference().valid());\n\n  // We have two different indexed_span instances: indexed_span{key_one} and indexed_span{key_two}.\n  ASSERT_NE(indexed.loc(), span.auto_reference().loc());\n  ASSERT_EQ(indexed.number(), key_one);\n  ASSERT_EQ(span.auto_reference().number(), key_two);\n\n  // Set the field that is used in the reference key to the `key_one` value.\n  span.modify().number(key_one);\n\n  // This caused the reference to be recomputed, and now the reference points to indexed_span{key_one}, while the\n  // indexed_span{key_two} instance has been free'd.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  // Those two are the same indexed_span instance (indexed_span{key_one}).\n  ASSERT_EQ(indexed.loc(), span.auto_reference().loc());\n\n  // Release the handle.\n  indexed.put();\n\n  // The auto-reference is keeping the span allocated.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n}\n\n// Test cached references, which are references that are re-computed when they are accessed\nTEST(RenderTest, CachedReference)\n{\n  static constexpr u32 key_one = 11;\n  static constexpr u32 key_two = 22;\n\n  test::app1::Index index;\n\n  auto indexed = index.indexed_span.by_key(key_one);\n  ASSERT_TRUE(indexed.valid());\n  ASSERT_EQ(indexed.number(), key_one);\n\n  // Only one indexed_span exists for now -- indexed_span{key_one}.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  auto span = index.span_with_cached_reference.alloc();\n  ASSERT_TRUE(span.valid());\n\n  // Still only one indexed_span exists.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  // Assign the field that is used in the reference key (see test.render).\n  span.modify().number(key_two);\n\n  // And still only one indexed_span exists.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  // Accessing the reference.\n  ASSERT_TRUE(span.cached_reference().valid());\n\n  // Accessing the referencing caused the indexed_span{key_two} to be allocated.\n  ASSERT_EQ(index.indexed_span.size(), 2ul);\n\n  // We have two different indexed_span instances: indexed_span{key_one} and indexed_span{key_two}.\n  ASSERT_NE(indexed.loc(), span.cached_reference().loc());\n  ASSERT_EQ(indexed.number(), key_one);\n  ASSERT_EQ(span.cached_reference().number(), key_two);\n\n  // Set the field that is used in the reference key to the `key_one` value.\n  span.modify().number(key_one);\n\n  // Still two are allocated -- reference will be recomputed only after it is accessed.\n  ASSERT_EQ(index.indexed_span.size(), 2ul);\n\n  // Access the reference, causing it to be recomputed.\n  ASSERT_TRUE(span.cached_reference().valid());\n\n  // We're back to there being only one indexed_span -- indexed_span{key_one}.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n\n  // Those are two same indexed_span instances (indexed_span{key_one}).\n  ASSERT_EQ(indexed.loc(), span.cached_reference().loc());\n\n  // Release the handle.\n  indexed.put();\n\n  // The cached reference is keeping the span allocated.\n  ASSERT_EQ(index.indexed_span.size(), 1ul);\n}\n"
  },
  {
    "path": "renderc/test/test.render",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\npackage test\n\nnamespace {\n  app1: 0-99\n}\n\napp app1 {\n\n  span simple_span {\n    u32 number\n  }\n\n  span indexed_span {\n    index (number)\n    u32 number\n  }\n\n  span metrics_span {\n    aggregate metrics (root type some_metrics interval 1 slots 1)\n  }\n\n  span span_with_manual_reference {\n    reference<simple_span> manual_reference\n  }\n\n  span span_with_auto_reference {\n    u32 number\n\n    reference<indexed_span> auto_reference auto {\n      number = number\n    }\n  }\n\n  span span_with_cached_reference {\n    u32 number\n\n    reference<indexed_span> cached_reference cached {\n      number = number\n    }\n  }\n\n} // app app1\n\nmetric some_metrics {\n  u32 active\n  u32 total\n}\n"
  },
  {
    "path": "scheduling/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nadd_library(\n  scheduling\n  STATIC\n    interval_scheduler.cc\n    timer.cc\n)\ntarget_compile_options(\n  scheduling\n  PRIVATE\n    ${CXX_ERROR_LIMIT_FLAG}=1\n)\ntarget_link_libraries(\n  scheduling\n    libuv-interface\n    spdlog\n)\n"
  },
  {
    "path": "scheduling/interval_scheduler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <scheduling/interval_scheduler.h>\n\n#include <util/log.h>\n\n#include <utility>\n\n#include <cassert>\n\nnamespace scheduling {\n\nIntervalScheduler::IntervalScheduler(uv_loop_t &loop, JobType job)\n    : job_(std::move(job)), timer_(loop, std::bind(&IntervalScheduler::callback, this))\n{}\n\nbool IntervalScheduler::defer(TimerPeriod timeout)\n{\n  return start(timeout, TimerPeriod::zero());\n}\n\nbool IntervalScheduler::start(TimerPeriod timeout, TimerPeriod interval)\n{\n  reset_backoff();\n  interval_ = interval;\n\n  if (auto result = timer_.defer(timeout); !result) {\n    LOG::error(\"unable to start interval scheduler: {}\", result.error());\n    return false;\n  }\n\n  return started_ = true;\n}\n\nbool IntervalScheduler::start(TimerPeriod timeout)\n{\n  return start(timeout, timeout);\n}\n\nbool IntervalScheduler::restart()\n{\n  if (auto result = timer_.restart(); !result) {\n    LOG::error(\"unable to restart interval scheduler: {}\", result.error());\n    return false;\n  }\n\n  return started_ = true;\n}\n\nbool IntervalScheduler::stop()\n{\n  if (!started_) {\n    return true;\n  }\n\n  if (auto result = timer_.stop(); !result) {\n    LOG::error(\"unable to stop interval scheduler: {}\", result.error());\n    return false;\n  }\n\n  started_ = false;\n\n  return true;\n}\n\nvoid IntervalScheduler::callback()\n{\n  switch (job_()) {\n  case JobFollowUp::ok: {\n    if (backoff_count_) {\n      reset_backoff();\n    }\n\n    if (interval_ != TimerPeriod::zero()) {\n      timer_.defer(interval_);\n    }\n    break;\n  }\n\n  case JobFollowUp::stop: {\n    stop();\n    break;\n  }\n\n  case JobFollowUp::backoff: {\n    ++backoff_count_;\n    backoff_ratio_ *= BACKOFF_RATIO;\n\n    auto const interval = interval_ * backoff_ratio_;\n    timer_.defer(interval);\n    break;\n  }\n\n  default:\n    assert(false);\n    break;\n  }\n}\n\nvoid IntervalScheduler::reset_backoff()\n{\n  backoff_count_ = 0;\n  backoff_ratio_ = 1;\n}\n\n} // namespace scheduling\n"
  },
  {
    "path": "scheduling/interval_scheduler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <scheduling/job.h>\n#include <scheduling/timer.h>\n\n#include <functional>\n\nnamespace scheduling {\n\n/**\n * A scheduler for jobs that runs on a fixed interval using a timer, supporting a geometric\n * back-off strategy.\n *\n * The job must provide a call operator taking no arguments. If it returns `JobFollowUp`,\n * then that will be used to control follow up runs. If it returns anything else, the return\n * value will be ignored and `JobFollowUp::ok` will be assumed.\n *\n * Job control based on returning the enum `JobFollowUp`:\n *\n *  - ok: proceed normally running the job at the previously specified interval\n *  - stop: stops the timer, no further calls to the job will be made\n *  - backoff: the next iterations of the job will be run on a different interval, according\n *    to the backoff strategy, untill something other than `backoff` is returned.\n *\n * Example:\n *\n *  void main_loop(uv_loop_t &loop) {\n *    IntervalScheduler scheduler(loop, [] {\n *      auto response = http_get(\"www.foo.bar\");\n *\n *      if (!response) {\n *        return JobFollowUp::backoff;\n *      }\n *\n *      process_response(response);\n *\n *      return JobFollowUp::ok;\n *    });\n *\n *    // runs the job every 10 seconds\n *    scheduler.start(10s);\n *  }\n */\nclass IntervalScheduler {\npublic:\n  using JobType = std::function<JobFollowUp()>;\n  using TimerPeriod = Timer::TimerPeriod;\n\n  /**\n   * Constructs an interval scheduler on the given event loop, to run the given job.\n   */\n  explicit IntervalScheduler(uv_loop_t &loop, JobType job);\n\n  /**\n   * Runs the job a single time in the future.\n   */\n  bool defer(TimerPeriod timeout);\n\n  /**\n   * Runs the job every `interval` time, starting at `timeout` from now.\n   *\n   * If `interval` is 0, behaves just like `defer`.\n   */\n  bool start(TimerPeriod timeout, TimerPeriod interval);\n\n  /**\n   * Runs the job every `timeout` time, starting at `timeout` from now.\n   */\n  bool start(TimerPeriod timeout);\n\n  /**\n   * Stops and restarts the job with the pre-configured `timeout`.\n   *\n   * Requires the job to have been started at some point.\n   */\n  bool restart();\n\n  /**\n   * Stops subsequent job runs.\n   */\n  bool stop();\n\n  /**\n   * Gets a reference to the timer.\n   */\n  auto &timer() { return timer_; }\n\nprivate:\n  void callback();\n\n  void reset_backoff();\n\n  std::size_t backoff_count_ = 0;\n  std::size_t backoff_ratio_ = 1;\n  TimerPeriod interval_;\n  JobType job_;\n  Timer timer_;\n  bool started_ = false;\n\n  static constexpr std::size_t BACKOFF_RATIO = 2;\n};\n\n} // namespace scheduling\n"
  },
  {
    "path": "scheduling/job.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\nnamespace scheduling {\n\nenum class JobFollowUp {\n  // proceed with follow up jobs as normal\n  ok,\n  // back-off follow-up jobs\n  backoff,\n  // prevent the execution of any follow-up jobs\n  stop\n};\n\n} // namespace scheduling\n"
  },
  {
    "path": "scheduling/timer.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <scheduling/timer.h>\n\nnamespace scheduling {\n\nTimer::Timer(uv_loop_t &loop, CallbackType callback) : callback_(std::move(callback))\n{\n  handle_.data = this;\n\n  if (auto const error = ::uv_timer_init(&loop, &handle_)) {\n    throw std::runtime_error(::uv_strerror(error));\n  }\n}\n\nTimer::Timer(Timer &&rhs) : handle_(std::move(rhs.handle_)), callback_(std::move(rhs.callback_))\n{\n  handle_.data = this;\n  rhs.handle_.data = nullptr;\n}\n\nTimer::~Timer()\n{\n  if (!handle_.data) {\n    return;\n  }\n\n  if (is_active()) {\n    stop();\n  }\n  if (!uv_is_closing(reinterpret_cast<uv_handle_t *>(&handle_))) {\n    uv_close(reinterpret_cast<uv_handle_t *>(&handle_), nullptr);\n  }\n}\n\nExpected<bool, uv_error_t> Timer::defer(TimerPeriod timeout)\n{\n  return start(timeout, TimerPeriod::zero());\n}\n\nExpected<bool, uv_error_t> Timer::start(TimerPeriod timeout, TimerPeriod interval)\n{\n  assert(!is_active());\n\n  if (auto const error = uv_timer_start(&handle_, callback, timeout.count(), interval.count())) {\n    return {unexpected, uv_error_t(error)};\n  }\n\n  return true;\n}\n\nExpected<bool, uv_error_t> Timer::restart()\n{\n  assert(is_active());\n\n  if (auto const error = uv_timer_again(&handle_)) {\n    return {unexpected, uv_error_t(error)};\n  }\n\n  return true;\n}\n\nvoid Timer::update(TimerPeriod interval)\n{\n  uv_timer_set_repeat(&handle_, interval.count());\n}\n\nExpected<bool, uv_error_t> Timer::stop()\n{\n  assert(is_active());\n\n  if (auto const error = uv_timer_stop(&handle_)) {\n    return {unexpected, uv_error_t(error)};\n  }\n\n  return true;\n}\n\nbool Timer::is_active() const\n{\n  return uv_is_active(reinterpret_cast<uv_handle_t const *>(&handle_));\n}\n\nvoid Timer::callback(uv_timer_t *handle)\n{\n  reinterpret_cast<Timer *>(handle->data)->callback_();\n};\n\n} // namespace scheduling\n"
  },
  {
    "path": "scheduling/timer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/expected.h>\n#include <util/uv_helpers.h>\n\n#include <uv.h>\n\n#include <chrono>\n#include <functional>\n#include <stdexcept>\n#include <utility>\n\n#include <cassert>\n\nnamespace scheduling {\n\n/**\n * An abstraction for libuv's timer.\n *\n * For portability purposes and to avoid library lock-in, this class shouldn't be used directly.\n * Instead, use abstractions like `scheduling/interval_scheduler.h`.\n */\nclass Timer {\npublic:\n  using TimerPeriod = std::chrono::milliseconds;\n  using CallbackType = std::function<void()>;\n\n  /**\n   * Constructs a timer to be run on the given event loop, to execute the\n   * given callback.\n   */\n  Timer(uv_loop_t &loop, CallbackType callback);\n\n  Timer(Timer const &) = delete;\n\n  /**\n   * If the timer is moved after it has been started, the behavior is undefined.\n   */\n  Timer(Timer &&rhs);\n\n  /**\n   * Dtor.\n   */\n  ~Timer();\n\n  /**\n   * Schedules the callback to be called once after `timeout`.\n   */\n  Expected<bool, uv_error_t> defer(TimerPeriod timeout);\n\n  /**\n   * Schedules the callback to be called every `interval`, with the first call\n   * happening after `timeout`.\n   */\n  Expected<bool, uv_error_t> start(TimerPeriod timeout, TimerPeriod interval);\n\n  /**\n   * Stops the timer. If it was initially started with an `interval`, restarts\n   * it after `interval` to repeat every `interval`.\n   */\n  Expected<bool, uv_error_t> restart();\n\n  /**\n   * Updates the repeat `interval` of the timer.\n   */\n  void update(TimerPeriod interval);\n\n  /**\n   * Stops the timer.\n   */\n  Expected<bool, uv_error_t> stop();\n\n  /**\n   * Returns the libuv handle for the timer.\n   */\n  uv_timer_t &native_handle() { return handle_; }\n\nprivate:\n  bool is_active() const;\n\n  static void callback(uv_timer_t *handle);\n\n  uv_timer_t handle_;\n  CallbackType callback_;\n};\n\n} // namespace scheduling\n"
  },
  {
    "path": "scratchpad/modules.md",
    "content": "# Kernel Collector Module Survey\n\nThis document maps the current monolithic eBPF program into conceptual modules by entity type. For each module it lists:\n- C++ counterparts that handle messages from eBPF\n- Messages produced by eBPF and their handlers\n- Hooks used (tagged as START, END, EXISTING, TELEMETRY) and which message(s) they generate\n- eBPF maps and other state used\n\nPaths referenced below use repository‑relative locations.\n\n---\n\n## Processes\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.cc`: `handle_pid_*`, dispatches to `ProcessHandler`\n  - `collector/kernel/process_handler.{h,cc}`: maintains process table and emits ingest messages\n\n- Messages (BufferedPoller handlers)\n  - `pid_info` → `BufferedPoller::handle_pid_info()` → `ProcessHandler::on_new_process()`\n  - `pid_close` → `BufferedPoller::handle_pid_close()` → `ProcessHandler::on_process_end()`\n  - `pid_set_comm` → `BufferedPoller::handle_pid_set_comm()` → `ProcessHandler::set_process_command()`\n  - `pid_exit` → `BufferedPoller::handle_pid_exit()` → `ProcessHandler::pid_exit()`\n  - `cgroup_attach_task` → `BufferedPoller::handle_cgroup_attach_task()` → `ProcessHandler::on_cgroup_move()`\n\n- Hooks and classification (rendered by `collector/kernel/bpf_src/render_bpf.c`)\n  - START\n    - `kprobe/wake_up_new_task` → `perf_submit_agent_internal__pid_info` (new process)\n  - END\n    - `kretprobe/cgroup_exit` → `perf_submit_agent_internal__pid_close`\n  - EXISTING\n    - `kretprobe/get_pid_task` → `perf_submit_agent_internal__pid_info` (discovery)\n  - TELEMETRY\n    - `kprobe/__set_task_comm` → `perf_submit_agent_internal__pid_set_comm` (command updates)\n    - Helper exists: `report_pid_exit(...)` → `perf_submit_agent_internal__pid_exit` (task exits)\n\n- Maps and state\n  - `tgid_info_table` (HASH): set of TGIDs already reported; dedupes START/EXISTING\n  - `dead_group_tasks` (HASH): marks thread‑group leaders seen by `taskstats_exit`; used to gate END\n\n---\n\n## Containers (cgroups)\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.cc`: `handle_kill_css`, `handle_css_populate_dir`, `handle_existing_cgroup_probe`, `handle_cgroup_attach_task`\n  - `collector/kernel/cgroup_handler.{h,cc}`: tracks cgroup hierarchy and fetches Docker/K8s metadata\n\n- Messages (BufferedPoller handlers)\n  - `kill_css` → `CgroupHandler::kill_css()`; also emits `writer_.cgroup_close_tstamp`\n  - `css_populate_dir` → `CgroupHandler::css_populate_dir()`; also emits `writer_.cgroup_create_tstamp`\n  - `existing_cgroup_probe` → `CgroupHandler::existing_cgroup_probe()`; also emits `writer_.cgroup_create_tstamp`\n  - `cgroup_attach_task` → `CgroupHandler::cgroup_attach_task()`; also `ProcessHandler::on_cgroup_move()` and `writer_.pid_cgroup_move_tstamp`\n\n- Hooks and classification (in `render_bpf.c`)\n  - START\n    - `kprobe/css_populate_dir` (>= 4.4) → `perf_submit_agent_internal__css_populate_dir`\n    - `kprobe/cgroup_populate_dir` (< 4.4) → `perf_submit_agent_internal__css_populate_dir`\n  - END\n    - `kprobe/kill_css` (>= 3.12) → `perf_submit_agent_internal__kill_css`\n    - `kprobe/cgroup_destroy_locked` (< 3.12) → `perf_submit_agent_internal__kill_css`\n  - EXISTING\n    - `kprobe/cgroup_control` + `kretprobe/cgroup_control` (>= 4.6) → `perf_submit_agent_internal__existing_cgroup_probe`\n    - `kretprobe/cgroup_get_from_fd` → `perf_submit_agent_internal__existing_cgroup_probe`\n    - `kprobe/cgroup_clone_children_read` (v1) → `perf_submit_agent_internal__existing_cgroup_probe`\n  - TELEMETRY\n    - `kprobe/cgroup_attach_task` → `perf_submit_agent_internal__cgroup_attach_task`\n\n- Maps and state\n  - No dedicated BPF maps; uses cgroup APIs and perf ring\n\n---\n\n## TCP Sockets (lifecycle, addressing, metrics)\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: TCP table/stats; handlers for tcp lifecycle/metrics messages\n  - NAT correlation done in `NatHandler` for `set_state_*`\n\n- Messages (BufferedPoller handlers)\n  - Lifecycle\n    - `new_sock_created` → `handle_new_socket`\n    - `close_sock_info` → `handle_close_socket`\n  - Addressing/state\n    - `set_state_ipv4` → `handle_set_state_ipv4` (also forwards to `NatHandler`)\n    - `set_state_ipv6` → `handle_set_state_ipv6` (also forwards to `NatHandler`)\n  - Metrics/telemetry\n    - `rtt_estimator` → `handle_rtt_estimator`\n    - `reset_tcp_counters` → `handle_reset_tcp_counters` (initial metrics snapshot)\n    - `tcp_syn_timeout` → `handle_tcp_syn_timeout`\n    - `tcp_reset` → `handle_tcp_reset`\n  - Protocol\n    - `http_response` → `handle_http_response`\n    - `tcp_data` → `handle_tcp_data` (delegates to `TCPDataHandler`)\n\n- Hooks and classification (in `render_bpf.c`, plus TCP processor includes)\n  - START\n    - `kprobe/tcp_init_sock` → `perf_submit_agent_internal__new_sock_created`\n    - `kretprobe/inet_csk_accept` → `perf_submit_agent_internal__new_sock_created` (+ `set_state_*`)\n  - END\n    - `kprobe/security_sk_free` → `perf_submit_agent_internal__close_sock_info`\n    - `kretprobe/inet_release` → `perf_submit_agent_internal__close_sock_info`\n  - EXISTING\n    - `kprobe/tcp4_seq_show`, `kprobe/tcp6_seq_show` → ensure existing → `reset_tcp_counters` + `set_state_*`\n  - TELEMETRY\n    - Address: `kprobe/tcp_connect`, `kprobe/inet_csk_listen_start` → `set_state_*`\n    - RTT/health: `kprobe/tcp_rtt_estimator`, `kprobe/tcp_rcv_established`, `kprobe/tcp_event_data_recv` → `rtt_estimator`\n    - SYN timeouts: `kprobe/tcp_retransmit_timer`, `kprobe/tcp_syn_ack_timeout` → `tcp_syn_timeout`\n    - Resets: `kprobe/tcp_reset`, and transmit path in `kprobe/ip_send_skb`, `kprobe/ip6_send_skb` (RST detection) → `tcp_reset`\n    - Protocol data: TCP processor hooks `kprobe/tcp_sendmsg`/`kretprobe/tcp_sendmsg` and `kprobe/tcp_recvmsg`/`kretprobe/tcp_recvmsg` (via tail calls) → `tcp_data` and `http_response`\n\n- Maps and state\n  - `tcp_open_sockets` (HASH): tracks live sockets, last stats, parent listen socket\n  - `seen_inodes` (HASH): dedupe/seeding for EXISTING path via `/proc` seq_show hooks\n  - `tail_calls` (PROG_ARRAY): tail calls for multi‑stage handlers\n  - TCP processor maps (in `collector/kernel/bpf_src/tcp-processor/`):\n    - `_tcp_connections` (HASH): connection state\n    - `_tcp_control` (HASH): backchannel enable/start per stream; read/written by `TCPDataHandler`\n    - `data_channel` (PERF_EVENT_ARRAY): per‑CPU perf ring for TCP payload chunks\n\n---\n\n## UDP Sockets (lifecycle, stats)\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: handlers `handle_udp_new_socket`, `handle_udp_destroy_socket`, `handle_udp_stats`\n  - DNS handling is integrated here (see DNS module)\n\n- Messages (BufferedPoller handlers)\n  - `udp_new_socket` → `handle_udp_new_socket`\n  - `udp_destroy_socket` → `handle_udp_destroy_socket`\n  - `udp_stats` → `handle_udp_stats` (tx/rx packets/bytes; addr changes; rx drops)\n\n- Hooks and classification (in `render_bpf.c`)\n  - START\n    - `kretprobe/udp_v4_get_port`, `kretprobe/udp_v6_get_port` (success) → ensure `udp_open_socket` → `udp_new_socket`\n  - END\n    - `kprobe/security_sk_free`, `kretprobe/inet_release` → `udp_destroy_socket`\n  - EXISTING\n    - `kprobe/udp4_seq_show`, `kprobe/udp6_seq_show` → ensure existing → `udp_new_socket`\n  - TELEMETRY\n    - TX path: `kprobe/udp_send_skb`, `kprobe/udp_v6_send_skb`, `kprobe/ip_send_skb`, `kprobe/ip6_send_skb` (+ tail calls) → `udp_stats`\n    - RX path: `kprobe/__skb_free_datagram_locked`, `kprobe/skb_free_datagram_locked` (tail call to `handle_receive_udp_skb`) → `udp_stats`\n\n- Maps and state\n  - `udp_open_sockets` (HASH): per‑socket stats and last reported endpoints (tx/rx)\n  - `udp_get_port_hash` (HASH): per‑CPU scratch to link `udp_v*_get_port` kprobe/kretprobe\n  - `seen_inodes` (HASH): shared dedupe for EXISTING via `/proc` seq_show\n  - `tail_calls` (PROG_ARRAY): multi‑stage send/receive and DNS splitting\n\n---\n\n## NAT\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: delegates to `NatHandler` for NAT messages and to correlate with socket state\n  - `collector/kernel/nat_handler.{h,cc}`: manages NAT mappings and emits `nat_remapping` ingest messages\n\n- Messages (BufferedPoller → NatHandler)\n  - `nf_conntrack_alter_reply` (START) → `NatHandler::handle_nf_conntrack_alter_reply()` (records NAT mapping; may emit remapping if socket known)\n  - `nf_nat_cleanup_conntrack` (END) → `NatHandler::handle_nf_nat_cleanup_conntrack()` (removes mapping)\n  - `existing_conntrack_tuple` (EXISTING) → `NatHandler::handle_existing_conntrack_tuple()` (learns both directions; records mapping if NAT‑ed)\n  - Also consumes socket addressing:\n    - `set_state_ipv4`/`set_state_ipv6` → record `sk ↔ 4‑tuple` for remapping correlation\n    - `close_sock_info` → cleanup socket ↔ tuple index\n\n- Hooks and classification (in `render_bpf.c`)\n  - START: `kprobe/nf_conntrack_alter_reply` → `nf_conntrack_alter_reply`\n  - END: `kprobe/nf_nat_cleanup_conntrack` → `nf_nat_cleanup_conntrack`\n  - EXISTING: `kprobe/ctnetlink_dump_tuples` → `existing_conntrack_tuple`\n  - TELEMETRY: (none standalone; NAT remap emission is driven by above plus socket `set_state_*`)\n\n- Maps and state\n  - BPF: `seen_conntracks` (HASH): marks conntracks seen to gate END reporting\n  - Userland (`NatHandler`): `nat_table_`, `nat_table_rev_`, `existing_conntrack_table_`, `existing_sk_table_`, `existing_sk_table_rev_`\n\n---\n\n## DNS\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: `handle_dns_message()`; manages request table (`DnsRequests`) and emits `dns_request/response/timeout` ingest messages\n\n- Messages\n  - `dns_packet` (raw UDP payload and direction flag). BufferedPoller parses and correlates into higher‑level ingest records.\n\n- Hooks and classification (in `render_bpf.c`)\n  - TELEMETRY only\n    - TX detection: `kprobe/udp_send_skb`, `kprobe/udp_v6_send_skb`, `kprobe/ip_send_skb`, `kprobe/ip6_send_skb` and tail‑call stages `on_*__2` → `perf_check_and_submit_dns`\n    - RX detection: `kprobe/__skb_free_datagram_locked`, `kprobe/skb_free_datagram_locked` → tail call `handle_receive_udp_skb`/`__2` → `perf_check_and_submit_dns`\n\n- Maps and state\n  - `dns_message_array` (PERCPU_ARRAY): per‑CPU scratch buffer to copy DNS payloads safely\n  - Reuses `udp_open_sockets` to resolve `sk` → socket entry for correlation\n  - Userland: `DnsRequests` table for outstanding queries and timeouts\n\n---\n\n## TCP Data / HTTP Protocol\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: `handle_tcp_data()` delegates to `TCPDataHandler`\n  - `collector/kernel/tcp_data_handler.{h,cc}`: reads `data_channel` rings, applies per‑socket protocol handlers, and emits ingest\n\n- Messages\n  - `tcp_data` (control message with sk/pid/len/offset/dir)\n  - `http_response` (status/latency/client_server)\n\n- Hooks and classification\n  - TELEMETRY only\n    - `kprobe/tcp_sendmsg`/`kretprobe/tcp_sendmsg` (tail‑called via `continue_tcp_sendmsg`)\n    - `kprobe/tcp_recvmsg`/`kretprobe/tcp_recvmsg` (tail‑called via `continue_tcp_recvmsg`)\n    - Lifecycle hooks for data map cleanup are shared with the main TCP module (`tcp_init_sock`, `security_sk_free`)\n\n- Maps and state\n  - `_tcp_connections`, `_tcp_control` (HASH): connection state and backchannel control\n  - `data_channel` (PERF_EVENT_ARRAY): per‑CPU ring for payload chunks\n\n---\n\n## BPF Logging / Diagnostics\n\n- C++ counterparts\n  - `collector/kernel/buffered_poller.{h,cc}`: `handle_bpf_log`, `handle_stack_trace`\n  - `collector/kernel/probe_handler.{h,cc}`: stack trace resolution utilities\n\n- Messages\n  - `bpf_log` (throttled; error codes/args)\n  - `stack_trace` (optional; when `DEBUG_ENABLE_STACKTRACE`)\n\n- Hooks and classification\n  - TELEMETRY only; emitted from many code paths via helper macros\n\n- Maps and state\n  - Log throttle state (per‑CPU) and stack trace lookup (via probe handler); primary perf ring `events`\n\n---\n\n## Perf Rings\n\n- Control/events ring: `events` (PERF_EVENT_ARRAY) in `render_bpf.c` carries all control messages listed above.\n- Data ring(s): `data_channel` (PERF_EVENT_ARRAY) in TCP processor streams TCP payload chunks; `TCPDataHandler` reads the per‑CPU ring corresponding to the control ring’s CPU index for ordering.\n\n---\n\n## Quick Cross‑Reference of Message → Module\n\n- Processes: `pid_info`, `pid_close`, `pid_set_comm`, `pid_exit`, `cgroup_attach_task`\n- Containers: `kill_css`, `css_populate_dir`, `existing_cgroup_probe`, `cgroup_attach_task`\n- TCP Sockets: `new_sock_created`, `set_state_ipv4`, `set_state_ipv6`, `close_sock_info`, `rtt_estimator`, `reset_tcp_counters`, `tcp_syn_timeout`, `tcp_reset`, `http_response`, `tcp_data`\n- UDP Sockets: `udp_new_socket`, `udp_destroy_socket`, `udp_stats`, `dns_packet`\n- NAT: `nf_conntrack_alter_reply`, `nf_nat_cleanup_conntrack`, `existing_conntrack_tuple` (plus consumes `set_state_*`, `close_sock_info`)\n- BPF Logging: `bpf_log`, `stack_trace`\n\nThis survey should enable splitting the monolithic program into loadable modules that share perf rings (by configuring each module’s `BPF_MAP_TYPE_PERF_EVENT_ARRAY` with the same FDs at load time) and reuse common maps as appropriate.\n\n"
  },
  {
    "path": "test/kernel/.gitignore",
    "content": "debian-*/\nubuntu-*/\ncentos-*/\n"
  },
  {
    "path": "test/kernel/README.md",
    "content": "# Kernel tests #\n\nOpenTelemetry eBPF needs to support multiple Linux kernel versions and distributions.  This directory contains\n- a test framework to run tests on various Linux distributions and kernel versions in virtual machines using Vagrant and VirtualBox.\n- tests to validate OpenTelemetry eBPF components against various Linux distributions and kernel versions.\n\nCurrent tests:\n- The **kernel header tests** are primarily used to verify that, for various Linux distributions and kernel versions, the kernel-collector agent has access to the kernel headers necessary to successfully build and load the eBPF code.  They can also be used as a basic kernel-collector sanity test, to validate that, for a given Linux distro/kernel version, the kernel-collector can successfully start up to the point where it is able to compile and load the eBPF code and send the reducer telemetry.\n\n- The **kernel_collector_test** is a self-contained test using the GoogleTest framework that runs a kernel-collector, executes some minimal process and network related workloads, and performs basic sanity checks, including\n  - the ability to compile and load the eBPF code.\n  - confirmation that eBPF probes instrumented by the OpenTelemetry eBPF kernel-collector gather telemetry as expected.  If, for example, certain expected telemetry from eBPF probes is missing, it could be due to Linux kernel changes that break existing eBPF probe code or require the use of different Linux kernel functions for the eBPF kprobes/kretprobes.\n\n\n## Kernel tests run in GitHub actions ##\n\nThe **kernel header tests** and **kernel_collector_test** are run on each Linux distro and kernel version specified in the `distros-and-kernels.sh` file for all OpenTelemetry eBPF PRs and merges to main.  See the [GitHub workflow yaml file](../../.github/workflows/build-and-test.yaml).\n\nThese tests can also be run manually, described below.\n\n\n## Pre-requisites ##\n\n- As described in [developing](../../docs/developing.md):\n  - `Docker` must be installed on the host\n  - local docker registry must be running\n  - `build-env` container must be available\n\n- As described in [devbox requirements](../../dev/devbox#requirements):\n  - `VirtualBox` and `Vagrant` must be installed on the host\n  - `vagrant-sshfs` and `vagrant-scp` Vagrant plugins must be installed\n\n\n## One-time set up to run kernel tests manually ##\n\n- Generate tests for a given Linux distro_name/distro_version pair by running:\n\n        ./bootstrap.sh <distro_name> <distro_version> <optional_kernel_version>\n\n  For example,\n\n        ./bootstrap.sh ubuntu focal64\n  or\n\n        ./bootstrap.sh ubuntu focal64 5.13.0-52-generic\n\n- Alternatively, run `./gen-tests.sh` to generate tests for a well known list of distros specified in the `distros-and-kernels.sh` file.\n\n\n## Kernel header tests ##\n\nMake sure the `kernel-collector` and `reducer` docker images have been built and published to the local registry by running the following in the `build-env` container:\n\n    ../build.sh kernel-collector-docker-registry reducer-docker-registry\n\n\n### Running kernel header tests manually ###\n\n- cd into the directory named `<distro_name>-<distro_version>[-kernel-version]`, generated by the set up step.\n\n- Run numbered scripts in order:\n  - `0-setup.sh`: starts the VM and gets it ready to run tests.  This step doesn't need to be repeated for multiple runs of the agent tests, even if the reducer is restarted.\n  - `1-apply-selinux-policy.sh`: determines if SELinux is enabled, and if so applies a SELinux security policy to allow eBPF operations required by the agent.\n  - `2-start-reducer.sh`: starts the reducer for agents to connect to.  This step doesn't need to be repeated for multiple runs of the agent tests.\n  - `3-fetch.sh`: ensures that kernel headers are not installed on the VM and that no cached kernel headers are available, then run the agent so it has to fetch kernel headers for the running kernel.\n  - `4-cached.sh`: runs the agent without any checks to existing or cached kernel headers.  When run after a successful `fetch` step, the VM will contain no pre-installed kernel headers and a valid cached kernel header that the agent can then retrieve.\n  - `5-pre-installed.sh`: removes any kernel headers previously cached by the agent and ensures kernel headers are installed on the VM, then runs agent so it can use pre-installed headers.\n  - `6-cleanup.sh`: tears down the VM and removes any temporary files.\n\n- For the `fetch`, `cached` and `pre-installed` steps, verify in the logs output from the kernel-collector container that the agent's binary is called with the expected kernel headers source, e.g.:\n\n    `exec /srv/kernel-collector --host-distro centos --kernel-headers-source [fetched | pre_fetched | pre_installed]`\n\n  Note that the scripts automatically verify that the agent connects to the reducer and reaches the \"Telemetry is flowing\" message.  If not, the script exits with a non-zero error status.  Once the script returns with a successful exit status of 0, you can continue on to run the next script in the sequence.\n\n\n### Automatically run all kernel header test steps for a given Linux distro and kernel version ###\n\n    ./run-test.sh --kernel-header-test <distro_name> <distro_version> <optional_kernel_version>\n\nFor example,\n\n    ./run-test.sh --kernel-header-test ubuntu focal64\n\nor\n\n    ./run-test.sh --kernel-header-test ubuntu focal64 5.13.0-52-generic\n\n\n### Automatically run all kernel header test steps for a set of well known distros and kernel versions ###\n\n    ./run-tests.sh --kernel-header-test\n\nThis will run each step of the kernel header tests on each Linux distro and kernel version specified in the `distros-and-kernels.sh` file and will output test results to a date-stamped directory in /tmp.\n\n\n### Kernel header test develop and test cycle ###\n\nA common scenarion is to make changes to the agent / kernel fetching scripts and re-run either the `fetch`, `cached` or `pre-installed` test steps.\n\nThat requires rebuilding the kernel-collector agent image and pushing it to the local docker registry, so it becomes available to the test VM.  Note that the `0-setup.sh`, `1-apply-selinux-policy.sh`, and `2-start-reducer.sh` steps don't need to be re-run if only the agent has changed.\n\nThe above can be accomplished within the `build-env` container by running `../build.sh` on the targets that build and push docker images to the local docker registry.  To list the available targets, run:\n\n    make help | grep 'docker-registry'\nor\n\n    ../build.sh --list-targets | grep 'docker-registry'\n\n\n## kernel_collector_test ##\n\nMake sure the `kernel-collector-test` docker image has been built and published to the local registry by running the following in the `build-env` container:\n\n    ../build.sh kernel-collector-test-docker-registry\n\n\n### Running kernel_collector_test manually ###\n\n- cd into the directory named `<distro_name>-<distro_version>[-kernel-version]`, generated by the set up step.\n\n- Run scripts in the following order:\n  - `0-setup.sh`: starts the VM and gets it ready to run tests.\n  - `1-apply-selinux-policy.sh`: determines if SELinux is enabled, and if so applies a SELinux security policy to allow eBPF operations required by the agent.\n  - `run-kernel-collector-test.sh`: runs the kernel_collector_test.\n  - `6-cleanup.sh`: tears down the VM and removes any temporary files.\n\n\n### Automatically run kernel_collector_test for a given Linux distro and kernel version ###\n\n    ./run-test.sh --kernel-collector-test <distro_name> <distro_version> <optional_kernel_version>\n\nFor example,\n\n    ./run-test.sh --kernel-collector-test ubuntu focal64\n\nor\n\n    ./run-test.sh --kernel-collector-test ubuntu focal64 5.13.0-52-generic\n\n\n### Automatically run all kernel header test steps for a set of well known distros and kernel versions ###\n\n    ./run-tests.sh --kernel-collector-test\n\nThis will run the kernel_collector_test on each Linux distro and kernel version specified in the `distros-and-kernels.sh` file and will output test results to a date-stamped directory in /tmp.\n\n\n## Running multiple tests ##\n\nThe `run-test.sh` and `run-tests.sh` scripts both accept arguments to run the specified tests:\n  - `--kernel-collector-test`\n  - `--kernel-header-test`\n  - `--all` to run all tests\n"
  },
  {
    "path": "test/kernel/bootstrap.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nsrc_path=\"${EBPF_NET_SRC_ROOT}/test/kernel/source\"\n\ndistro_name=\"$1\"; shift\ndistro_version=\"$1\"; shift\nkernel_version=\"\"\nif [[ \"$#\" -gt 0 ]]; then\n  kernel_version=\"$1\"; shift\nfi\n\nif [[ -z \"${distro_name}\" ]] || [[ -z \"${distro_version}\" ]]; then\n  echo \"usage: $0 distro_name distro_version [kernel_version]\"\nfi\n\ndistro_path=\"${distro_name}-${distro_version}\"\nif [[ -n \"${kernel_version}\" ]]; then\n  distro_path=\"${distro_path}-${kernel_version}\"\nfi\n\nmkdir -p \"${distro_path}\"\npushd \"${distro_path}\"\n\nif [[ \"${distro_name}\" == \"bento\" && \"${distro_version}\" == \"amazonlinux-2\" ]]\nthen\n  script_distro_name=\"centos\"\nelse\n  script_distro_name=\"${distro_name}\"\nfi\n\nsed_args=( \\\n  -e \"s/PLACEHOLDER_BOX_DISTRO_NAME/${distro_name}/g\"\n  -e \"s/PLACEHOLDER_DISTRO_NAME/${script_distro_name}/g\"\n  -e \"s/PLACEHOLDER_DISTRO_VERSION/${distro_version}/g\"\n  -e \"s/PLACEHOLDER_KERNEL_VERSION/${kernel_version}/g\"\n)\n\nsed \"${sed_args[@]}\" \"${src_path}/Vagrantfile\" > \"Vagrantfile\"\n\nif [ ! -L ./data ]\nthen\n  ln -s \"${src_path}/data\" \"data\"\nfi\n\nfor runner in ${src_path}/runners/*.sh; do\n  ln -sf \"${runner}\"\ndone\n\npopd\n"
  },
  {
    "path": "test/kernel/distros-and-kernels.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ndistros_and_kernels=(\n\"centos 7\"\n\"debian buster64\"\n\"debian bullseye64\"\n\"bento amazonlinux-2\"\n\"ubuntu bionic64\"\n\"ubuntu focal64\"\n\"ubuntu jammy64\"\n)\n\n"
  },
  {
    "path": "test/kernel/gen-tests.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nsource ${EBPF_NET_SRC_ROOT}/test/kernel/distros-and-kernels.sh\n\nfor ((i = 0; i < ${#distros_and_kernels[@]}; i++))\ndo\n  distro_and_kernel=\"${distros_and_kernels[$i]}\"\n  ${EBPF_NET_SRC_ROOT}/test/kernel/bootstrap.sh ${distro_and_kernel}\ndone\n"
  },
  {
    "path": "test/kernel/run-test.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n\nfunction print {\n  set +x\n  echo \"===================================== $1 =====================================\"\n  set -x\n}\n\nfunction print_help {\n  echo \"usage: $0 [--all|--kernel-collector-test|--kernel-header-test] <DISTRO> <VERSION> <optional KERNEL_VERSION>\"\n  echo\n  echo \"  --all: run all tests\"\n  echo \"  --kernel-collector-test: run the kernel_collector_test\"\n  echo \"  --kernel-header-test: run the kernel header tests\"\n}\n\nrun_kernel_collector_test=\"false\"\nrun_kernel_header_test=\"false\"\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --all)\n      run_kernel_collector_test=\"true\"\n      run_kernel_header_test=\"true\"\n      ;;\n\n    --kernel-collector-test)\n      run_kernel_collector_test=\"true\"\n      ;;\n\n    --kernel-header-test)\n      run_kernel_header_test=\"true\"\n      ;;\n\n    *)\n      args+=(\"${arg}\")\n      ;;\n  esac\ndone\n\nif [[ \"${run_kernel_collector_test}\" != \"true\" && \"${run_kernel_header_test}\" != \"true\" ]]\nthen\n  echo -e \"\\nNeed to specify test(s) to run.\\n\"\n  print_help\n  exit 1\nfi\n\ndistro=${args[0]}\nversion=${args[1]}\nkernel_version=${args[2]}\n\nif [[ \"${distro}\" == \"\" || \"${version}\" == \"\" ]]\nthen\n  echo -e \"\\nNeed to specify distro and version.\\n\"\n  print_help\n  exit 1\nfi\n\nset -x\n\nname=${distro}-${version}\n[ \"$kernel_version\" != \"\" ] && name=${name}-${kernel_version}\nprint \"Testing ${name}\"\n${EBPF_NET_SRC_ROOT}/test/kernel/bootstrap.sh ${distro} ${version} ${kernel_version}\n\ncd ${name}\nprint \"running 0-setup.sh\"\n./0-setup.sh\n\nprint \"running 1-apply-selinux-policy.sh\"\n./1-apply-selinux-policy.sh\n\nif [[ \"${run_kernel_collector_test}\" == \"true\" ]]\nthen\n  print \"running run-kernel-collector-test.sh\"\n  if ./run-kernel-collector-test.sh\n  then\n    print \"kernel_collector_test on ${name} succeeded\"\n  else\n    print \"kernel_collector_test on ${name} FAILED\"\n    test_failed=\"true\"\n  fi\nfi\n\nif [[ \"${run_kernel_header_test}\" == \"true\" ]]\nthen\n  print \"running 2-start-reducer.sh\"\n  ./2-start-reducer.sh\n\n  # Ubuntu Jammy cannot automatically fetch headers currently because kernel-collector with bitnami/minideb:buster\n  # base image does not support zstd compression\n  if [[ \"${version}\" == *\"jammy\"* ]]\n  then\n    print \"SKIPPING 3-fetch.sh and 4-cached.sh for ${name}\"\n  else\n    print \"running 3-fetch.sh\"\n    ./3-fetch.sh\n    print \"running 4-cached.sh\"\n    ./4-cached.sh\n  fi\n  print \"running 5-pre-installed.sh\"\n  ./5-pre-installed.sh\n\n  print \"Kernel header tests on ${name} succeeded\"\nfi\n\nprint \"running 6-cleanup.sh\"\n./6-cleanup.sh\n\nif [[ \"${test_failed}\" == \"true\" ]]\nthen\n  print \"Testing on ${name} FAILED\"\n  exit 1\nelse\n  print \"Testing on ${name} succeeded\"\nfi\n\n\n"
  },
  {
    "path": "test/kernel/run-tests.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nexport EBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nset -o pipefail\n\nfunction print_help {\n  echo \"usage: $0 [--all|--kernel-collector-test|--kernel-header-test]\"\n  echo\n  echo \"  --all run all tests on all distros/kernels\"\n  echo \"  --kernel-collector-test: run the kernel_collector_test on all distros/kernels\"\n  echo \"  --kernel-header-test: run the kernel header tests on all distros/kernels\"\n}\n\nwhile [[ \"$#\" -gt 0 ]]; do\n  arg=\"$1\"; shift\n  case \"${arg}\" in\n    --all)\n      test_args=\"--all\"\n      ;;\n\n    --kernel-collector-test)\n      test_args=\"--kernel-collector-test\"\n      ;;\n\n    --kernel-header-test)\n      test_args=\"--kernel-header-test\"\n      ;;\n\n    *)\n      print_help\n      exit 1\n      ;;\n  esac\ndone\n\nif [[ \"${test_args}\" == \"\" ]]\nthen\n  echo -e \"\\nNeed to specify test(s) to run.\\n\"\n  print_help\n  exit 1\nfi\n\nset -x\n\nsource ${EBPF_NET_SRC_ROOT}/test/kernel/distros-and-kernels.sh\n\ntest_dir=\"$(mktemp -d /tmp/kernel-tests-$(date +%Y-%m-%d-%H-%M)-XXX)\"\ncd ${test_dir}\n\ntouch ${test_dir}/summary.log\necho \"Saving test results in ${test_dir}\"\n\nnum_run=0\nnum_failed=0\n\nfor ((i = 0; i < ${#distros_and_kernels[@]}; i++))\ndo\n  num_run=$((num_run+1))\n  distro_and_kernel=\"${distros_and_kernels[$i]}\"\n  log_file=${test_dir}/\"$(sed 's/ /-/g' <<<$distro_and_kernel)\".log\n\n  echo \"Running tests (${test_args}) for \\\"${distro_and_kernel}\\\".\" | tee -a ${test_dir}/summary.log\n  ${EBPF_NET_SRC_ROOT}/test/kernel/run-test.sh ${test_args} ${distro_and_kernel} 2>&1 | tee ${log_file}\n\n  if [[ $? == 0 ]]\n  then\n    echo -e \"Test succeeded for \\\"${distro_and_kernel}\\\".\\n\" | tee -a ${test_dir}/summary.log\n  else\n    num_failed=$((num_failed+1))\n    echo -e \"Test FAILED for \\\"${distro_and_kernel}\\\".\\n\" | tee -a ${test_dir}/summary.log\n  fi\n\ndone\n\nset +x\n\necho -e \"Ran tests (${test_args}) on ${num_run} distros/kernels, with ${num_failed} failure(s).\\n\" | tee -a ${test_dir}/summary.log\n\necho \"Test results are in ${test_dir}\"\n\nif [[ ${num_failed} -gt 0 ]]\nthen\n    echo \"One or more test(s) failed.\"\n    exit 1\nfi\n\n"
  },
  {
    "path": "test/kernel/source/data/agent.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n\ncontainer_name=\"test-kernel-collector\"\n\nif [ \"$(docker ps -a -q -f name=\"${container_name}\")\" ]\nthen\n  docker stop \"${container_name}\"\n  docker rename \"${container_name}\" \"${container_name}-stopped\"\n  docker rm \"${container_name}-stopped\"\nfi\n\ndocker pull localhost:5000/kernel-collector\n\ndocker create \\\n  --name \"${container_name}\" \\\n  --env EBPF_NET_INTAKE_PORT=\"8000\" \\\n  --env EBPF_NET_INTAKE_HOST=\"127.0.0.1\" \\\n  --env EBPF_NET_AGENT_NAMESPACE=\"${EBPF_NET_AGENT_NAMESPACE}\" \\\n  --env EBPF_NET_AGENT_CLUSTER=\"${EBPF_NET_AGENT_CLUSTER}\" \\\n  --env EBPF_NET_AGENT_SERVICE=\"${EBPF_NET_AGENT_SERVICE}\" \\\n  --env EBPF_NET_AGENT_HOST=\"${EBPF_NET_AGENT_HOST}\" \\\n  --env EBPF_NET_AGENT_ZONE=\"${EBPF_NET_AGENT_ZONE}\" \\\n  --env EBPF_NET_KERNEL_HEADERS_AUTO_FETCH=\"true\" \\\n  --env EBPF_NET_HOST_DIR=\"/hostfs\" \\\n  --privileged \\\n  --pid host \\\n  --network host \\\n  --volume /sys/fs/cgroup:/hostfs/sys/fs/cgroup \\\n  --volume /usr/src:/hostfs/usr/src \\\n  --volume /lib/modules:/hostfs/lib/modules \\\n  --volume /etc:/hostfs/etc \\\n  --volume /var/cache:/hostfs/cache \\\n  --volume /var/run/docker.sock:/var/run/docker.sock \\\n  --entrypoint \"/srv/test-entrypoint.sh\" \\\n  localhost:5000/kernel-collector \\\n    --log-console \\\n    --debug\n\ndocker cp \".env\" \"${container_name}:/srv/.env\"\ndocker cp \"test-entrypoint.sh\" \"${container_name}:/srv/test-entrypoint.sh\"\n\ndocker start \"${container_name}\"\n\nstart_string=\"Telemetry is flowing\\!\"\n\nremaining_attempts=60\nwhile true\ndo\n  result=$(docker ps | grep \"${container_name}\") || true\n  if [[ \"${result}\" == \"\" ]]\n  then\n    docker ps -a\n    echo \"ERROR: kernel-collector container is not running!\"\n    exit 1\n  fi\n\n  result=$(docker logs \"${container_name}\" | grep \"${start_string}\") || true\n  if [[ \"${result}\" != \"\" ]]\n  then\n    break\n  fi\n\n  remaining_attempts=$(($remaining_attempts-1))\n  if [[ $remaining_attempts == 0 ]]\n  then\n    docker ps -a\n    docker inspect \"${container_name}\"\n    docker logs \"${container_name}\"\n    echo \"ERROR: kernel-collector did not start within the time expected!\"\n    exit 1\n  fi\n\n  sleep 5\ndone\n\necho \"kernel-collector is running!\"\n"
  },
  {
    "path": "test/kernel/source/data/centos-provision.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n\nuname -a\n\nyum list\n\nyum install -y \\\n  curl \\\n  openssl\n\nif ! grep 'ID=\"amzn\"' /etc/os-release\nthen\n  curl -fsSL https://get.docker.com/ | sh\nelse\n  # get.docker.com does not currently support amazon linux\n  yum install -y docker\nfi\n\nusermod -aG docker vagrant\nsystemctl enable docker\n\nexport RUNNING_KERNEL_VERSION=\"`uname -r`\"\nexport RUNNING_KERNEL_ARCH=\"${RUNNING_KERNEL_VERSION##*-}\"\n\nif [[ -n \"${KERNEL_VERSION}\" ]]; then\n  yum install -y kernel-\"${KERNEL_VERSION}\"\n\n  if [ \"${RUNNING_KERNEL_VERSION}\" != \"${KERNEL_VERSION}\" ]; then\n    yum autoremove -y kernel-\"${RUNNING_KERNEL_VERSION}\" || true\n    yum autoremove -y kernel-\"${RUNNING_KERNEL_ARCH}\" || true\n  fi\nelse\n  yum update -y kernel\nfi\n"
  },
  {
    "path": "test/kernel/source/data/debian-provision.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n\nuname -a\n\napt-get update -y\napt-get install -y --no-install-recommends \\\n  docker.io\n\nusermod -aG docker vagrant\nsystemctl enable docker\n\nexport RUNNING_KERNEL_VERSION=\"`uname -r`\"\nexport RUNNING_KERNEL_ARCH=\"${RUNNING_KERNEL_VERSION##*-}\"\n\nif [[ -n \"${KERNEL_VERSION}\" ]]; then\n  apt-get install -y --no-install-recommends \\\n    --allow-unauthenticated \\\n    --allow-downgrades \\\n    --allow-remove-essential \\\n    --allow-change-held-packages \\\n    \\\n    \"linux-image-${KERNEL_VERSION}\"\n\n  if [ \"${RUNNING_KERNEL_VERSION}\" != \"${KERNEL_VERSION}\" ]; then\n    apt-get purge --auto-remove --purge -y --no-install-recommends \\\n      --allow-unauthenticated \\\n      --allow-downgrades \\\n      --allow-remove-essential \\\n      --allow-change-held-packages \\\n      \\\n      \"linux-image-${RUNNING_KERNEL_VERSION}\" \\\n      \"linux-image-${RUNNING_KERNEL_ARCH}\" \\\n      || true\n  fi\nelse\n  apt-get upgrade --auto-remove --purge -y --no-install-recommends \\\n    --allow-unauthenticated \\\n    --allow-downgrades \\\n    --allow-remove-essential \\\n    --allow-change-held-packages \\\n    \\\n    \"linux-image-${RUNNING_KERNEL_ARCH}\"\nfi\n"
  },
  {
    "path": "test/kernel/source/data/env-provision.sh",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ncat >> .env <<EOF\nexport EBPF_NET_INTAKE_PORT=\"${EBPF_NET_INTAKE_PORT}\"\nexport EBPF_NET_INTAKE_HOST=\"${EBPF_NET_INTAKE_HOST}\"\nexport EBPF_NET_AGENT_NAMESPACE=\"${EBPF_NET_AGENT_NAMESPACE}\"\nexport EBPF_NET_AGENT_CLUSTER=\"${EBPF_NET_AGENT_CLUSTER}\"\nexport EBPF_NET_AGENT_SERVICE=\"${EBPF_NET_AGENT_SERVICE}\"\nexport EBPF_NET_AGENT_HOST=\"${EBPF_NET_AGENT_HOST}\"\nexport EBPF_NET_AGENT_ZONE=\"${EBPF_NET_AGENT_ZONE}\"\nexport EBPF_NET_KERNEL_HEADERS_AUTO_FETCH=\"${EBPF_NET_KERNEL_HEADERS_AUTO_FETCH}\"\nEOF\n"
  },
  {
    "path": "test/kernel/source/data/get-agent-shell.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n\ncontainer_name=\"test-kernel-collector\"\ndocker exec -it \"${container_name}\" bash\n"
  },
  {
    "path": "test/kernel/source/data/reducer.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xeE\n\ntrap 'catch $? $LINENO' ERR\ncatch() {\n  echo \"Error $1 occurred at $0 line $2\"\n  exit $1\n}\n\ndocker pull localhost:5000/reducer\n\ndocker run --detach --rm \\\n  --network=host \\\n  localhost:5000/reducer \\\n  --port 8000 \\\n  --prom 0.0.0.0:7000 \\\n  --partitions-per-shard 1 \\\n  --num-ingest-shards=1 \\\n  --num-matching-shards=1 \\\n  --num-aggregation-shards=1 \\\n  --enable-aws-enrichment \\\n  --enable-otlp-grpc-metrics \\\n  --log-console \\\n  --debug\n\nstart_string=\"Starting OpenTelemetry eBPF Reducer\"\n\nremaining_attempts=30\nwhile true\ndo\n  containerid=$(docker ps | grep \"reducer\" | awk '{print $1}') || true\n  if [[ \"${containerid}\" == \"\" ]]\n  then\n    docker ps\n    echo \"ERROR: Reducer container is not running!\"\n    exit 1\n  fi\n\n  result=$(docker logs ${containerid} | grep \"${start_string}\") || true\n  if [[ \"${result}\" != \"\" ]]\n  then\n    break\n  fi\n\n  remaining_attempts=$(($remaining_attempts-1))\n  if [[ $remaining_attempts == 0 ]]\n  then\n    docker ps\n    docker logs ${containerid}\n    echo \"ERROR: Reducer did not start within the time expected!\"\n    exit 1\n  fi\n\n  sleep 1\ndone\n\necho \"Reducer is running!\"\n"
  },
  {
    "path": "test/kernel/source/data/test-entrypoint.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n. .env\n/srv/entrypoint.sh \"$@\"\n"
  },
  {
    "path": "test/kernel/source/data/ubuntu-provision.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nset -xe\n\nuname -a\n\napt-get update -y\n\napt-get install -y --no-install-recommends \\\n  docker.io\n\nusermod -aG docker vagrant\nsystemctl enable docker\n\nexport RUNNING_KERNEL_VERSION=\"`uname -r`\"\nexport RUNNING_KERNEL_ARCH=\"${RUNNING_KERNEL_VERSION##*-}\"\n\nif [[ -n \"${KERNEL_VERSION}\" ]]; then\n  apt-get install -y --no-install-recommends \\\n    --allow-unauthenticated \\\n    --allow-downgrades \\\n    --allow-remove-essential \\\n    --allow-change-held-packages \\\n    \\\n    \"linux-image-${KERNEL_VERSION}\"\n\n  if [ \"${RUNNING_KERNEL_VERSION}\" != \"${KERNEL_VERSION}\" ]; then\n    apt-get purge --auto-remove --purge -y --no-install-recommends \\\n      --allow-unauthenticated \\\n      --allow-downgrades \\\n      --allow-remove-essential \\\n      --allow-change-held-packages \\\n      \\\n      \"linux-image-${RUNNING_KERNEL_VERSION}\" \\\n      \"linux-image-virtual\" \\\n      \"linux-virtual\" \\\n      || true\n  fi\nelse\n  apt-get upgrade --auto-remove --purge -y --no-install-recommends \\\n    --allow-unauthenticated \\\n    --allow-downgrades \\\n    --allow-remove-essential \\\n    --allow-change-held-packages \\\n    \\\n    \"linux-virtual\"\nfi\n"
  },
  {
    "path": "test/kernel/source/runners/0-setup.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\necho $1\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nset -x\n\n\nvagrant destroy -f || true\n[ -e .vagrant ] && rm -rf .vagrant\nvagrant box update\nvagrant up --provision\nif [[ $? != 0 ]]\nthen\n    echo \"vagrant up failed\"\n    vagrant destroy -f\n    exit 1\nfi\n\n\n# don't do this until after vagrant up so the script can check the result and cleanup and exit if it fails\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n\nvagrant up --no-provision\n\n# Confirm that the vagrant VM is running\nresult=$(vagrant status | grep ^default | grep running) || true\nif [[ \"${result}\" == \"\" ]]\nthen\n  echo \"ERROR: vagrant VM is not running!\"\n  vagrant status\n  exit 1\nelse\n  echo \"vagrant VM is running!\"\nfi\n\n# Sometimes vagrant ssh, as run by subsequent test runners, fails immediately after vagrant up, so wait until it works successfully.\nready_string=\"ready to ssh\"\nremaining_attempts=10\nwhile true\ndo\n  out=$(vagrant ssh -- -R \"5000:localhost:5000\" -- echo \"${ready_string}\") || true\n  if [[ \"${out}\" == \"${ready_string}\" ]]\n  then\n    break\n  fi\n\n  remaining_attempts=$(($remaining_attempts-1))\n  if [[ $remaining_attempts == 0 ]]\n  then\n    vagrant status\n    vagrant ssh-config\n    echo \"ERROR: vagrant VM is not accepting ssh requests!\"\n    exit 1\n  fi\n\n  sleep 1\ndone\n"
  },
  {
    "path": "test/kernel/source/runners/1-apply-selinux-policy.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n# on systems with SELinux enabled this script should be run before starting the\n# kernel-collector with 2-fetch.sh, 3-cached.sh or 4-pre-installed.sh\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant scp ${EBPF_NET_SRC_ROOT}/dev/selinux-bpf.sh /tmp\nvagrant ssh -- sudo /tmp/selinux-bpf.sh\n"
  },
  {
    "path": "test/kernel/source/runners/2-start-reducer.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant ssh -- -R \"5000:localhost:5000\" -- ./reducer.sh\n"
  },
  {
    "path": "test/kernel/source/runners/3-fetch.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant ssh -- -- ./uninstall-kernel-headers.sh\nvagrant ssh -- -- sudo rm -rf /var/cache/ebpf_net/kernel-headers || true\nvagrant ssh -- -R \"5000:localhost:5000\" -- ./agent.sh\n"
  },
  {
    "path": "test/kernel/source/runners/4-cached.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant ssh -- -R \"5000:localhost:5000\" -- ./agent.sh\n"
  },
  {
    "path": "test/kernel/source/runners/5-pre-installed.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant ssh -- -- sudo rm -rf /var/cache/ebpf_net/kernel-headers || true\nvagrant ssh -- -- ./install-kernel-headers.sh\nvagrant ssh -- -R \"5000:localhost:5000\" -- ./agent.sh\n"
  },
  {
    "path": "test/kernel/source/runners/6-cleanup.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\nset -x\n\nvagrant destroy -f || true\n[ -e .vagrant ] && rm -rf .vagrant\n"
  },
  {
    "path": "test/kernel/source/runners/run-kernel-collector-test.sh",
    "content": "#!/bin/bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\nEBPF_NET_SRC_ROOT=\"${EBPF_NET_SRC_ROOT:-$(git rev-parse --show-toplevel)}\"\nsource \"${EBPF_NET_SRC_ROOT}/dev/script/bash-error-lib.sh\"\n\nset -x\nvagrant ssh -- -R \"5000:localhost:5000\" -- ./test-kernel-collector.sh\n"
  },
  {
    "path": "tools/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\ninclude(tool)\n\nadd_tool_executable(\n  error_lookup\n  SRCS\n    error_lookup.cc\n  DEPS\n    libuv-shared\n)\n\nadd_library(wire_msg_to_json INTERFACE)\ntarget_link_libraries(\n  wire_msg_to_json\n  INTERFACE\n    file_ops\n    json\n    logging\n)\n\nadd_tool_executable(\n  bpf_wire_to_json\n  SRCS\n    bpf_wire_to_json.cc\n  DEPS\n    render_ebpf_net_agent_internal\n    wire_msg_to_json\n)\n\nadd_tool_executable(\n  intake_wire_to_json\n  SRCS\n    intake_wire_to_json.cc\n  DEPS\n    render_pipeline\n    wire_msg_to_json\n)\n\nadd_tool_executable(\n  matching_wire_to_json\n  SRCS\n    matching_wire_to_json.cc\n  DEPS\n    render_pipeline\n    wire_msg_to_json\n)\n\nadd_tool_executable(\n  aggregation_wire_to_json\n  SRCS\n    aggregation_wire_to_json.cc\n  DEPS\n    render_pipeline\n    wire_msg_to_json\n)\n"
  },
  {
    "path": "tools/aggregation_wire_to_json.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <generated/ebpf_net/aggregation/meta.h>\n\n#include <util/json_converter.h>\n\nint main()\n{\n  auto in = FileDescriptor::std_in();\n  return json_converter::print_from_wire_format<ebpf_net::aggregation_metadata>(in, std::cout);\n}\n"
  },
  {
    "path": "tools/bpf_wire_to_json.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <generated/ebpf_net/agent_internal/meta.h>\n\n#include <util/json_converter.h>\n\nint main()\n{\n  auto in = FileDescriptor::std_in();\n  return json_converter::print_from_wire_format<ebpf_net::agent_internal_metadata>(in, std::cout);\n}\n"
  },
  {
    "path": "tools/error_lookup.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Error code lookup tool\n *\n * This is a handy utility that can be used to error messages corresponding\n * to a given error code.\n *\n * The lookup is performed across a variety of libraries used internally.\n */\n\n#include <cerrno>\n#include <cstdlib>\n#include <cstring>\n#include <iostream>\n\n#include <uv.h>\n\nint main(int argc, char **argv)\n{\n  if (argc < 2) {\n    std::cerr << \"usage: error_lookup errno_1 [errno_2 [... [errno_N]]]\" << std::endl;\n    return EXIT_FAILURE;\n  }\n\n  for (int i = 1; i < argc; ++i) {\n    auto const code = std::atoi(argv[i]);\n    std::cout << \"code: \" << code << '\\n';\n\n    std::cout << \"errno: \" << std::strerror(code) << '\\n';\n    std::cout << \"libuv: \" << uv_err_name(code) << \" - \" << uv_strerror(code) << '\\n';\n\n    // add any other libraries we use here\n\n    std::cout << std::endl;\n  }\n\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "tools/intake_wire_to_json.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <generated/ebpf_net/ingest/meta.h>\n\n#include <util/json_converter.h>\n\nint main()\n{\n  auto in = FileDescriptor::std_in();\n  return json_converter::print_from_wire_format<ebpf_net::ingest_metadata>(in, std::cout);\n}\n"
  },
  {
    "path": "tools/matching_wire_to_json.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <generated/ebpf_net/matching/meta.h>\n\n#include <util/json_converter.h>\n\nint main()\n{\n  auto in = FileDescriptor::std_in();\n  return json_converter::print_from_wire_format<ebpf_net::matching_metadata>(in, std::cout);\n}\n"
  },
  {
    "path": "util/CMakeLists.txt",
    "content": "# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\noption(ENABLE_CODE_TIMING \"Enable code timing\" ON)\nmessage(STATUS \"time ENABLE_CODE_TIMING is ${ENABLE_CODE_TIMING}\")\n\nadd_library(\n  lz4_decompressor\n  STATIC\n    lz4_decompressor.cc\n)\ntarget_link_libraries(\n  lz4_decompressor\n    lz4\n)\n\nadd_library(\n  random\n  STATIC\n    random.cc\n)\nadd_unit_test(random LIBS random)\nadd_unit_test(string_view)\n\nadd_library(\n  time\n  STATIC\n    code_timing.cc\n    time.cc\n)\ntarget_link_libraries(\n  time\n    stdc++fs\n)\nadd_unit_test(time LIBS time)\n\nadd_library(\n  proc_ops\n  STATIC\n    proc_ops.cc\n)\ntarget_link_libraries(\n  proc_ops\n    absl::flat_hash_map\n)\nadd_unit_test(proc_ops LIBS proc_ops)\n\nadd_library(\n  system_ops\n  STATIC\n    system_ops.cc\n)\n\nadd_library(\n  file_ops\n  STATIC\n    file_ops.cc\n)\ntarget_link_libraries(\n  file_ops\n    fastpass_util\n    logging\n    stdc++fs\n)\n\nadd_library(\n  base64\n  STATIC\n    base64.cc\n)\ntarget_link_libraries(\n  base64\n    OpenSSL::Crypto\n)\nadd_unit_test(base64 LIBS base64)\n\nadd_library(\n  environment_variables\n  STATIC\n    environment_variables.cc\n)\ntarget_link_libraries(\n  environment_variables\n    logging\n)\n\nadd_library(\n  signal_handler\n  STATIC\n    signal_handler.cc\n)\ntarget_link_libraries(\n  signal_handler\n    logging\n    libuv-interface\n)\n\nadd_library(\n  json\n  INTERFACE\n)\ntarget_include_directories(\n  json\n  INTERFACE\n    \"${CMAKE_SOURCE_DIR}/ext/json/include\"\n)\nadd_unit_test(json LIBS json)\n\nadd_library(\n  restful\n  STATIC\n    restful.cc\n)\ntarget_link_libraries(\n  restful\n    curl-cpp\n    logging\n)\n\nadd_library(\n  resource_usage_reporter\n  STATIC\n    resource_usage_reporter.cc\n)\ntarget_link_libraries(\n  resource_usage_reporter\n    render_ebpf_net_artifacts\n    scheduling\n    system_ops\n    logging\n)\n\nadd_library(\n  curl_engine\n  STATIC\n    curl_engine.cc\n)\n\ntarget_link_libraries(\n  curl_engine\n  PUBLIC\n    libuv-interface\n    element_queue_writer\n    CURL::libcurl\n)\n\nadd_library(\n  logging\n  STATIC\n    log.cc\n    log_whitelist.cc\n)\n\nadd_library(\n  versions\n  STATIC\n    version.cc\n)\n# include build dir for generated util/version_config.h\n# and source dir for project headers if needed\n# keep PUBLIC so dependents get the include for generated header\n# (CMAKE_BINARY_DIR is already a global include but be explicit here)\ntarget_include_directories(\n  versions\n  PUBLIC\n    ${CMAKE_BINARY_DIR}\n    ${PROJECT_SOURCE_DIR}\n)\n\ntarget_link_libraries(\n  logging\n    environment_variables\n    spdlog\n)\n\nadd_library(\n  agent_id\n  STATIC\n    agent_id.cc\n)\ntarget_link_libraries(\n  agent_id\n    curl_engine\n)\n\nadd_library(\n  args_parser\n  STATIC\n    args_parser.cc\n)\ntarget_link_libraries(\n  args_parser\n    environment_variables\n    logging\n)\n\nadd_library(\n  element_queue\n  STATIC\n    element_queue.c\n)\n\nadd_library(\n  fastpass_util\n  STATIC\n    perf_ring.c\n    pool_allocator.c\n    lookup3.c\n    boot_time.c\n)\ntarget_compile_options(\n  fastpass_util\n  PRIVATE\n    -fPIC\n)\n\nadd_library(fixed_hash INTERFACE)\ntarget_link_libraries(\n  fixed_hash\n  INTERFACE\n    fastpass_util\n    absl::flat_hash_map\n)\nadd_unit_test(fixed_hash LIBS fixed_hash)\n\nadd_library(\n  element_queue_writer\n  STATIC\n    element_queue_writer.cc\n)\ntarget_link_libraries(\n  element_queue_writer\n    logging\n    logging\n    element_queue\n)\n\nadd_library(\n  tdigest\n  STATIC\n    tdigest.cc\n)\nadd_unit_test(tdigest LIBS tdigest)\n\nadd_library(\n  ip_address\n  STATIC\n    ip_address.cc\n)\ntarget_link_libraries(\n  ip_address\n    logging\n    render_ebpf_net_artifacts\n)\nadd_unit_test(ip_address LIBS ip_address absl::str_format)\n\nadd_library(\n  error_handling\n  STATIC\n    error_handling.cc\n)\ntarget_link_libraries(\n  error_handling\n    absl::strings\n    absl::symbolize\n    element_queue_writer\n    logging\n)\n\nadd_library(\n  uv_helpers\n  STATIC\n    uv_helpers.cc\n)\ntarget_link_libraries(\n  uv_helpers\n    logging\n    libuv-static\n)\n\nadd_library(\n  aws_instance_metadata\n  STATIC\n    aws_instance_metadata.cc\n)\ntarget_link_libraries(\n  aws_instance_metadata\n    restful\n    json\n    spdlog\n)\n\nadd_library(\n  gcp_instance_metadata\n  STATIC\n    gcp_instance_metadata.cc\n)\ntarget_link_libraries(\n  gcp_instance_metadata\n    restful\n    json\n    ip_address\n    spdlog\n)\n\nadd_library(\n  docker_host_config_metadata\n  STATIC\n    docker_host_config_metadata.cc\n)\ntarget_link_libraries(\n  docker_host_config_metadata\n    json\n)\n\nadd_library(\n  k8s_metadata\n  STATIC\n    k8s_metadata.cc\n)\ntarget_link_libraries(\n  k8s_metadata\n    json\n)\n\nadd_library(\n  nomad_metadata\n  STATIC\n    nomad_metadata.cc\n)\ntarget_link_libraries(\n  nomad_metadata\n    environment_variables\n    logging\n    json\n    absl::strings\n)\n\nadd_library(\n  cgroup_parser\n  STATIC\n    cgroup_parser.cc\n)\ntarget_link_libraries(\n  cgroup_parser\n    logging\n)\n\nadd_unit_test(log_modifiers LIBS logging)\nadd_unit_test(expected)\nadd_unit_test(enum)\nadd_unit_test(meta LIBS render_ebpf_net_artifacts llvm logging)\nadd_unit_test(lookup3_hasher LIBS fastpass_util)\nadd_unit_test(jitter)\nadd_unit_test(bits)\nadd_unit_test(counter)\nadd_unit_test(gauge)\nadd_unit_test(cgroup_parser LIBS cgroup_parser logging)\nadd_unit_test(defer LIBS logging)\n"
  },
  {
    "path": "util/LRU.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <ccan/list/list.h>\n#include <cstddef>\n#include <memory>\n#include <platform/platform.h>\n#include <util/fixed_hash.h>\n\ntemplate <\n    class Key,\n    class T,\n    std::size_t ELEM_POOL_SZ,\n    class Hash = std::hash<Key>,\n    class KeyEqual = std::equal_to<Key>,\n    class Allocator = std::allocator<T>>\nclass LRU {\npublic:\n  using key_type = Key;\n  struct value_type {\n    template <typename K, typename... Args>\n    value_type(K &&k, Args &&... args) : key(std::forward<K>(k)), node{}, value(std::forward<Args>(args)...)\n    {}\n    key_type key;\n    struct list_node node;\n    T value;\n  };\n  using map_type = FixedHash<key_type, value_type, ELEM_POOL_SZ, Hash, KeyEqual, Allocator>;\n  using size_type = typename map_type::size_type;\n\n  static constexpr typename map_type::index_type invalid = map_type::invalid;\n\npublic:\n  /**\n   * c'tor\n   */\n  LRU()\n  {\n    /* initialize the head */\n    list_head_init(&lru_);\n  }\n\n  bool empty() const { return map_.empty(); }\n\n  bool full() const { return map_.full(); }\n\n  size_type size() const { return map_.size(); }\n\n  size_type capacity() const { return map_.capacity(); }\n\n  template <typename K> bool contains(const K &key) const { return map_.contains(key); }\n\n  /**\n   * Finds the given element.\n   * @returns: pointer to the element, or nullptr if not found\n   */\n  template <typename K> T *find(const K &key)\n  {\n    auto pos = map_.find(key);\n    if (pos.index == invalid)\n      return nullptr;\n\n    /* used; update LRU */\n    list_del_from(&lru_, &pos.entry->node);\n    list_add_tail(&lru_, &pos.entry->node);\n    return &pos.entry->value;\n  }\n\n  /**\n   * Inserts the value into the LRU with given key.\n   * @returns the inserted value, or nullptr if the value exists\n   */\n  template <typename K, typename... Args> T *insert(K &&key, Args &&... args)\n  {\n    if (full()) {\n      /* evict an entry */\n      struct value_type *entry = list_pop(&lru_, struct value_type, node);\n      map_.erase(entry->key);\n    }\n\n    auto pos = map_.insert(key, value_type{key, std::forward<Args>(args)...});\n\n    if (pos.index == map_.invalid)\n      return nullptr;\n\n    list_add_tail(&lru_, &pos.entry->node);\n    return &pos.entry->value;\n  }\n\n  /**\n   * Removes the given element if it exists in the LRU\n   */\n  template <typename K> void remove(const K &key)\n  {\n    auto pos = map_.find(key);\n    if (pos.index == invalid)\n      return;\n\n    list_del(&pos.entry->node);\n    map_.erase(key);\n    return;\n  }\n\nprivate:\n  /* map sk -> table_index */\n  map_type map_;\n\n  struct list_head lru_;\n};\n"
  },
  {
    "path": "util/agent_id.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"agent_id.h\"\n\n#include <random>\n\nstd::string gen_agent_id()\n{\n  static const char chars[] = {\"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"};\n  const size_t idlen = 32;\n\n  std::random_device rd;                                      // seed with non-deterministic random values from the system\n  std::uniform_int_distribution<> dist(0, sizeof(chars) - 2); // range inclusive + zero terminator\n\n  std::string id{\"FAID\"};\n\n  for (size_t i = 0; i < idlen; i++) {\n    id.push_back(chars[dist(rd)]);\n  }\n\n  return id;\n}\n"
  },
  {
    "path": "util/agent_id.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n\n// Generates random agent ID in form of FAID...\nstd::string gen_agent_id();\n"
  },
  {
    "path": "util/args_parser.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/args_parser.h>\n\n#include <util/environment_variables.h>\n#include <util/log.h>\n#include <util/log_whitelist.h>\n\n#include <spdlog/spdlog.h>\n\n#include <iostream>\n\nnamespace cli {\n\nclass LogLevelsHandler : public ArgsParser::Handler {\npublic:\n  LogLevelsHandler(cli::ArgsParser &parser)\n      : no_log_file_(parser.add_flag(\"no-log-file\", \"Do not log to a log file.\")),\n        log_console_(parser.add_flag(\"log-console\", \"Log to console.\")),\n        trace_(parser.add_flag(\"trace\", \"Sets minimum log level to `trace` (very verbose)\")),\n        debug_(parser.add_flag(\"debug\", \"Sets minimum log level to `debug` (very verbose)\")),\n        info_(parser.add_flag(\"info\", \"Sets minimum log level to `info`\")),\n        warn_(parser.add_flag(\"warning\", \"Sets minimum log level to `warning`\")),\n        error_(parser.add_flag(\"error\", \"Sets minimum log level to `error`\")),\n        crit_(parser.add_flag(\"critical\", \"Sets minimum log level to `critical`\")),\n        whitelist_all_(parser.add_flag(\"log-whitelist-all\", \"enable all whitelists\"))\n  {}\n\n  void handle() override\n  {\n    auto const log_file = std::string(LOG::log_file_path());\n    LOG::init(*log_console_, no_log_file_ ? nullptr : &log_file);\n\n    if (trace_) {\n      spdlog::set_level(spdlog::level::trace);\n    } else if (debug_) {\n      spdlog::set_level(spdlog::level::debug);\n    } else if (info_) {\n      spdlog::set_level(spdlog::level::info);\n    } else if (warn_) {\n      spdlog::set_level(spdlog::level::warn);\n    } else if (error_) {\n      spdlog::set_level(spdlog::level::err);\n    } else if (crit_) {\n      spdlog::set_level(spdlog::level::critical);\n    }\n\n    if (whitelist_all_) {\n      log_whitelist_all_globally();\n    }\n  }\n\nprivate:\n  cli::ArgsParser::FlagProxy no_log_file_;\n  cli::ArgsParser::FlagProxy log_console_;\n  cli::ArgsParser::FlagProxy trace_;\n  cli::ArgsParser::FlagProxy debug_;\n  cli::ArgsParser::FlagProxy info_;\n  cli::ArgsParser::FlagProxy warn_;\n  cli::ArgsParser::FlagProxy error_;\n  cli::ArgsParser::FlagProxy crit_;\n  cli::ArgsParser::FlagProxy whitelist_all_;\n};\n\nArgsParser::ArgsParser(std::string header, std::string footer, Flags flags) : parser_(std::move(header), std::move(footer))\n{\n  if (flags & Flags::log_levels) {\n    handlers_.emplace_back(std::make_unique<LogLevelsHandler>(*this));\n  }\n}\n\nArgsParser::ArgsParser(std::string header, Flags flags) : ArgsParser(std::move(header), \"\", flags) {}\n\nArgsParser::FlagProxy::FlagProxy(\n    args::ArgumentParser &parser, std::string const &name, std::string const &description, bool default_value)\n    : flag_(parser, name, description, {name}), default_(default_value)\n{}\n\nbool ArgsParser::FlagProxy::given() const\n{\n  return flag_.Matched();\n}\n\nArgsParser::FlagProxy::operator bool() const\n{\n  return given() ? flag_ : default_;\n}\n\nArgsParser::FlagProxy ArgsParser::add_flag(std::string const &name, std::string const &description, bool default_value)\n{\n  return FlagProxy(parser_, name, description, default_value);\n}\n\nArgsParser::FlagProxy\nArgsParser::add_env_flag(std::string const &name, std::string const &description, char const *env_var, bool default_value)\n{\n  assert(env_var);\n\n  if (auto const value = try_get_env_var(env_var); !value.empty()) {\n    default_value = value == \"true\" || value == \"True\" || value == \"TRUE\" || value == \"yes\" || value == \"Yes\" ||\n                    value == \"YES\" || value == \"1\";\n  }\n\n  return add_flag(name, description, default_value);\n}\n\nExpected<bool, int> ArgsParser::process(int argc, char **argv)\n{\n  try {\n    parser_.ParseCLI(argc, argv);\n  } catch (args::Help const &) {\n    std::cout << parser_.Help();\n    return {unexpected, 0};\n  } catch (args::Error const &e) {\n    std::cerr << e.what() << std::endl;\n    std::cerr << parser_.Help();\n    return {unexpected, -9};\n  }\n\n  for (auto &handler : handlers_) {\n    handler->handle();\n  }\n\n  return true;\n}\n\nvoid ArgsParser::split_arguments(const std::string &argliststr, std::list<std::string> &argument_list)\n{\n  // Tokenize string\n  const char *delims = \", |;\\t\\r\\n\";\n  std::size_t start = argliststr.find_first_not_of(delims), end = 0;\n  while ((end = argliststr.find_first_of(delims, start)) != std::string::npos) {\n    argument_list.push_back(argliststr.substr(start, end - start));\n    start = argliststr.find_first_not_of(delims, end);\n  }\n  if (start != std::string::npos) {\n    argument_list.push_back(argliststr.substr(start));\n  }\n}\n\n} // namespace cli\n"
  },
  {
    "path": "util/args_parser.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n/**\n * A class to handle command line parameters.\n */\n\n#include <util/debug.h>\n#include <util/expected.h>\n\n#include <args.hxx>\n\n#include <list>\n#include <memory>\n#include <string>\n#include <vector>\n\nnamespace cli {\n\nclass ArgsParser {\npublic:\n  /**\n   * Bitmasks for flags that can be automatically handled by this arguments parser.\n   *\n   * NOTE: use unique powers of two for enum values.\n   */\n  enum Flags {\n    none = 0,\n    /**\n     * Flags for handling minimum level for logging.\n     */\n    log_levels = 1\n  };\n\n  static constexpr Flags default_flags = Flags::log_levels;\n\n  /**\n   * Constructs an args parser with given header and footer.\n   *\n   * flags: a bitwise or'ed set of flags to be automatically handled by the parser.\n   */\n  ArgsParser(std::string header, std::string footer, Flags flags = default_flags);\n\n  /**\n   * Constructs an args parser with given header.\n   *\n   * flags: a bitwise or'ed set of flags to be automatically handled by the parser.\n   */\n  ArgsParser(std::string header, Flags flags = default_flags);\n\n  class FlagProxy {\n  public:\n    FlagProxy(args::ArgumentParser &parser, std::string const &name, std::string const &description, bool default_value);\n\n    bool given() const;\n\n    explicit operator bool() const;\n    bool operator!() const { return !static_cast<bool>(*this); }\n    bool operator*() const { return static_cast<bool>(*this); }\n\n  private:\n    args::Flag flag_;\n    bool default_;\n  };\n\n  /**\n   * Adds a boolean flag.\n   */\n  FlagProxy add_flag(std::string const &name, std::string const &description, bool default_value = false);\n\n  /**\n   * Adds a boolean flag that defaults to a given environment variable's value, if present.\n   *\n   * Order of priority is:\n   *  1. command-line argument\n   *  2. environment variable's value\n   *  3. default value\n   */\n  FlagProxy\n  add_env_flag(std::string const &name, std::string const &description, char const *env_var, bool default_value = false);\n\n  template <typename T> class ArgProxy {\n    static constexpr bool needs_proxy = std::is_enum_v<T>;\n    using proxy_ref = std::conditional_t<needs_proxy, T, T &>;\n\n  public:\n    ArgProxy(args::ArgumentParser &parser, std::string const &name, std::string const &description, T const &default_value);\n\n    bool given() const;\n\n    explicit operator bool() const { return given(); }\n\n    proxy_ref Get();\n    proxy_ref operator*() { return Get(); }\n\n  private:\n    using arg_type = std::conditional_t<std::is_enum_v<T>, std::string, T>;\n    args::ValueFlag<arg_type> arg_;\n    bool default_;\n  };\n\n  /**\n   * Adds a new command line flag of type `T` with given argument name,\n   * description and default value.\n   *\n   * If null-terminated string `env_var` is not null and an environment\n   * variable with that name exists, its value overrides `default_value`.\n   *\n   * If the value of the environment variable can't be converted to `T`,\n   * `std::invalid_argument` is thrown.\n   */\n  template <typename T>\n  ArgProxy<T>\n  add_arg(std::string const &name, std::string const &description, char const *env_var = nullptr, T default_value = {});\n\n  /**\n   * Parses command line arguments and handles pre-defined flags.\n   *\n   * On error, returns an exit code.\n   */\n  Expected<bool, int> process(int argc, char **argv);\n\n  /**\n   * Compatibility with args::ArgumentsParser.\n   */\n  args::ArgumentParser *operator->() { return &parser_; }\n  args::ArgumentParser &operator*() { return parser_; }\n\n  class Handler {\n  public:\n    virtual ~Handler() {}\n\n    virtual void handle() {}\n  };\n\n  /**\n   * Add an args parser handler\n   */\n  template <class HandlerType, typename... Args> HandlerType &new_handler(Args &&... args);\n\n  /**\n   * Utility function for splitting argument lists\n   */\n  static void split_arguments(const std::string &argliststr, std::list<std::string> &argument_list);\n\nprivate:\n  args::ArgumentParser parser_;\n  std::vector<std::unique_ptr<Handler>> handlers_;\n};\n\n} // namespace cli\n\n#include <util/args_parser.inl>\n"
  },
  {
    "path": "util/args_parser.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/enum.h>\n#include <util/string.h>\n\n#include <spdlog/fmt/fmt.h>\n\nnamespace cli {\n\ntemplate <typename T>\nArgsParser::ArgProxy<T>::ArgProxy(\n    args::ArgumentParser &parser, std::string const &name, std::string const &description, T const &default_value)\n    : arg_(parser, name, description, {name}, [&]() -> std::conditional_t<std::is_enum_v<T>, std::string, T const &> {\n        if constexpr (std::is_enum_v<T>) {\n          if constexpr (enum_traits<T>::has_default_value) {\n            return std::string(to_string(default_value));\n          } else {\n            return {};\n          }\n        } else {\n          return default_value;\n        }\n      }())\n{}\n\ntemplate <typename T> bool ArgsParser::ArgProxy<T>::given() const\n{\n  return arg_.Matched();\n}\n\ntemplate <typename T> typename ArgsParser::ArgProxy<T>::proxy_ref ArgsParser::ArgProxy<T>::Get()\n{\n  if constexpr (std::is_enum_v<T>) {\n    auto const &arg = arg_.Get();\n    if constexpr (enum_traits<T>::has_default_value) {\n      if (arg.empty()) {\n        return enum_traits<T>::default_value;\n      }\n    }\n    T value;\n    if (!enum_from_string(arg, value)) {\n      throw std::invalid_argument(fmt::format(\"invalid value given for enumeration: '{}'\", arg));\n    }\n    return value;\n  } else {\n    return arg_.Get();\n  }\n}\n\ntemplate <typename T>\nArgsParser::ArgProxy<T>\nArgsParser::add_arg(std::string const &name, std::string const &description, char const *env_var, T default_value)\n{\n  if (env_var) {\n    if (auto const value = std::getenv(env_var); value && !from_string(value, default_value) && *value) {\n      throw std::invalid_argument(fmt::format(\n          \"unable to convert environment variable '{}' to the type\"\n          \" of command line argument '{}' (value='{}')\",\n          env_var,\n          name,\n          value));\n    }\n  }\n\n  return ArgProxy(parser_, name, description, default_value);\n}\n\ntemplate <class HandlerType, typename... Args> HandlerType &ArgsParser::new_handler(Args &&... args)\n{\n  handlers_.emplace_back(std::make_unique<HandlerType>(*this, std::forward<Args>(args)...));\n  return static_cast<HandlerType &>(*handlers_.back().get());\n}\n\n} // namespace cli\n"
  },
  {
    "path": "util/aws_instance_metadata.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/aws_instance_metadata.h>\n\n#include <common/cloud_platform.h>\n#include <config.h>\n#include <platform/platform.h>\n#include <util/json.h>\n#include <util/log.h>\n\n#include <curlpp/Easy.hpp>\n#include <curlpp/Multi.hpp>\n#include <curlpp/Options.hpp>\n#include <curlpp/Types.hpp>\n\n#include <sys/select.h>\n\n#include <functional>\n#include <sstream>\n#include <stdexcept>\n#include <list>\n\n#define METADATA_PREFIX_V1 \"http://169.254.169.254/2016-09-02/meta-data/\"\n#define METADATA_PREFIX_V2 \"http://169.254.169.254/latest/meta-data/\"\n\n#define DYNAMIC_METADATA_PREFIX_V1 \"http://169.254.169.254/2016-09-02/dynamic/\"\n#define DYNAMIC_METADATA_PREFIX_V2 \"http://169.254.169.254/latest/dynamic/\"\n\n#define IMDSV2_TOKEN_URL \"http://169.254.169.254/latest/api/token\"\n#define IMDSV2_TOKEN_TTL \"21600\" /* seconds */\n#define MAX_LEN 4096\n#define PAUSE_USEC_WHEN_NOT_READY (1000)\n\nclass Fetch {\npublic:\n  /* c'tor */\n  Fetch(const std::string &name, const char *url, const std::string &imdsv2_token = \"\");\n\n  /* write functor callback from curlpp */\n  size_t write(char *buf, size_t size, size_t nmemb);\n\n  std::string key;\n  std::string value;\n  curlpp::Easy easy;\n  int retcode;\n};\n\nstd::string_view AwsMetadataValue::value() const\n{\n  if (retcode_ != 0) {\n    return {};\n  }\n  return value_;\n}\n\nAwsNetworkInterface::AwsNetworkInterface(std::string interface_id) : interface_id_(interface_id) {}\n\nvoid AwsNetworkInterface::set_info(FetchResult const &info)\n{\n  std::string vpc_id_key = interface_id_ + \"_vpc_id\";\n  if (info.at(vpc_id_key).valid()) {\n    auto vpc = info.at(vpc_id_key).value();\n    // remove the slash at the end of the vpc-id, if one was given\n    if ((vpc.length() > 0) && (vpc.back() == '/')) {\n      vpc.remove_suffix(1);\n    }\n    vpc_id_ = vpc;\n  }\n\n  // we can't use set_multiline_info here since private_ipv4 is a set (and not a\n  // vector)\n  if (info.at(interface_id_ + \"_private_ipv4\").valid()) {\n    std::istringstream stream(std::string(info.at(interface_id_ + \"_private_ipv4\").value()));\n    std::string line;\n    while (std::getline(stream, line)) {\n      private_ipv4s_.emplace(line);\n    }\n  }\n  set_multiline_info(info.at(interface_id_ + \"_public_ipv4\"), public_ipv4s_);\n  set_multiline_info(info.at(interface_id_ + \"_ipv6\"), ipv6s_);\n}\n\nvoid AwsNetworkInterface::set_mapped_ipv4s(FetchResult const &info)\n{\n  for (auto public_ipv4 : public_ipv4s_) {\n    std::string mapped_ipv4_key = interface_id_ + \"_\" + public_ipv4;\n    if (info.at(mapped_ipv4_key).valid()) {\n      mapped_ipv4s_[public_ipv4] = info.at(mapped_ipv4_key).value();\n      // remove from our set of private_ipv4s\n      private_ipv4s_.erase(std::string(info.at(mapped_ipv4_key).value()));\n    }\n  }\n}\n\nvoid AwsNetworkInterface::set_multiline_info(AwsMetadataValue const &info, std::vector<std::string> &dest)\n{\n  if (info.valid()) {\n    std::istringstream stream(std::string(info.value()));\n    std::string line;\n    while (std::getline(stream, line)) {\n      dest.emplace_back(line);\n    }\n  }\n}\n\nvoid AwsNetworkInterface::print_interface() const\n{\n  LOG::debug_in(CloudPlatform::aws, \"Interface Id: {}\", interface_id_);\n  LOG::debug_in(CloudPlatform::aws, \"\\tVPC Id: {}\", vpc_id_);\n\n  LOG::debug_in(CloudPlatform::aws, \"\\tPrivate IPV4s:\");\n  for (auto private_ipv4 : private_ipv4s_) {\n    LOG::debug_in(CloudPlatform::aws, \"\\t\\t{}\", private_ipv4);\n  }\n\n  LOG::debug_in(CloudPlatform::aws, \"\\tPublic IPV4s -> Private IPV4s:\");\n  for (auto mapped_ipv4 : mapped_ipv4s_) {\n    LOG::debug_in(CloudPlatform::aws, \"\\t\\t{} -> {}\", mapped_ipv4.first, mapped_ipv4.second);\n  }\n\n  LOG::debug_in(CloudPlatform::aws, \"\\tIPV6s: \");\n  for (auto ipv6 : ipv6s_) {\n    LOG::debug_in(CloudPlatform::aws, \"\\t\\t{}\", ipv6);\n  }\n}\n\nAwsMetadata::AwsMetadata(std::chrono::microseconds timeout) : timeout_(timeout) {}\n\nAwsMetadata::FetchResult AwsMetadata::fetch(std::map<std::string, std::string> keys_to_endpoints,\n                                            const std::string &imdsv2_token)\n{\n  std::vector<Fetch *> fetches;\n  for (auto key_to_endpoint : keys_to_endpoints) {\n    fetches.push_back(new Fetch(key_to_endpoint.first, key_to_endpoint.second.c_str(), imdsv2_token));\n  }\n\n  /* add fetches to curlpp::Multi for parallel fetch */\n  curlpp::Multi multi;\n  for (auto fetch : fetches)\n    multi.add(&fetch->easy);\n\n  /* loop until we're finished fetching */\n  fd_set fdread;\n  fd_set fdwrite;\n  fd_set fdexcep;\n  struct timeval timeout;\n  int maxfd = -1;\n  int still_running;\n  int rc;\n\n  /* set the timeout. select updates it with the remaining time */\n  timeout.tv_sec = timeout_.count() / 1000000;\n  timeout.tv_usec = (timeout_.count() % 1000000);\n\n  /* loop while timeout has not passed, to fetch requests */\n  while (timeout.tv_sec > 0 || timeout.tv_usec > 0) {\n    FD_ZERO(&fdread);\n    FD_ZERO(&fdwrite);\n    FD_ZERO(&fdexcep);\n\n    /* get file descriptors from the transfers */\n    multi.fdset(&fdread, &fdwrite, &fdexcep, &maxfd);\n\n    if (maxfd == -1) {\n      /* multi needs a bit of time, pause for PAUSE_USEC_WHEN_NOT_READY */\n      u64 remaining_usecs = timeout.tv_sec * 1000000 + timeout.tv_usec;\n      if (remaining_usecs <= PAUSE_USEC_WHEN_NOT_READY) {\n        /* just sleep the rest of the timeout */\n        rc = select(1, NULL, NULL, NULL, &timeout);\n      } else {\n        /* we'll sleep for PAUSE_USEC_WHEN_NOT_READY */\n        struct timeval pause;\n        pause.tv_sec = PAUSE_USEC_WHEN_NOT_READY / 1000000;\n        pause.tv_usec = PAUSE_USEC_WHEN_NOT_READY % 1000000;\n        rc = select(1, NULL, NULL, NULL, &pause);\n\n        /* we wanted to sleep for PAUSE_USEC_WHEN_NOT_READY */\n        remaining_usecs -= PAUSE_USEC_WHEN_NOT_READY;\n        /* account for how much time select did NOT sleep */\n        remaining_usecs += (pause.tv_sec * 1000000) + pause.tv_usec;\n\n        /* fix timeout to account for the pause */\n        timeout.tv_sec = remaining_usecs / 1000000;\n        timeout.tv_usec = remaining_usecs % 1000000;\n      }\n    } else {\n      rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);\n    }\n\n    if (rc == -1) {\n      /* cleanup */\n      for (auto fetchp : fetches) {\n        multi.remove(&fetchp->easy);\n        delete fetchp;\n      }\n      std::stringstream ss;\n      ss << \"select returned -1\\n\"\n         << \"id: \" << id() << \",az: \" << az() << \",iam_role: \" << iam_role() << \",type: \" << type() << \"\\n\";\n      throw std::runtime_error(ss.str());\n    }\n\n    while (!multi.perform(&still_running)) {\n    }\n    LOG::debug_in(\n        CloudPlatform::aws,\n        \"select {} still_running {} timeout {}\",\n        rc,\n        still_running,\n        (timeout.tv_sec * 1000000 + timeout.tv_usec));\n\n    if (still_running == 0)\n      break; /* finished all, can stop looping */\n  }\n\n  /* we will need to map the Easy * to the Fetch *, create a map */\n  std::map<const curlpp::Easy *, Fetch *> curl_to_fetch;\n  for (auto fetch : fetches)\n    curl_to_fetch[&fetch->easy] = fetch;\n\n  /* parse info messages */\n  for (auto info : multi.info()) {\n    auto fetchp = curl_to_fetch[info.first];\n    fetchp->retcode = info.second.code;\n  }\n\n  /* Create result map */\n  std::map<std::string, AwsMetadataValue> ret;\n  for (auto fetchp : fetches)\n    ret.emplace(\n        std::piecewise_construct, std::forward_as_tuple(fetchp->key), std::forward_as_tuple(fetchp->value, fetchp->retcode));\n\n  /* cleanup */\n  for (auto fetchp : fetches) {\n    multi.remove(&fetchp->easy);\n    delete fetchp;\n  }\n\n  return ret;\n}\n\nstd::string AwsMetadata::fetch_imdsv2_token()\n{\n  try {\n    curlpp::Easy easy;\n    std::string token;\n\n    easy.setOpt(curlpp::options::Url(IMDSV2_TOKEN_URL));\n    /* IMDSv2 token requires a PUT to /latest/api/token with TTL header */\n    easy.setOpt(curlpp::options::CustomRequest(\"PUT\"));\n\n    std::list<std::string> headers;\n    headers.push_back(std::string(\"X-aws-ec2-metadata-token-ttl-seconds: \") + IMDSV2_TOKEN_TTL);\n    easy.setOpt(curlpp::options::HttpHeader(headers));\n\n    /* write the response into token string */\n    curlpp::types::WriteFunctionFunctor functor =\n        [&](char *buf, size_t size, size_t nmemb) -> size_t {\n          size_t len = size * nmemb;\n          if (len > 0) token.append(buf, len);\n          return len;\n        };\n    easy.setOpt(curlpp::options::WriteFunction(functor));\n\n    /* don't throw for small network glitches; we still want to fallback to v1 */\n    easy.setOpt(curlpp::options::FailOnError(true));\n\n    /* set a short timeout so token fetch doesn't stall whole metadata retrieval */\n    easy.setOpt(curlpp::options::Timeout(2)); // 2 seconds; tune as you wish\n\n    easy.perform();\n\n    if (!token.empty()) {\n      // strip trailing newline (if any)\n      if (!token.empty() && token.back() == '\\n') token.pop_back();\n      return token;\n    }\n  } catch (const std::exception &e) {\n    // token fetch failed; we will fall back to IMDSv1\n    LOG::debug_in(CloudPlatform::aws, \"IMDSv2 token fetch failed: {}\", e.what());\n  } catch (...) {\n    LOG::debug_in(CloudPlatform::aws, \"IMDSv2 token fetch unknown failure\");\n  }\n\n  return std::string{}; // empty => fall back to IMDSv1\n}\n\nExpected<AwsMetadata, std::runtime_error> AwsMetadata::fetch(std::chrono::microseconds timeout)\n{\n  AwsMetadata metadata{timeout};\n\n  try {\n    if (!metadata.fetch_aws_instance_metadata()) {\n      return {unexpected, \"no metadata returned by AWS\"};\n    }\n  } catch (std::exception const &e) {\n    return {unexpected, e.what()};\n  }\n\n  return std::move(metadata);\n}\n\nbool AwsMetadata::fetch_aws_instance_metadata()\n{\n  // try IMDSv2 token first; if empty => we'll do IMDSv1 GETs (fallback)\n  std::string token = fetch_imdsv2_token();\n  bool using_imdsv2 = !token.empty();\n\n  if (using_imdsv2) {\n    LOG::debug_in(CloudPlatform::aws, \"IMDSv2 token obtained, using IMDSv2 for metadata calls\");\n  } else {\n    LOG::debug_in(CloudPlatform::aws, \"No IMDSv2 token; falling back to IMDSv1 for metadata calls\");\n  }\n\n  // Choose the appropriate metadata & dynamic prefixes based on IMDS version.\n  // Use std::string so runtime concatenation works.\n  std::string metadata_prefix = using_imdsv2 ? std::string(METADATA_PREFIX_V2) : std::string(METADATA_PREFIX_V1);\n  std::string dynamic_prefix = using_imdsv2 ? std::string(DYNAMIC_METADATA_PREFIX_V2) : std::string(DYNAMIC_METADATA_PREFIX_V1);\n\n  /* add all the Fetch object we want to get */\n  std::map<std::string, std::string> fetches;\n  fetches[\"az\"] = metadata_prefix + \"placement/availability-zone\";\n  fetches[\"iam-role\"] = metadata_prefix + \"iam/security-credentials/\";\n  fetches[\"id\"] = metadata_prefix + \"instance-id\";\n  fetches[\"type\"] = metadata_prefix + \"instance-type\";\n  fetches[\"interfaces\"] = metadata_prefix + \"network/interfaces/macs/\";\n  fetches[\"instance-identity\"] = dynamic_prefix + \"instance-identity/document\";\n\n  FetchResult ret = fetch(fetches, token);\n\n  // set general instance metadata\n  id_ = ret[\"id\"];\n  az_ = ret[\"az\"];\n  iam_role_ = ret[\"iam-role\"];\n  type_ = ret[\"type\"];\n\n  if (auto const &identity = ret[\"instance-identity\"]; identity.valid()) {\n    nlohmann::json const instance_identity(identity.value());\n\n    if (auto account_id = try_get_string(instance_identity, \"accountId\")) {\n      account_id_.set(*account_id);\n      LOG::trace_in(CloudPlatform::aws, \"retrieved AWS account id: {}\", *account_id);\n    }\n  }\n\n  if (!id_.valid() && !az_.valid() && !iam_role_.valid() && !type_.valid()) {\n    return false;\n  }\n\n  // set network interfaces\n  if (ret.at(\"interfaces\").valid()) {\n    std::istringstream stream(std::string(ret.at(\"interfaces\").value()));\n    std::string line;\n    while (std::getline(stream, line)) {\n      network_interfaces_.emplace_back(line);\n    }\n  }\n\n  // get vpc_id, private/public ipv4, and ipv6 for each network interface\n  fetches.clear();\n  for (auto interface : network_interfaces_) {\n    std::string vpc_url = metadata_prefix + \"network/interfaces/macs/\" + interface.id() + \"/vpc-id\";\n    std::string local_ipv4_url = metadata_prefix + \"network/interfaces/macs/\" + interface.id() + \"/local-ipv4s\";\n    std::string public_ipv4_url = metadata_prefix + \"network/interfaces/macs/\" + interface.id() + \"/public-ipv4s\";\n    std::string ipv6_url = metadata_prefix + \"network/interfaces/macs/\" + interface.id() + \"/ipv6s\";\n    fetches[interface.id() + \"_vpc_id\"] = vpc_url;\n    fetches[interface.id() + \"_private_ipv4\"] = local_ipv4_url;\n    fetches[interface.id() + \"_public_ipv4\"] = public_ipv4_url;\n    fetches[interface.id() + \"_ipv6\"] = ipv6_url;\n  }\n  ret = fetch(fetches, token);\n\n  for (auto &interface : network_interfaces_) {\n    interface.set_info(ret);\n  }\n\n  // get public-private ipv4 mappings for each network interface\n  fetches.clear();\n  for (auto &interface : network_interfaces_) {\n    for (auto public_ip : interface.public_ipv4s()) {\n      std::string url = metadata_prefix + \"network/interfaces/macs/\" + interface.id() + \"/ipv4-associations/\" + public_ip;\n      fetches[interface.id() + \"_\" + public_ip] = url;\n    }\n  }\n  ret = fetch(fetches, token);\n\n  for (auto &interface : network_interfaces_) {\n    interface.set_mapped_ipv4s(ret);\n  }\n\n  return true;\n}\n\nvoid AwsMetadata::print_instance_metadata() const\n{\n  LOG::info(\"  AZ: {} (valid: {})\", az_.value(), az_.valid());\n  LOG::info(\"  IAM role: {} (valid: {})\", iam_role_.value(), iam_role_.valid());\n  LOG::info(\"  Instance ID: {} (valid: {})\", id_.value(), id_.valid());\n  LOG::info(\"  Instance type: {} (valid: {})\", type_.value(), type_.valid());\n  LOG::info(\"  Account ID: {} (valid: {})\", account_id_.value(), account_id_.valid());\n}\n\nvoid AwsMetadata::print_interfaces() const\n{\n  for (auto interface : network_interfaces_) {\n    interface.print_interface();\n  }\n}\n\nFetch::Fetch(const std::string &name, const char *url, const std::string &imdsv2_token)\n    : key(name), retcode(-1)\n{\n  /* set the URL */\n  easy.setOpt(curlpp::options::Url(url));\n\n  /* set the write callback */\n  using namespace std::placeholders;\n  curlpp::types::WriteFunctionFunctor functor = std::bind(&Fetch::write, this, _1, _2, _3);\n  easy.setOpt(curlpp::options::WriteFunction(functor));\n\n  /* We want to get a failure on failed HTTP retcode (404, etc) */\n  easy.setOpt(curlpp::options::FailOnError(true));\n\n  /* If we have an IMDSv2 token, send it as header for this request */\n  if (!imdsv2_token.empty()) {\n    std::list<std::string> headers;\n    headers.push_back(std::string(\"X-aws-ec2-metadata-token: \") + imdsv2_token);\n    easy.setOpt(curlpp::options::HttpHeader(headers));\n  }\n}\n\nsize_t Fetch::write(char *buf, size_t size, size_t nmemb)\n{\n  size_t len = size * nmemb;\n\n  /* is size sane? */\n  if (len <= 0)\n    return 0;\n\n  /* if resulting value would be too big, only take the remaining size */\n  if (len + value.size() > MAX_LEN) {\n    len = MAX_LEN - value.size();\n  }\n\n  /* sanity check */\n  if (len <= 0)\n    return 0;\n\n  /* ok, can append. will throw if there's a problem */\n  value.append(buf, len);\n  return len;\n}\n\nstd::ostream &operator<<(std::ostream &os, const AwsMetadataValue &val)\n{\n  os << \"AWS-Meta(valid=\" << val.valid() << \" value='\" << val.value() << \"')\";\n  return os;\n}\n"
  },
  {
    "path": "util/aws_instance_metadata.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/expected.h>\n\n#include <chrono>\n#include <iostream>\n#include <list>\n#include <map>\n#include <set>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\nstruct AwsMetadataValue {\n  AwsMetadataValue() : retcode_(-1) {}\n  AwsMetadataValue(std::string v, int rc) : value_(std::move(v)), retcode_(rc) {}\n  friend std::ostream &operator<<(std::ostream &os, const AwsMetadataValue &val);\n\n  std::string_view value() const;\n\n  void set(std::string_view value, int retcode = 0)\n  {\n    value_ = value;\n    retcode_ = 0;\n  }\n\n  bool valid() const { return retcode_ == 0; }\n\n  explicit operator bool() const { return valid(); }\n  bool operator!() const { return !valid(); }\n\nprivate:\n  std::string value_;\n  int retcode_;\n};\n\nclass AwsNetworkInterface {\npublic:\n  using FetchResult = std::map<std::string, AwsMetadataValue>;\n\n  /**\n   * c'tor\n   */\n  AwsNetworkInterface(std::string interface_id);\n\n  /*\n   * Sets vpc_id_, private_ipv4s_, public_ipv4s_, and ipv6s_.\n   * @param info: the result of AwsMetadata::fetch()\n   */\n  void set_info(FetchResult const &info);\n\n  /*\n   * Sets mapped_ipv4s_.\n   * @param info: the result of AwsMetadata::fetch()\n   */\n  void set_mapped_ipv4s(FetchResult const &info);\n\n  /**\n   * Prints information for this interface\n   */\n  void print_interface() const;\n\n  /* accessors */\n  std::string const &id() const { return interface_id_; };\n  std::string const &vpc_id() const { return vpc_id_; };\n  std::set<std::string> const &private_ipv4s() const { return private_ipv4s_; };\n  std::vector<std::string> const &public_ipv4s() const { return public_ipv4s_; };\n  std::vector<std::string> const &ipv6s() const { return ipv6s_; };\n  std::map<std::string, std::string> const &mapped_ipv4s() const { return mapped_ipv4s_; };\n\nprivate:\n  /*\n   * A helper function that fills a vector with lines from a\n   * multiline curl response.\n   * @param info: One of the responses recieved from AwsMetadata::fetch()\n   * @param dest: The vector to be filled.\n   *\n   * This is used specifically to fill {public/private}_ipv4s_ and ipv6s_.\n   */\n  void set_multiline_info(AwsMetadataValue const &info, std::vector<std::string> &dest);\n\n  std::string interface_id_;\n  std::string vpc_id_;\n  std::set<std::string> private_ipv4s_;\n  std::vector<std::string> public_ipv4s_;\n  std::vector<std::string> ipv6s_;\n  std::map<std::string, std::string> mapped_ipv4s_;\n};\n\nclass AwsMetadata {\npublic:\n  using FetchResult = AwsNetworkInterface::FetchResult;\n\n  /**\n   * Prints instance metadata without interface info\n   */\n  void print_instance_metadata() const;\n\n  /**\n   * Prints interface info\n   */\n  void print_interfaces() const;\n\n  /* accessors */\n  AwsMetadataValue const &id() const { return id_; };\n  AwsMetadataValue const &az() const { return az_; };\n  AwsMetadataValue const &iam_role() const { return iam_role_; };\n  AwsMetadataValue const &type() const { return type_; };\n  AwsMetadataValue const &account_id() const { return account_id_; };\n\n  const std::vector<AwsNetworkInterface> &network_interfaces() const { return network_interfaces_; };\n\n  static Expected<AwsMetadata, std::runtime_error> fetch(std::chrono::microseconds timeout);\n\nprivate:\n  /**\n   * c'tor\n   * @param timeout_usec: the timeout any fetches that will be performed.\n   */\n  AwsMetadata(std::chrono::microseconds timeout);\n\n  /**\n   * Calls fetch for some meta-data fields\n   *\n   * Returns true on success or false on failure.\n   */\n  bool fetch_aws_instance_metadata();\n\n  /**\n   * Fetches data in parallel with a timeout of timeout_usec_\n   *\n   * @assumes curl_global_init() was called!\n   *\n   * @param keys_to_endpoints: a map of <key> to <endpoint>\n   * @param imdsv2_token: optional IMDSv2 token for authenticated requests\n   * @result: a map of <key> to <result> of a curl to the corresponding endpoint\n   */\n  FetchResult fetch(std::map<std::string, std::string> keys_to_endpoints, const std::string &imdsv2_token = \"\");\n\n  /**\n   * Try to fetch an IMDSv2 token. Returns empty string on failure.\n   */\n  std::string fetch_imdsv2_token();\n\n  std::chrono::microseconds timeout_;\n  AwsMetadataValue id_;\n  AwsMetadataValue az_;\n  AwsMetadataValue iam_role_;\n  AwsMetadataValue type_;\n  AwsMetadataValue account_id_;\n  std::vector<AwsNetworkInterface> network_interfaces_;\n};\n"
  },
  {
    "path": "util/base64.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/base64.h>\n\n#include <openssl/bio.h>\n#include <openssl/evp.h>\n\n#include <cmath>\n#include <cstdio>\n#include <cstdlib>\n\nstd::string base64_encode(std::string_view input)\n{\n  std::string buffer;\n  buffer.resize(4 * std::ceil(static_cast<double>(input.size()) / 3) + 1);\n\n  auto out = ::fmemopen(buffer.data(), buffer.size(), \"w\");\n  auto encoder = BIO_push(BIO_new(BIO_f_base64()), BIO_new_fp(out, BIO_NOCLOSE));\n\n  BIO_set_flags(encoder, BIO_FLAGS_BASE64_NO_NL);\n\n  BIO_write(encoder, input.data(), input.size());\n  BIO_flush(encoder);\n\n  BIO_free_all(encoder);\n  std::fclose(out);\n\n  if (!buffer.empty() && buffer.back() == '\\0') {\n    buffer.pop_back();\n  }\n\n  return buffer;\n}\n"
  },
  {
    "path": "util/base64.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n#include <string_view>\n\nstd::string base64_encode(std::string_view input);\n"
  },
  {
    "path": "util/base64_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/base64.h>\n\nTEST(base64, test_battery)\n{\n  EXPECT_EQ(\"\", base64_encode(\"\"));\n\n  EXPECT_EQ(\"MA==\", base64_encode(\"0\"));\n  EXPECT_EQ(\"MDE=\", base64_encode(\"01\"));\n  EXPECT_EQ(\"MDEy\", base64_encode(\"012\"));\n  EXPECT_EQ(\"MDEyMw==\", base64_encode(\"0123\"));\n  EXPECT_EQ(\"MDEyMzQ=\", base64_encode(\"01234\"));\n  EXPECT_EQ(\"MDEyMzQ1\", base64_encode(\"012345\"));\n  EXPECT_EQ(\"MDEyMzQ1Ng==\", base64_encode(\"0123456\"));\n  EXPECT_EQ(\"MDEyMzQ1Njc=\", base64_encode(\"01234567\"));\n  EXPECT_EQ(\"MDEyMzQ1Njc4\", base64_encode(\"012345678\"));\n  EXPECT_EQ(\"MDEyMzQ1Njc4OQ==\", base64_encode(\"0123456789\"));\n\n  EXPECT_EQ(\"OQ==\", base64_encode(\"9\"));\n  EXPECT_EQ(\"OTg=\", base64_encode(\"98\"));\n  EXPECT_EQ(\"OTg3\", base64_encode(\"987\"));\n  EXPECT_EQ(\"OTg3Ng==\", base64_encode(\"9876\"));\n  EXPECT_EQ(\"OTg3NjU=\", base64_encode(\"98765\"));\n  EXPECT_EQ(\"OTg3NjU0\", base64_encode(\"987654\"));\n  EXPECT_EQ(\"OTg3NjU0Mw==\", base64_encode(\"9876543\"));\n  EXPECT_EQ(\"OTg3NjU0MzI=\", base64_encode(\"98765432\"));\n  EXPECT_EQ(\"OTg3NjU0MzIx\", base64_encode(\"987654321\"));\n  EXPECT_EQ(\"OTg3NjU0MzIxMA==\", base64_encode(\"9876543210\"));\n\n  EXPECT_EQ(\"QQ==\", base64_encode(\"A\"));\n  EXPECT_EQ(\"QUI=\", base64_encode(\"AB\"));\n  EXPECT_EQ(\"QUJD\", base64_encode(\"ABC\"));\n  EXPECT_EQ(\"QUJDRA==\", base64_encode(\"ABCD\"));\n  EXPECT_EQ(\"QUJDREU=\", base64_encode(\"ABCDE\"));\n  EXPECT_EQ(\"QUJDREVG\", base64_encode(\"ABCDEF\"));\n  EXPECT_EQ(\"QUJDREVGRw==\", base64_encode(\"ABCDEFG\"));\n  EXPECT_EQ(\"QUJDREVGR0g=\", base64_encode(\"ABCDEFGH\"));\n  EXPECT_EQ(\"QUJDREVGR0hJ\", base64_encode(\"ABCDEFGHI\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSg==\", base64_encode(\"ABCDEFGHIJ\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSks=\", base64_encode(\"ABCDEFGHIJK\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktM\", base64_encode(\"ABCDEFGHIJKL\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTQ==\", base64_encode(\"ABCDEFGHIJKLM\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU4=\", base64_encode(\"ABCDEFGHIJKLMN\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5P\", base64_encode(\"ABCDEFGHIJKLMNO\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUA==\", base64_encode(\"ABCDEFGHIJKLMNOP\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFE=\", base64_encode(\"ABCDEFGHIJKLMNOPQ\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFS\", base64_encode(\"ABCDEFGHIJKLMNOPQR\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSUw==\", base64_encode(\"ABCDEFGHIJKLMNOPQRS\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1Q=\", base64_encode(\"ABCDEFGHIJKLMNOPQRST\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RV\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTU\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RVVg==\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTUV\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RVVlc=\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTUVW\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RVVldY\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTUVWX\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWQ==\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTUVWXY\"));\n  EXPECT_EQ(\"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\", base64_encode(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"));\n\n  EXPECT_EQ(\"Wg==\", base64_encode(\"Z\"));\n  EXPECT_EQ(\"Wlk=\", base64_encode(\"ZY\"));\n  EXPECT_EQ(\"WllY\", base64_encode(\"ZYX\"));\n  EXPECT_EQ(\"WllYVw==\", base64_encode(\"ZYXW\"));\n  EXPECT_EQ(\"WllYV1Y=\", base64_encode(\"ZYXWV\"));\n  EXPECT_EQ(\"WllYV1ZV\", base64_encode(\"ZYXWVU\"));\n  EXPECT_EQ(\"WllYV1ZVVA==\", base64_encode(\"ZYXWVUT\"));\n  EXPECT_EQ(\"WllYV1ZVVFM=\", base64_encode(\"ZYXWVUTS\"));\n  EXPECT_EQ(\"WllYV1ZVVFNS\", base64_encode(\"ZYXWVUTSR\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUQ==\", base64_encode(\"ZYXWVUTSRQ\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVA=\", base64_encode(\"ZYXWVUTSRQP\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBP\", base64_encode(\"ZYXWVUTSRQPO\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTg==\", base64_encode(\"ZYXWVUTSRQPON\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk0=\", base64_encode(\"ZYXWVUTSRQPONM\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1M\", base64_encode(\"ZYXWVUTSRQPONML\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MSw==\", base64_encode(\"ZYXWVUTSRQPONMLK\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0o=\", base64_encode(\"ZYXWVUTSRQPONMLKJ\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJ\", base64_encode(\"ZYXWVUTSRQPONMLKJI\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSA==\", base64_encode(\"ZYXWVUTSRQPONMLKJIH\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEc=\", base64_encode(\"ZYXWVUTSRQPONMLKJIHG\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdG\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGF\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdGRQ==\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGFE\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdGRUQ=\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGFED\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdGRURD\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGFEDC\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQg==\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGFEDCB\"));\n  EXPECT_EQ(\"WllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkE=\", base64_encode(\"ZYXWVUTSRQPONMLKJIHGFEDCBA\"));\n\n  EXPECT_EQ(\"YQ==\", base64_encode(\"a\"));\n  EXPECT_EQ(\"YWI=\", base64_encode(\"ab\"));\n  EXPECT_EQ(\"YWJj\", base64_encode(\"abc\"));\n  EXPECT_EQ(\"YWJjZA==\", base64_encode(\"abcd\"));\n  EXPECT_EQ(\"YWJjZGU=\", base64_encode(\"abcde\"));\n  EXPECT_EQ(\"YWJjZGVm\", base64_encode(\"abcdef\"));\n  EXPECT_EQ(\"YWJjZGVmZw==\", base64_encode(\"abcdefg\"));\n  EXPECT_EQ(\"YWJjZGVmZ2g=\", base64_encode(\"abcdefgh\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hp\", base64_encode(\"abcdefghi\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpag==\", base64_encode(\"abcdefghij\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpams=\", base64_encode(\"abcdefghijk\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamts\", base64_encode(\"abcdefghijkl\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbQ==\", base64_encode(\"abcdefghijklm\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW4=\", base64_encode(\"abcdefghijklmn\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5v\", base64_encode(\"abcdefghijklmno\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcA==\", base64_encode(\"abcdefghijklmnop\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHE=\", base64_encode(\"abcdefghijklmnopq\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFy\", base64_encode(\"abcdefghijklmnopqr\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFycw==\", base64_encode(\"abcdefghijklmnopqrs\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3Q=\", base64_encode(\"abcdefghijklmnopqrst\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1\", base64_encode(\"abcdefghijklmnopqrstu\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==\", base64_encode(\"abcdefghijklmnopqrstuv\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=\", base64_encode(\"abcdefghijklmnopqrstuvw\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4\", base64_encode(\"abcdefghijklmnopqrstuvwx\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==\", base64_encode(\"abcdefghijklmnopqrstuvwxy\"));\n  EXPECT_EQ(\"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\", base64_encode(\"abcdefghijklmnopqrstuvwxyz\"));\n\n  EXPECT_EQ(\"eg==\", base64_encode(\"z\"));\n  EXPECT_EQ(\"enk=\", base64_encode(\"zy\"));\n  EXPECT_EQ(\"enl4\", base64_encode(\"zyx\"));\n  EXPECT_EQ(\"enl4dw==\", base64_encode(\"zyxw\"));\n  EXPECT_EQ(\"enl4d3Y=\", base64_encode(\"zyxwv\"));\n  EXPECT_EQ(\"enl4d3Z1\", base64_encode(\"zyxwvu\"));\n  EXPECT_EQ(\"enl4d3Z1dA==\", base64_encode(\"zyxwvut\"));\n  EXPECT_EQ(\"enl4d3Z1dHM=\", base64_encode(\"zyxwvuts\"));\n  EXPECT_EQ(\"enl4d3Z1dHNy\", base64_encode(\"zyxwvutsr\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycQ==\", base64_encode(\"zyxwvutsrq\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXA=\", base64_encode(\"zyxwvutsrqp\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBv\", base64_encode(\"zyxwvutsrqpo\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbg==\", base64_encode(\"zyxwvutsrqpon\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm0=\", base64_encode(\"zyxwvutsrqponm\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1s\", base64_encode(\"zyxwvutsrqponml\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1saw==\", base64_encode(\"zyxwvutsrqponmlk\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2o=\", base64_encode(\"zyxwvutsrqponmlkj\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2pp\", base64_encode(\"zyxwvutsrqponmlkji\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaA==\", base64_encode(\"zyxwvutsrqponmlkjih\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGc=\", base64_encode(\"zyxwvutsrqponmlkjihg\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdm\", base64_encode(\"zyxwvutsrqponmlkjihgf\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdmZQ==\", base64_encode(\"zyxwvutsrqponmlkjihgfe\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdmZWQ=\", base64_encode(\"zyxwvutsrqponmlkjihgfed\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRj\", base64_encode(\"zyxwvutsrqponmlkjihgfedc\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYg==\", base64_encode(\"zyxwvutsrqponmlkjihgfedcb\"));\n  EXPECT_EQ(\"enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmE=\", base64_encode(\"zyxwvutsrqponmlkjihgfedcba\"));\n}\n"
  },
  {
    "path": "util/bits.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <type_traits>\n\n#include <cassert>\n#include <climits>\n\nnamespace impl {\n\n/**\n * Resolves to the underlying type of the enum, or to the type itself it it's\n * not an enum.\n *\n * Needed because `underlying_type_t` doesn't \"just work\" for non-enums.\n */\ntemplate <typename T, bool = std::is_enum_v<T>> struct enum_integer {\n  using type = T;\n};\n\ntemplate <typename T> struct enum_integer<T, true> {\n  using type = std::underlying_type_t<T>;\n};\n\ntemplate <typename T> using enum_integer_t = typename enum_integer<T>::type;\n\n// implementation of `smallest_unsigned_integer`\ntemplate <std::size_t BitCount> constexpr auto smallest_unsigned_integer_impl()\n{\n  if constexpr (BitCount <= 1) {\n    return bool{};\n  } else if constexpr (BitCount <= (sizeof(u8) * CHAR_BIT)) {\n    return u8{};\n  } else if constexpr (BitCount <= (sizeof(u16) * CHAR_BIT)) {\n    return u16{};\n  } else if constexpr (BitCount <= (sizeof(u32) * CHAR_BIT)) {\n    return u32{};\n  } else if constexpr (BitCount <= (sizeof(u64) * CHAR_BIT)) {\n    return u64{};\n  } else if constexpr (BitCount <= (sizeof(u128) * CHAR_BIT)) {\n    return u128{};\n  } else {\n    static_assert(BitCount <= (sizeof(u128) * CHAR_BIT), \"bit count is too large for any known integer\");\n  }\n}\n\n} // namespace impl\n\n/**\n * Resolves to the smallest unsigned integer type capable of holding `BitCount` bits.\n */\ntemplate <std::size_t BitCount> using smallest_unsigned_integer = decltype(impl::smallest_unsigned_integer_impl<BitCount>());\n\n/**\n * Counts the number of `on` bits in the given value.\n *\n * Example:\n *\n *  count_bits_set(0b0000); // returns 0\n *  count_bits_set(0b0001); // returns 1\n *  count_bits_set(0b0010); // returns 1\n *  count_bits_set(0b1011); // returns 3\n *  count_bits_set(0b1100); // returns 2\n */\ntemplate <typename T> constexpr std::size_t count_bits_set(T value)\n{\n  static_assert(std::is_integral_v<T>);\n  if constexpr (sizeof(T) <= (sizeof(unsigned int))) {\n    return __builtin_popcount(static_cast<unsigned int>(static_cast<std::make_unsigned_t<T>>(value)));\n  } else if constexpr (sizeof(T) <= (sizeof(unsigned long))) {\n    return __builtin_popcountl(static_cast<unsigned long>(static_cast<std::make_unsigned_t<T>>(value)));\n  } else {\n    static_assert(sizeof(T) <= (sizeof(unsigned long long)));\n    return __builtin_popcountll(static_cast<unsigned long long>(static_cast<std::make_unsigned_t<T>>(value)));\n  }\n}\n\n/**\n * Counts the 0-based index of the least significant bit\n * Result is undefined if `value` is `0`\n *\n * Example:\n *\n *  least_significant_bit_index(0b0001); // returns 0\n *  least_significant_bit_index(0b0010); // returns 1\n *  least_significant_bit_index(0b1011); // returns 0\n *  least_significant_bit_index(0b1000); // returns 3\n */\ntemplate <typename T> constexpr std::size_t least_significant_bit_index(T value)\n{\n  static_assert(std::is_integral_v<T>);\n  if constexpr (sizeof(T) <= (sizeof(unsigned int))) {\n    return __builtin_ctz(static_cast<unsigned int>(static_cast<std::make_unsigned_t<T>>(value)));\n  } else if constexpr (sizeof(T) <= (sizeof(unsigned long))) {\n    return __builtin_ctzl(static_cast<unsigned long>(static_cast<std::make_unsigned_t<T>>(value)));\n  } else {\n    static_assert(sizeof(T) <= (sizeof(unsigned long long)));\n    return __builtin_ctzll(static_cast<unsigned long long>(static_cast<std::make_unsigned_t<T>>(value)));\n  }\n}\n\n/**\n * Returns the given value with its least significant bit set to `off`.\n *\n * Takes care of proper type conversion, enum class support and avoids problems\n * with implicit type promotions.\n *\n * Example:\n *\n *  disable_least_significant_bit(0b0000); // returns 0b0000\n *  disable_least_significant_bit(0b0001); // returns 0b0000\n *  disable_least_significant_bit(0b0010); // returns 0b0000\n *  disable_least_significant_bit(0b1011); // returns 0b1010\n *  disable_least_significant_bit(0b1100); // returns 0b1000\n */\ntemplate <typename T> constexpr T disable_least_significant_bit(T value)\n{\n  using type = impl::enum_integer_t<T>;\n  return static_cast<T>(static_cast<type>(value) & (static_cast<type>(value) - type{1}));\n}\n\n/**\n * Returns the least significant bit only, in its original position.\n *\n * Takes care of proper type conversion, enum class support and avoids problems\n * with implicit type promotions.\n *\n * Example:\n *\n *  least_significant_bit(0b0000); // returns 0b0000\n *  least_significant_bit(0b0001); // returns 0b0001\n *  least_significant_bit(0b0010); // returns 0b0010\n *  least_significant_bit(0b1011); // returns 0b0001\n *  least_significant_bit(0b1100); // returns 0b0100\n */\ntemplate <typename T> constexpr T least_significant_bit(T value)\n{\n  using type = impl::enum_integer_t<T>;\n  return static_cast<T>((static_cast<type>(value) ^ (static_cast<type>(value) - type{1})) & static_cast<type>(value));\n}\n\n/**\n * Returns a value of type `T` with the bit at 0-based position `index` set to\n * `on` and all other bits `off`.\n *\n * Takes care of proper type conversion, enum class support and avoids problems\n * with implicit type promotions.\n *\n * Example:\n *\n *  make_bit<int>(0); // returns 0b0001\n *  make_bit<int>(1); // returns 0b0010\n *  make_bit<int>(2); // returns 0b0100\n *  make_bit<int>(3); // returns 0b1000\n */\ntemplate <typename T> constexpr T make_bit(std::size_t bit_index)\n{\n  using type = impl::enum_integer_t<T>;\n  // ensure that the given bit fits in the result type\n  assert(bit_index < (sizeof(T) * CHAR_BIT));\n  return static_cast<T>(type{1} << static_cast<type>(bit_index));\n}\n\n/**\n * Returns the result of `value << shift`, taking care of implicit type\n * promotions which could go unnoticed.\n */\ntemplate <typename T, typename LHS, typename RHS> constexpr T shift_left(LHS value, RHS shift)\n{\n  return static_cast<T>(static_cast<T>(value) << static_cast<T>(shift));\n}\n\n/**\n * Returns the result of `value >> shift`, taking care of implicit type\n * promotions which could go unnoticed.\n */\ntemplate <typename T, typename LHS, typename RHS> constexpr T shift_right(LHS value, RHS shift)\n{\n  return static_cast<T>(static_cast<T>(value) >> static_cast<T>(shift));\n}\n"
  },
  {
    "path": "util/bits_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/bits.h>\n\n#include <gtest/gtest.h>\n\n#include <type_traits>\n\n#define EXPECT_SAME(Expected, ...) EXPECT_TRUE((std::is_same_v<Expected, __VA_ARGS__>))\n\nTEST(bits, smallest_unsigned_integer)\n{\n  EXPECT_SAME(bool, smallest_unsigned_integer<0>);\n  EXPECT_SAME(bool, smallest_unsigned_integer<1>);\n\n  EXPECT_SAME(u8, smallest_unsigned_integer<2>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<3>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<4>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<5>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<6>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<7>);\n  EXPECT_SAME(u8, smallest_unsigned_integer<8>);\n\n  EXPECT_SAME(u16, smallest_unsigned_integer<9>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<10>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<11>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<12>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<13>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<14>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<15>);\n  EXPECT_SAME(u16, smallest_unsigned_integer<16>);\n\n  EXPECT_SAME(u32, smallest_unsigned_integer<17>);\n  EXPECT_SAME(u32, smallest_unsigned_integer<24>);\n  EXPECT_SAME(u32, smallest_unsigned_integer<32>);\n\n  EXPECT_SAME(u64, smallest_unsigned_integer<33>);\n  EXPECT_SAME(u64, smallest_unsigned_integer<48>);\n  EXPECT_SAME(u64, smallest_unsigned_integer<64>);\n\n  EXPECT_SAME(u128, smallest_unsigned_integer<65>);\n  EXPECT_SAME(u128, smallest_unsigned_integer<99>);\n  EXPECT_SAME(u128, smallest_unsigned_integer<128>);\n}\n\nTEST(bits, count_bits_set)\n{\n  EXPECT_EQ(0u, count_bits_set(0b0000));\n  EXPECT_EQ(1u, count_bits_set(0b0001));\n  EXPECT_EQ(1u, count_bits_set(0b0010));\n  EXPECT_EQ(3u, count_bits_set(0b1011));\n  EXPECT_EQ(2u, count_bits_set(0b1100));\n}\n\nTEST(bits, least_significant_bit_index)\n{\n  EXPECT_EQ(0u, least_significant_bit_index(0b0001));\n  EXPECT_EQ(1u, least_significant_bit_index(0b0010));\n  EXPECT_EQ(0u, least_significant_bit_index(0b1011));\n  EXPECT_EQ(3u, least_significant_bit_index(0b1000));\n}\n\nTEST(bits, disable_least_significant_bit)\n{\n  EXPECT_EQ(0b0000, disable_least_significant_bit(0b0000));\n  EXPECT_EQ(0b0000, disable_least_significant_bit(0b0001));\n  EXPECT_EQ(0b0000, disable_least_significant_bit(0b0010));\n  EXPECT_EQ(0b1010, disable_least_significant_bit(0b1011));\n  EXPECT_EQ(0b1000, disable_least_significant_bit(0b1100));\n}\n\nTEST(bits, least_significant_bit)\n{\n  EXPECT_EQ(0b0000, least_significant_bit(0b0000));\n  EXPECT_EQ(0b0001, least_significant_bit(0b0001));\n  EXPECT_EQ(0b0010, least_significant_bit(0b0010));\n  EXPECT_EQ(0b0001, least_significant_bit(0b1011));\n  EXPECT_EQ(0b0100, least_significant_bit(0b1100));\n}\n\nTEST(bits, make_bits)\n{\n  EXPECT_EQ(0b0001, make_bit<int>(0));\n  EXPECT_EQ(0b0010, make_bit<int>(1));\n  EXPECT_EQ(0b0100, make_bit<int>(2));\n  EXPECT_EQ(0b1000, make_bit<int>(3));\n}\n"
  },
  {
    "path": "util/boot_time.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"util/boot_time.h\"\n\n#include <time.h>\n\nu64 get_boot_time()\n{\n  struct timespec current, uptime;\n  clock_gettime(CLOCK_REALTIME, &current);\n  clock_gettime(CLOCK_MONOTONIC, &uptime);\n  u64 boot_time = current.tv_sec * 1000000000uLL + current.tv_nsec - (uptime.tv_sec * 1000000000uLL + uptime.tv_nsec);\n  return boot_time;\n}\n"
  },
  {
    "path": "util/boot_time.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"platform/platform.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// time in nanoseconds\nu64 get_boot_time();\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "util/buffer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string_view>\n\nnamespace buffer {\n\n/**\n * A writable view of a contiguous chunk of memory.\n *\n * This class is similar to `std::string_view`, but allows data to be written\n * to the buffer.\n *\n * TODO: support ring buffers where we can have 1 or 2 chunks of contiguous\n * buffers - a generalization would allow N chunks.\n */\nclass WritableBufferView {\npublic:\n  WritableBufferView(char *begin, char *end) : begin_(begin), end_(end) {}\n\n  /**\n   * Returns a read-only view of this writable view.\n   */\n  std::string_view view() const { return {begin_, size()}; }\n\n  // STL containers compatibility\n\n  char const *data() const { return begin_; }\n  char *data() { return begin_; }\n\n  char const *cbegin() const { return begin_; }\n  char const *begin() const { return begin_; }\n  char *begin() { return begin_; }\n  char const *cend() const { return end_; }\n  char const *end() const { return end_; }\n  char *end() { return end_; }\n\n  std::size_t size() const { return end_ - begin_; }\n\nprivate:\n  char *begin_;\n  char *end_;\n};\n\n/**\n * A buffer class that supports chunk read/write operations.\n *\n * Internally, the buffer is a contiguous block of memory with the following layout:\n *\n *   array:   [ consumed / available / free ]\n *            ^          ^           ^      ^\n * indices:   0          read_       write_ Size\n *\n * - consumed: data that has already been read from the buffer;\n * - available: data that has been written into the buffer, available to\n *   read operations;\n * - free: free space in the buffer, available for write operations.\n */\ntemplate <std::size_t Size> class Buffer {\npublic:\n  /**\n   * Constructs an empty buffer of size `Size`.\n   */\n  Buffer() : read_(buffer_), write_(buffer_) {}\n\n  Buffer(Buffer const &) = delete;\n  Buffer(Buffer &&) = delete;\n\n  /**\n   * Returns a read-only view of the `consumed` data section.\n   */\n  std::string_view consumed() const { return {buffer_, static_cast<std::size_t>(read_ - buffer_)}; }\n\n  /**\n   * Returns a read-only view of the `available` data section.\n   */\n  std::string_view available() const { return {read_, static_cast<std::size_t>(write_ - read_)}; }\n\n  /**\n   * Returns a read-only view of the `consumed` and `available` data sections\n   * in a single chunk.\n   */\n  std::string_view view() const { return {buffer_, static_cast<std::size_t>(write_ - buffer_)}; }\n\n  /**\n   * Marks the first `count` bytes of the `available` section as `consumed`.\n   * This must be called after data is read from the buffer so more data can be\n   * made available to read operations.\n   *\n   * @assumes: `count` <= `available.size()`\n   */\n  Buffer &consume(std::size_t count)\n  {\n    assert(count <= available().size());\n    read_ += count;\n    return *this;\n  }\n\n  /**\n   * Returns a writable view of the `free` data section.\n   *\n   * See `commit()` for how to make written data available to read operations.\n   */\n  WritableBufferView writable()\n  {\n    assert(buffer_ <= write_);\n    assert(buffer_ + Size >= write_);\n    return {write_, buffer_ + Size};\n  }\n\n  /**\n   * Marks the first `count` bytes of the `free` section as `available`. This\n   * must be called after data is written into the buffer so it can be made\n   * available to read operations.\n   *\n   * @assumes: `count` <= `free()`\n   */\n  Buffer &commit(std::size_t count)\n  {\n    assert(count <= free());\n    write_ += count;\n    return *this;\n  }\n\n  /**\n   * Drop all written data that hasn't been consumed by a read operation.\n   */\n  Buffer &drop()\n  {\n    write_ = read_;\n    return *this;\n  }\n\n  /**\n   * Drop the last `count` written bytes that haven't been consumed by a read operation.\n   *\n   * @assumes: `count` <= `available().size()`\n   */\n  Buffer &drop(std::size_t count)\n  {\n    assert(count <= available().size());\n    write_ = read_;\n    return *this;\n  }\n\n  /**\n   * Rewinds the read cursor to the beginning of the buffer, so that data\n   * that has already been consumed can be read again.\n   */\n  Buffer &rewind()\n  {\n    read_ = 0;\n    return *this;\n  }\n\n  /**\n   * Rewinds the read cursor back `count` bytes so that the last `count`\n   * bytes read can be read again.\n   *\n   * @assumes: `count` <= `consumed().size()`\n   */\n  Buffer &rewind(std::size_t count)\n  {\n    assert(count <= consumed().size());\n    read_ -= count;\n    return *this;\n  }\n\n  /**\n   * Resets the buffer to its initial state, where there's no data available\n   * for read operations and the whole capacity is available for write\n   * operations.\n   *\n   * No data in the internal buffer is overwritten.\n   */\n  Buffer &clear()\n  {\n    read_ = write_ = buffer_;\n    return *this;\n  }\n\n  /**\n   * Frees up space in the buffer by pruning the `consumed` section and\n   * moving the `available` section to the beginning of the buffer.\n   */\n  Buffer &compact()\n  {\n    std::memmove(buffer_, read_, available().size());\n    write_ -= consumed().size();\n    read_ = buffer_;\n    return *this;\n  }\n\n  std::size_t free() const { return buffer_ + Size - write_; }\n  static constexpr std::size_t size() { return Size; }\n\n  /**\n   * Tells whether the buffer is full and has no more space for writes.\n   */\n  bool full() const { return write_ == buffer_ + Size; }\n\n  /**\n   * Tells whether the buffer is empty and has no data written into it.\n   */\n  bool empty() const { return write_ == buffer_; }\n\nprivate:\n  char buffer_[Size];\n  char *read_;\n  char *write_;\n};\n\n} // namespace buffer\n"
  },
  {
    "path": "util/cgroup_parser.cc",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"cgroup_parser.h\"\n\n#include \"parser_utils.h\"\n#include \"string_view.h\"\n#include \"log.h\"\n\n#include <cctype>\n\nusing namespace parsing;\n\nnamespace {\n  // just a little adapter for the c-style classification fn\n  bool is_hex_digit(char c) {\n    return isxdigit((int)c) > 0;\n  }\n}\n\nCGroupParser::CGroupParser(std::string_view cgroup_name) :\n  cgroup_name_(cgroup_name),\n  read_head_(cgroup_name_.begin())\n{\n  // enter and descend\n  info_.valid = parse_cgroup();\n  LOG::trace(\n      \"CGroupParser info for\"\n      \" cgroup_name: '{}'\"\n      \" container_id: '{}' \"\n      \" pod_id: '{}'\"\n      \" qos: '{}'\"\n      \" runtime: '{}'\"\n      \" service: '{}'\"\n      \" valid: '{}'\",\n      cgroup_name_,\n      info_.container_id,\n      info_.pod_id,\n      info_.qos,\n      info_.runtime,\n      info_.service,\n      info_.valid);\n}\n\nbool CGroupParser::parse_cgroup() {\n  // the seen cgroup formats\n  if (!parse_systemd() && \n      !parse_cri() && \n      !parse_pod_id() && \n      !parse_container_id() &&\n      !parse_service()) {\n    return false;\n  }\n\n  return true;\n}\n\nbool CGroupParser::parse_systemd()\n{\n  // systemd style cgroups.  note: uses '-' to seperate the parent-child\n  // relations\n  //\n  // examples:\n  // kubepods-burstable-pod146bb920_a47b_4f6c_a69a_166b63944d15.slice:cri-containerd:c45f3e9c19746eabf0a4af63d780ba5c2a657a7352c7ad7acc5d599da5115eef\n  // kubepods-besteffort-pod29c71929_0064_4c15_9595_702c5931a368.slice\n  if (!parse_match(read_head_, cgroup_name_.end(), \"kubepods-\")) {\n    return false;\n  }\n\n  // quality of service classification\n  if (!parse_qos()) {\n    return false;\n  }\n\n  // eat until the next separator - we don't care about \".slice:...\" if present\n  parse_token(read_head_, cgroup_name_.end(), '-');\n\n // optional - may just be kubepods-<qos>.slice\n  // pod unique id\n  if (!parse_pod_id()) {\n    // done and valid\n    return true;\n  }\n  \n  // eat until the next separator - we don't care about \".slice:...\" if present\n  parse_token(read_head_, cgroup_name_.end(), '-');\n\n  // optional - may just be kubepods-<qos>-pod<pod_id>.slice\n  // containerd and friends\n  if (!parse_runtime(':')) {\n    // done and valid\n    return true;\n  }\n\n  // optional\n  // the container's id\n  if (!parse_container_id()) {\n    return true;\n  }\n\n  return true;\n}\n\nbool CGroupParser::parse_cri() {\n  // cgroups starting with 'cri'.  this has a runtime and \n  // a container id\n  // example:\n  // cri-containerd-15736ea91752be37a640dc949e3e805521f4af5c5e3fe50643af0e63a5ce0df5.scope\n  if (!parse_match(read_head_, cgroup_name_.end(), \"cri-\")) {\n    return false;\n  }\n\n  // containerd and friends\n  if (!parse_runtime('-')) {\n    return false;\n  }\n\n  // the container's id\n  if (!parse_container_id()) {\n    return false;\n  }\n\n  return true;\n}\n\nbool CGroupParser::parse_container_id() {\n  // just 64 hex characters for the container's id\n  // example:\n  // 6f652f89943b50f7b101d13f11371daf34bf836b7e1b725b5e8b6439451018bd\n  for (size_t ii = 0; ii < 64; ++ii) {\n    if (!parse_match(read_head_, cgroup_name_.end(), is_hex_digit, &info_.container_id)) {\n      info_.container_id.clear();\n      return false;\n    }\n  }\n  return true;\n}\n\nbool CGroupParser::parse_pod_id() {\n  // \"pod\" followed by a unique pod id.\n  // the format of the unique id varies between cgroupfs and systemd\n  // we normalize to the canonical representation\n  // examples:\n  // podf55fb707-9bf6-4bf5-8a7e-19c5f3e52215\n  // podf55fb707_9bf6_4bf5_8a7e_19c5f3e52215\n  // podf55fb7079bf64bf58a7e19c5f3e52215\n  if (!parse_match(read_head_, cgroup_name_.end(), \"pod\")) {\n    return false;\n  }\n\n  return parse_uid();\n}\n\nbool CGroupParser::parse_service() {\n  // service is ambiguous, so just peek at the suffix to see if we are a \n  // service\n  //\n  // example :\n  // systemd-journald.service\n  static constexpr std::string_view SERVICE_SUFFIX = \".service\";\n  if (views::ends_with(cgroup_name_, SERVICE_SUFFIX)) {\n    info_.service = cgroup_name_.substr(0, cgroup_name_.size() - SERVICE_SUFFIX.size());\n    return true;\n  }\n  return false;\n}\n\nbool CGroupParser::parse_qos() {\n  // see https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/\n  if (parse_match(read_head_, cgroup_name_.end(), \"guaranteed\", &info_.qos)) {\n    return true;\n  }\n\n  if (parse_match(read_head_, cgroup_name_.end(), \"besteffort\", &info_.qos)) {\n    return true;\n  }\n\n  if (parse_match(read_head_, cgroup_name_.end(), \"burstable\", &info_.qos)) {\n    return true;\n  }\n\n  return false;\n}\n\nbool CGroupParser::parse_uid() {\n  // parse the uuid groups, converting to the canonical format\n  for(size_t ii : {8, 4, 4, 4, 12}) {\n    if (!parse_uid_group(ii)) {\n      return false;\n    }\n\n    // if it's not the last group, handle the differences in seen\n    // formats.  it may have a '-' or '_' to separate, or \n    // no separator at all.\n    if (ii != 12) {\n      char c;\n      if (!peek(read_head_, cgroup_name_.end(), &c)) {\n        return false;\n      }\n\n      // the next char may be '_' (for systemd style), '-' (cgroupfs),\n      // or no separator. regardless, convert to canonical style:\n      // 8-4-4-4-12\n      if (c == '-' || c == '_') {\n        // eat the separator, if present\n        consume(read_head_, cgroup_name_.end());\n      }\n\n      // append the canonical separator\n      info_.pod_id += \"-\";\n    }\n  }\n\n  return true;\n}\n\nbool CGroupParser::parse_uid_group(size_t count)\n{\n  // all the hex digits in the group (8,4, or 12)\n  for(size_t ii = 0; ii < count; ++ii) {\n    if (!parse_match(read_head_, cgroup_name_.end(), is_hex_digit, &info_.pod_id)) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool CGroupParser::parse_runtime(char token) {\n  // ought we to validate this? containerd, etc...\n  return parse_token(read_head_, cgroup_name_.end(), token, &info_.runtime);\n}\n"
  },
  {
    "path": "util/cgroup_parser.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n#pragma once\n\n#include <string>\n#include <string_view>\n#include <iostream>\n\n// information extracted from the cgroup, used by CGroupParser (below).\nstruct CGroupInfo\n{\n  std::string container_id;\n  std::string runtime;\n  std::string pod_id;\n  std::string qos;\n  std::string service;\n\n  // that is, if parsing failed\n  bool valid = false;\n};\n\n// This class parses a cgroup name, attempting to extract information that\n// is later useful: the container id, pod id, service, etc.\n// It supports both cgroupfs and systemd cgroups.\n//\n// To use, pass the cgroup's name through the constructor, and obtain the\n// results through the get() method.\nclass CGroupParser\n{\npublic:\n  explicit CGroupParser(std::string_view cgroup_name);\n  \n  const CGroupInfo& get() const { return info_; }\n  std::string_view cgroup_name() const { return cgroup_name_; }\n\nprivate:\n  bool parse_cgroup();\n  bool parse_systemd();\n  bool parse_cri();\n  bool parse_pod_id();\n  bool parse_container_id();\n  bool parse_qos();\n  bool parse_uid();\n  bool parse_uid_group(size_t count);\n  bool parse_runtime(char token);\n  bool parse_service();\n\n  CGroupInfo info_;\n  std::string_view cgroup_name_;\n  typename std::string_view::const_iterator read_head_;\n};\n"
  },
  {
    "path": "util/cgroup_parser_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/cgroup_parser.h>\n\n\nTEST(cgroup_parser, systemd_happy_path)\n{\n  CGroupParser c(\"kubepods-burstable-pod146bb920_a47b_4f6c_a69a_166b63944d15.slice:cri-containerd:c45f3e9c19746eabf0a4af63d780ba5c2a657a7352c7ad7acc5d599da5115eef\");\n  EXPECT_TRUE(c.get().valid);\n  EXPECT_EQ(c.get().qos, \"burstable\");\n  EXPECT_EQ(c.get().pod_id, \"146bb920-a47b-4f6c-a69a-166b63944d15\");\n  EXPECT_EQ(c.get().runtime, \"containerd\");\n  EXPECT_EQ(c.get().container_id, \"c45f3e9c19746eabf0a4af63d780ba5c2a657a7352c7ad7acc5d599da5115eef\");\n  EXPECT_EQ(c.get().service, \"\");\n\n  CGroupParser c2(\"kubepods-besteffort-pod745edc0d_4e07_49ef_ba5d_576d66791bfb.slice\");\n  EXPECT_TRUE(c2.get().valid);\n  EXPECT_EQ(c2.get().qos, \"besteffort\");\n  EXPECT_EQ(c2.get().pod_id, \"745edc0d-4e07-49ef-ba5d-576d66791bfb\");\n  EXPECT_EQ(c2.get().runtime, \"\");\n  EXPECT_EQ(c2.get().container_id, \"\");\n  EXPECT_EQ(c2.get().service, \"\");\n\n  CGroupParser c3(\"kubepods-besteffort.slice\");\n  EXPECT_TRUE(c3.get().valid);\n  EXPECT_EQ(c3.get().qos, \"besteffort\");\n  EXPECT_EQ(c3.get().pod_id, \"\");\n  EXPECT_EQ(c3.get().runtime, \"\");\n  EXPECT_EQ(c3.get().container_id, \"\");\n  EXPECT_EQ(c3.get().service, \"\");\n}\n\nTEST(cgroup_parser, cri_happy_path)\n{\n  CGroupParser c(\"cri-containerd-6f652f89943b50f7b101d13f11371daf34bf836b7e1b725b5e8b6439451018bd.scope\");\n  EXPECT_TRUE(c.get().valid);\n  EXPECT_EQ(c.get().qos, \"\");\n  EXPECT_EQ(c.get().pod_id, \"\");\n  EXPECT_EQ(c.get().runtime, \"containerd\");\n  EXPECT_EQ(c.get().container_id, \"6f652f89943b50f7b101d13f11371daf34bf836b7e1b725b5e8b6439451018bd\");\n  EXPECT_EQ(c.get().service, \"\");\n}\n\nTEST(cgroup_parser, pod_happy_path)\n{\n  CGroupParser c(\"pod146bb920_a47b_4f6c_a69a_166b63944d15\");\n  EXPECT_TRUE(c.get().valid);\n  EXPECT_EQ(c.get().qos, \"\");\n  EXPECT_EQ(c.get().pod_id, \"146bb920-a47b-4f6c-a69a-166b63944d15\");\n  EXPECT_EQ(c.get().runtime, \"\");\n  EXPECT_EQ(c.get().container_id, \"\");\n  EXPECT_EQ(c.get().service, \"\");\n\n  CGroupParser c2(\"pod146bb920a47b4f6ca69a166b63944d15\");\n  EXPECT_TRUE(c2.get().valid);\n  EXPECT_EQ(c2.get().qos, \"\");\n  EXPECT_EQ(c2.get().pod_id, \"146bb920-a47b-4f6c-a69a-166b63944d15\");\n  EXPECT_EQ(c2.get().runtime, \"\");\n  EXPECT_EQ(c2.get().container_id, \"\");\n  EXPECT_EQ(c2.get().service, \"\");\n\n  CGroupParser c3(\"pod146bb920-a47b-4f6c-a69a-166b63944d15\");\n  EXPECT_TRUE(c3.get().valid);\n  EXPECT_EQ(c3.get().qos, \"\");\n  EXPECT_EQ(c3.get().pod_id, \"146bb920-a47b-4f6c-a69a-166b63944d15\");\n  EXPECT_EQ(c3.get().runtime, \"\");\n  EXPECT_EQ(c3.get().container_id, \"\");\n  EXPECT_EQ(c3.get().service, \"\");\n}\n\nTEST(cgroup_parser, container_id_happy_path)\n{\n  CGroupParser c(\"6f652f89943b50f7b101d13f11371daf34bf836b7e1b725b5e8b6439451018bd\");\n  EXPECT_TRUE(c.get().valid);\n  EXPECT_EQ(c.get().qos, \"\");\n  EXPECT_EQ(c.get().pod_id, \"\");\n  EXPECT_EQ(c.get().runtime, \"\");\n  EXPECT_EQ(c.get().container_id, \"6f652f89943b50f7b101d13f11371daf34bf836b7e1b725b5e8b6439451018bd\");\n  EXPECT_EQ(c.get().service, \"\");\n}\n\nTEST(cgroup_parser, service_happy_path)\n{\n  CGroupParser c(\"systemd-journald.service\");\n  EXPECT_TRUE(c.get().valid);\n  EXPECT_EQ(c.get().qos, \"\");\n  EXPECT_EQ(c.get().pod_id, \"\");\n  EXPECT_EQ(c.get().runtime, \"\");\n  EXPECT_EQ(c.get().container_id, \"\");\n  EXPECT_EQ(c.get().service, \"systemd-journald\");\n}\n\nTEST(cgroup_parser, others)\n{\n  CGroupParser c1(\"junk\");\n  EXPECT_FALSE(c1.get().valid);\n  EXPECT_EQ(c1.get().qos, \"\");\n  EXPECT_EQ(c1.get().pod_id, \"\");\n  EXPECT_EQ(c1.get().runtime, \"\");\n  EXPECT_EQ(c1.get().container_id, \"\");\n  EXPECT_EQ(c1.get().service, \"\");\n\n  CGroupParser c2(\"kubepods\");\n  EXPECT_FALSE(c2.get().valid);\n  EXPECT_EQ(c2.get().qos, \"\");\n  EXPECT_EQ(c2.get().pod_id, \"\");\n  EXPECT_EQ(c2.get().runtime, \"\");\n  EXPECT_EQ(c2.get().container_id, \"\");\n  EXPECT_EQ(c2.get().service, \"\");\n\n  CGroupParser c3(\"kubepods.slice\");\n  EXPECT_FALSE(c3.get().valid);\n  EXPECT_EQ(c3.get().qos, \"\");\n  EXPECT_EQ(c3.get().pod_id, \"\");\n  EXPECT_EQ(c3.get().runtime, \"\");\n  EXPECT_EQ(c3.get().container_id, \"\");\n  EXPECT_EQ(c3.get().service, \"\");\n\n  // all hex - starts down container_id parsing, but not 64 chars\n  CGroupParser c4(\"1234abcd\");\n  EXPECT_FALSE(c4.get().valid);\n  EXPECT_EQ(c4.get().qos, \"\");\n  EXPECT_EQ(c4.get().pod_id, \"\");\n  EXPECT_EQ(c4.get().runtime, \"\");\n  EXPECT_EQ(c4.get().container_id, \"\");\n  EXPECT_EQ(c4.get().service, \"\");\n}"
  },
  {
    "path": "util/circular_queue.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * circular_queue.h\n */\n\n#ifndef CIRCULAR_QUEUE_H\n#define CIRCULAR_QUEUE_H\n\n#include <platform/platform.h>\n\n/**\n * @param head: index of the first item in the queue\n * @param tail: index following the last item in the queue\n * @param mask: the number of allowed elements (which is a power of two) minus 1\n * @param elems: pointer to element buffer\n */\nstruct circular_queue {\n  uint32_t head;\n  uint32_t tail;\n  uint32_t mask;\n  void *elems;\n};\n\n/**\n * Creates a new packet queue with capacity @num_elems.\n * @important: @num_elems must be a power of two\n * @param elems: a buffer large enough to hold num_elems elements\n * @returns: 0 on success,\n *           -EINVAL if num_elems is not a power of two\n */\nstatic inline int cq_init(struct circular_queue *q, void *elems, uint32_t num_elems)\n{\n  assert(q != NULL);\n\n  if ((num_elems & (num_elems - 1)) != 0)\n    return -EINVAL;\n\n  q->head = 0;\n  q->tail = 0;\n  q->mask = num_elems - 1;\n  q->elems = elems;\n\n  return 0;\n}\n\n/**\n * Enqueues @x at the end of @q.\n * @assumes there is enough space in q. (user can check with cq_full())\n */\n#define cq_enqueue(name, type)                                                                                                 \\\n  static inline void cq_##name##_enqueue(struct circular_queue *q, type x)                                                     \\\n  {                                                                                                                            \\\n    uint32_t tail;                                                                                                             \\\n                                                                                                                               \\\n    assert(q != NULL);                                                                                                         \\\n                                                                                                                               \\\n    tail = q->tail;                                                                                                            \\\n    ((type *)q->elems)[tail & q->mask] = x;                                                                                    \\\n    barrier();                                                                                                                 \\\n    ACCESS_ONCE(q->tail) = tail + 1;                                                                                           \\\n  }\n\n/**\n * Dequeues an element from @q.\n * @assumes q is non-empty (user can check with cq_empty())\n */\n#define cq_dequeue(name, type)                                                                                                 \\\n  static inline type cq_##name##_dequeue(struct circular_queue *q)                                                             \\\n  {                                                                                                                            \\\n    uint32_t head;                                                                                                             \\\n    type retval;                                                                                                               \\\n                                                                                                                               \\\n    assert(q != NULL);                                                                                                         \\\n                                                                                                                               \\\n    head = q->head;                                                                                                            \\\n    retval = ((type *)q->elems)[head & q->mask];                                                                               \\\n    barrier();                                                                                                                 \\\n    ACCESS_ONCE(q->head) = head + 1;                                                                                           \\\n    return retval;                                                                                                             \\\n  }\n\n/**\n * Returns the head element from @q without dequeuing it.\n * @assumes q is non-empty (user can check with cq_empty())\n */\n#define cq_peek(name, type)                                                                                                    \\\n  static inline type cq_##name##_peek(struct circular_queue *q)                                                                \\\n  {                                                                                                                            \\\n    assert(q != NULL);                                                                                                         \\\n    return ((type *)q->elems)[q->head & q->mask];                                                                              \\\n  }\n\n/**\n * Returns 1 if @q is empty, 0 otherwise.\n */\nstatic inline int cq_empty(struct circular_queue *q)\n{\n  assert(q != NULL);\n  return (q->tail == q->head);\n}\n\n/**\n * Returns the current occupancy of the queue.\n * (to be used by the consumer)\n */\nstatic inline uint32_t cq_occupancy(struct circular_queue *q)\n{\n  assert(q != NULL);\n  return q->tail - q->head;\n}\n\n/**\n * Returns the number of free slots in the queue\n * (to be used by the producer)\n */\nstatic inline uint32_t cq_space(struct circular_queue *q)\n{\n  assert(q != NULL);\n  return q->mask + 1 - (q->tail - q->head);\n}\n\n/**\n * Returns 1 if @q is full, 0 otherwise.\n */\nstatic inline int cq_full(struct circular_queue *q)\n{\n  return (cq_occupancy(q) >= q->mask + 1);\n}\n\n/**\n * Discards 'n_elem' elements from the head of the queue\n */\nstatic inline void cq_discard(struct circular_queue *q, uint32_t n_elems)\n{\n  assert(q != NULL);\n  assert(n_elems <= cq_occupancy(q));\n\n  ACCESS_ONCE(q->head) = q->head + n_elems;\n}\n\n/**\n * Macro to define functions for different types.\n *   For example,\n *      using_circular_queue(u32, uint32_t);\n *   will define functions\n *      void cq_u32_enqueue(struct circular_queue *q, uint32_t x);\n *      uint32_t cq_u32_dequeue(struct circular_queue *q);\n *   etc.\n */\n#define using_circular_queue(name, type)                                                                                       \\\n  cq_enqueue(name, type);                                                                                                      \\\n  cq_dequeue(name, type);                                                                                                      \\\n  cq_peek(name, type);\n\nusing_circular_queue(u16, u16);\nusing_circular_queue(u32, u32);\nusing_circular_queue(u64, u64);\n\n#endif /* CIRCULAR_QUEUE_H */\n"
  },
  {
    "path": "util/circular_queue_cpp.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <stdexcept>\n\ntemplate <typename T> class CircularQueue {\npublic:\n  using value_type = T;\n\n  /**\n   * Creates a new circular queue with capacity @num_elems.\n   * @important: @num_elems must be a power of two\n   * @param elems: a buffer large enough to hold num_elems elements of type T\n   * @throws runtime_error if num_elems is not a power of two\n   */\n  CircularQueue(value_type *elems, uint32_t num_elems) : head_(0), tail_(0), mask_(num_elems - 1), elems_(elems)\n  {\n    if ((num_elems & (num_elems - 1)) != 0)\n      throw std::runtime_error(\"num_elems must be a power of two\");\n  }\n\n  /**\n   * Enqueues @x at the end of @q.\n   * @assumes there is enough space in q. (user can check with full())\n   */\n  void enqueue(value_type x)\n  {\n    uint32_t tail = tail_;\n    elems_[tail & mask_] = x;\n    barrier();\n    ACCESS_ONCE(tail_) = tail + 1;\n  }\n\n  /**\n   * Dequeues an element from @q.\n   * @assumes q is non-empty (user can check with empty())\n   */\n  value_type dequeue()\n  {\n    uint32_t head = head_;\n    value_type retval;\n\n    retval = elems_[head & mask_];\n    barrier();\n    ACCESS_ONCE(head_) = head + 1;\n    return retval;\n  }\n\n  /**\n   * Returns the head element from @q without dequeuing it.\n   * @assumes q is non-empty (user can check with empty())\n   */\n  value_type peek() { return elems_[head_ & mask_]; }\n\n  /**\n   * Returns true if @q is empty, false otherwise.\n   */\n  bool empty() { return (tail_ == head_); }\n\n  /**\n   * Returns the current occupancy of the queue.\n   * (to be used by the consumer)\n   */\n  uint32_t occupancy() { return tail_ - head_; }\n\n  /**\n   * Returns the number of free slots in the queue\n   * (to be used by the producer)\n   */\n  uint32_t space() { return mask_ + 1 - (tail_ - head_); }\n\n  /**\n   * Returns true if @q is full, false otherwise.\n   */\n  int full() { return (occupancy() >= mask_ + 1); }\n\n  /**\n   * Discards 'n_elem' elements from the head of the queue\n   */\n  void discard(uint32_t n_elems)\n  {\n    assert(n_elems <= occupancy());\n\n    ACCESS_ONCE(head_) = head_ + n_elems;\n  }\n\nprivate:\n  /* index of the first item in the queue */\n  uint32_t head_;\n  /* index following the last item in the queue */\n  uint32_t tail_;\n  /* the number of allowed elements (which is a power of two) minus 1 */\n  const uint32_t mask_;\n  /* pointer to element buffer */\n  value_type *elems_;\n};\n"
  },
  {
    "path": "util/code_timing.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/code_timing.h>\n#include <util/log.h>\n\n#include <filesystem>\n\n#if ENABLE_CODE_TIMING\n\nu64 CodeTiming::next_index_ = 0;\n\nCodeTiming::CodeTiming(std::string const &name, std::string const &filename, int line)\n    : name_(name),\n      filename_(std::filesystem::path(filename).filename().string()),\n      line_(line),\n      index_(next_index_++),\n      full_name_(fmt::format(\"{}:{}:{}:{}\", name_, filename_, line_, index_))\n{\n  code_timing_registry_.register_code_timing(full_name_, this);\n}\n\nCodeTiming::~CodeTiming()\n{\n  code_timing_registry_.unregister_code_timing(full_name_);\n}\n\nvoid CodeTiming::set(u64 duration_ns)\n{\n  gauge_ += duration_ns;\n}\n\nvoid CodeTiming::visit(VisitCallback func)\n{\n  func(name_, filename_, line_, index_, gauge_);\n}\n\nvoid CodeTiming::print()\n{\n  LOG::info(\"  {} [{}:{} ({})] {}\", name_, filename_, line_, index_, *this);\n}\n\nstd::ostream &operator<<(std::ostream &os, CodeTiming const &timing)\n{\n  os << \"{\"\n     << \"count=\" << timing.gauge_.count() << \" avg=\" << timing.gauge_.average<u64>() << \" min=\" << timing.gauge_.min()\n     << \" max=\" << timing.gauge_.max() << \" sum=\" << timing.gauge_.sum() << '}';\n\n  return os;\n}\n\nthread_local CodeTimingRegistry code_timing_registry_;\n\nvoid print_code_timings()\n{\n  code_timing_registry_.print();\n}\n\nvoid CodeTimingRegistry::register_code_timing(std::string const &name, CodeTiming *timing)\n{\n  code_timings_[name] = timing;\n}\n\nvoid CodeTimingRegistry::unregister_code_timing(std::string const &name)\n{\n  code_timings_.erase(name);\n}\n\nvoid CodeTimingRegistry::visit(CodeTiming::VisitCallback func)\n{\n  for (auto const &[name, timing] : code_timings_) {\n    timing->visit(func);\n  }\n}\n\nvoid CodeTimingRegistry::print()\n{\n  LOG::info(\"CodeTimings for thread id hash=0x{:x}:\", std::hash<std::thread::id>{}(std::this_thread::get_id()));\n  for (auto const &[name, timing] : code_timings_) {\n    timing->print();\n  }\n}\n\n#endif // ENABLE_CODE_TIMING\n"
  },
  {
    "path": "util/code_timing.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <config.h>\n\n#include <util/defer.h>\n#include <util/gauge.h>\n#include <util/preprocessor.h>\n#include <util/stop_watch.h>\n\n#include <map>\n#include <string>\n#include <sstream>\n#include <spdlog/fmt/fmt.h>\n\n#if ENABLE_CODE_TIMING\n\nclass CodeTiming {\n  friend std::ostream &operator<<(std::ostream &os, CodeTiming const &timing);\n\npublic:\n  using VisitCallback = std::function<void(std::string_view, std::string_view, int, u64, data::Gauge<u64> &)>;\n\n  CodeTiming(std::string const &name, std::string const &filename, int line);\n  ~CodeTiming();\n\n  void set(u64 duration_ns);\n\n  void visit(VisitCallback func);\n\n  void print();\n\nprivate:\n  const std::string name_;\n  const std::string filename_;\n  const int line_;\n  static u64 next_index_;\n  const u64 index_; // to make sure CodeTimings in templated functions have a unique key for the CodeTimingRegistry\n  const std::string full_name_;\n  data::Gauge<u64> gauge_;\n};\n\nclass CodeTimingRegistry {\npublic:\n  // Register a CodeTiming.\n  void register_code_timing(std::string const &name, CodeTiming *timing);\n\n  // Unregister a CodeTiming.\n  void unregister_code_timing(std::string const &name);\n\n  void visit(CodeTiming::VisitCallback func);\n\n  // Prints all registered code timings out via LOG::info().\n  void print();\n\nprivate:\n  std::map<std::string, CodeTiming *> code_timings_;\n};\n\nextern thread_local CodeTimingRegistry code_timing_registry_;\nextern void print_code_timings();\n\n#define SCOPED_TIMING(name)                                                                                                    \\\n  static thread_local CodeTiming timing_##name(#name, __FILE__, __LINE__);                                                     \\\n  StopWatch sw_##name;                                                                                                         \\\n  DEFER([&] { timing_##name.set(sw_##name.elapsed_ns()); });\n\n#define START_TIMING(name)                                                                                                     \\\n  static thread_local CodeTiming timing_##name(#name, __FILE__, __LINE__);                                                     \\\n  StopWatch sw_##name;\n\n#define STOP_TIMING(name) timing_##name.set(sw_##name.elapsed_ns());\n\n#else // ENABLE_CODE_TIMING\n\n#define SCOPED_TIMING(name)\n#define START_TIMING(name)\n#define STOP_TIMING(name)\n\n#endif // ENABLE_CODE_TIMING\n\n// Provide fmt support for CodeTiming using its ostream representation when enabled.\n#if ENABLE_CODE_TIMING\nnamespace fmt {\ntemplate <> struct formatter<CodeTiming> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n  template <typename FormatContext> auto format(CodeTiming const &ct, FormatContext &ctx) const\n  {\n    std::ostringstream os;\n    os << ct;\n    return fmt::format_to(ctx.out(), \"{}\", os.str());\n  }\n};\n} // namespace fmt\n#endif\n"
  },
  {
    "path": "util/common_test.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/log.h>\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\n#include <cstdlib>\n#include <string>\n\nclass CommonTest : public ::testing::Test {\npublic:\n  virtual void SetUp() override\n  {\n    LOG::init(true); // log to console\n\n    // Default to debug logging\n    spdlog::set_level(spdlog::level::debug);\n    // Override level with SPDLOG_LEVEL env var if set\n    spdlog::cfg::load_env_levels();\n  }\n};\n"
  },
  {
    "path": "util/container_of.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * C++-compatible container-of implementation.\n *\n * Inspired by https://stackoverflow.com/a/40851139\n */\n\n#pragma once\n\n#include <cstddef>\n\ntemplate <class P, class M> std::size_t fp_offsetof(const M P::*member)\n{\n  return (std::size_t) & (reinterpret_cast<P *>(0)->*member);\n}\n\ntemplate <class P, class M> P *fp_container_of(M *ptr, const M P::*member)\n{\n  return (P *)((char *)ptr - fp_offsetof(member));\n}\n"
  },
  {
    "path": "util/counter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <optional>\n#include <utility>\n\n#include <cassert>\n\nnamespace data {\n\ntemplate <typename T> struct Counter {\n  Counter() = default;\n  Counter(Counter const &) = default;\n  Counter(Counter &&) = default;\n\n  Counter(T data) : value_(std::move(data)) {}\n\n  Counter &operator+=(T const &value)\n  {\n    value_ = value;\n    return *this;\n  }\n\n  Counter &operator+=(T &&value)\n  {\n    value_ = std::move(value);\n    return *this;\n  }\n\n  T const &value() const\n  {\n    assert(value_.has_value());\n    return *value_;\n  }\n\n  T const *try_value() const { return value_.has_value() ? &*value_ : nullptr; }\n\n  void reset() { value_.reset(); }\n  bool empty() const { return !value_.has_value(); }\n\n  T const *operator->() const { return &value(); }\n  T const &operator*() const { return value(); }\n\nprivate:\n  std::optional<T> value_ = {};\n};\n\n} // namespace data\n\n#include <util/counter.inl>\n"
  },
  {
    "path": "util/counter.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\nnamespace data {\n\ntemplate <typename Out, typename T> Out &&operator<<(Out &&out, Counter<T> const &what)\n{\n  if (what.empty()) {\n    out << \"{no_value}\";\n  } else {\n    out << \"{value=\" << what.value() << '}';\n  }\n  return std::forward<Out>(out);\n}\n\n} // namespace data\n"
  },
  {
    "path": "util/counter_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/counter.h>\n\n#include <platform/types.h>\n\n#include <gtest/gtest.h>\n\n#include <chrono>\n\nnamespace data {\n\nTEST(counter_int, default_ctor)\n{\n  Counter<int> const counter;\n  EXPECT_TRUE(counter.empty());\n  EXPECT_EQ(nullptr, counter.try_value());\n}\n\nTEST(counter_int, cast_constructor)\n{\n  Counter<int> const counter(10);\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(10, counter.value());\n  EXPECT_EQ(10, *counter.try_value());\n}\n\nTEST(counter_int, default_ctor_add_4_values)\n{\n  Counter<int> counter;\n  counter += 10;\n  counter += 20;\n  counter += 30;\n  counter += 40;\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(40, counter.value());\n  EXPECT_EQ(40, *counter.try_value());\n}\n\nTEST(counter_int, cast_ctor_add_4_values)\n{\n  Counter<int> counter(60);\n  counter += 90;\n  counter += 70;\n  counter += 50;\n  counter += 80;\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(80, counter.value());\n  EXPECT_EQ(80, *counter.try_value());\n}\n\nTEST(counter_int, reset)\n{\n  Counter<int> counter(60);\n  counter += 90;\n  counter += 70;\n  counter += 50;\n  counter += 80;\n\n  counter.reset();\n  EXPECT_TRUE(counter.empty());\n  EXPECT_EQ(nullptr, counter.try_value());\n}\n\nTEST(counter_chrono, default_ctor)\n{\n  Counter<std::chrono::seconds> const counter;\n  EXPECT_TRUE(counter.empty());\n  EXPECT_EQ(nullptr, counter.try_value());\n}\n\nTEST(counter_chrono, cast_constructor)\n{\n  Counter<std::chrono::seconds> const counter(10s);\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(10s, counter.value());\n  EXPECT_EQ(10s, *counter.try_value());\n}\n\nTEST(counter_chrono, default_ctor_add_4_values)\n{\n  Counter<std::chrono::seconds> counter;\n  counter += 10s;\n  counter += 20s;\n  counter += 30s;\n  counter += 40s;\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(40s, counter.value());\n  EXPECT_EQ(40s, *counter.try_value());\n}\n\nTEST(counter_chrono, cast_ctor_add_4_values)\n{\n  Counter<std::chrono::seconds> counter(60s);\n  counter += 90s;\n  counter += 70s;\n  counter += 50s;\n  counter += 80s;\n  EXPECT_FALSE(counter.empty());\n  EXPECT_EQ(80s, counter.value());\n  EXPECT_EQ(80s, *counter.try_value());\n}\n\nTEST(counter_chrono, reset)\n{\n  Counter<std::chrono::seconds> counter(60s);\n  counter += 90s;\n  counter += 70s;\n  counter += 50s;\n  counter += 80s;\n\n  counter.reset();\n  EXPECT_TRUE(counter.empty());\n  EXPECT_EQ(nullptr, counter.try_value());\n}\n\n} // namespace data\n"
  },
  {
    "path": "util/curl_engine.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/curl_engine.h>\n#include <util/utility.h>\n\n#include <stdexcept>\n#include <unordered_set>\n\n#include <util/log.h>\n\nnamespace {\n\nstatic constexpr size_t kWriteBufferCapacity = CURL_MAX_WRITE_SIZE;\n\n// The global atomic flag to make sure curl_global_init() is called once\n// and only once.\nstd::atomic_flag has_curl_global_init = ATOMIC_FLAG_INIT;\n\n// Forward define.\nstatic void on_uv_poll(uv_poll_t *poll_handle, int status, int events);\n\n// Implements CurlEngine interface.\nclass CurlEngineImpl : public CurlEngine {\npublic:\n  explicit CurlEngineImpl(uv_loop_t *loop);\n  ~CurlEngineImpl() override;\n\n  CurlEngineStatus schedule_fetch(FetchRequest &request) override;\n  CurlEngineStatus cancel_fetch(FetchRequest &request) override;\n\n  // Functions used by libuv & libcurl callbacks.\n  void start_timer(u64 timeout_ms);\n  void stop_timer();\n  void on_timeout();\n\n  void start_poll(curl_socket_t socket_fd, int events);\n  void remove_poll(uv_poll_t *poll_handle);\n  void on_poll(uv_poll_t *poll_handle, int flags);\n\nprivate:\n  void handle_curl_info_queue();\n\n  size_t num_active_fetches_ = 0;\n  uv_loop_t *loop_;\n  CURLM *curl_handle_;\n  uv_timer_t timer_;\n};\n} // namespace\n\nCurlEngine::FetchRequest::FetchRequest(std::string target, DataAvailableFn available_fn, FetchDoneFn done_fn)\n    : easy_handle_(curl_easy_init()),\n      target_(std::move(target)),\n      available_fn_(std::move(available_fn)),\n      done_fn_(std::move(done_fn))\n{}\n\nCurlEngine::FetchRequest::~FetchRequest()\n{\n  // curl cleanup functions are no-op when the handle is null\n  if (easy_handle_) {\n    curl_easy_cleanup(easy_handle_);\n  }\n  if (headers_) {\n    curl_slist_free_all(headers_);\n  }\n}\n\nbool CurlEngine::FetchRequest::add_header(char const *header)\n{\n  if (auto head = curl_slist_append(headers_, header)) {\n    headers_ = head;\n    return true;\n  } else {\n    LOG::error(\"Cannot insert HTTP header to libcurl request.\");\n    return false;\n  }\n}\n\nvoid CurlEngine::FetchRequest::http_proxy(std::string host, std::uint16_t port)\n{\n  proxy_ = std::move(host);\n  proxy_port_ = port;\n  proxy_type_ = CURLPROXY_HTTP;\n}\n\n// curl_write() is a callback passed to libcurl, which is invoked when\n// server returns data.\nsize_t CurlEngine::FetchRequest::curl_write(char *ptr, size_t size, size_t nmemb, void *userdata)\n{\n  LOG::trace_in(Utility::curl, \"{}(): {}\", __func__, size * nmemb);\n\n  auto *request = (CurlEngine::FetchRequest *)userdata;\n\n  // See https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html\n  // |size| is actually always 1, but handle it anyway.\n  size_t incoming_size = size * nmemb;\n  request->available_fn_(ptr, incoming_size);\n\n  return incoming_size;\n}\n\n// |unix_socket|: if not empty, the unix socket the engine should talk to.\nvoid CurlEngine::FetchRequest::unix_socket(std::string_view path)\n{\n  unix_socket_ = std::string(path);\n}\n\nvoid CurlEngine::FetchRequest::debug_mode(bool debug)\n{\n  curl_easy_setopt(easy_handle_, CURLOPT_VERBOSE, static_cast<long>(debug));\n}\n\nvoid CurlEngine::FetchRequest::done(CurlEngineStatus status, bool success, std::string_view error)\n{\n  long response_code = -1;\n  if (success && !get_info(CURLINFO_RESPONSE_CODE, response_code)) {\n    error = \"fetch successful, but unable to fetch response code\";\n  }\n\n  done_fn_(status, response_code, error);\n}\n\nCURL *CurlEngine::FetchRequest::prepare()\n{\n  // Note: unfortunately libcurl does not validate the URL.\n  // This function call always returns OK.\n  // See: https://curl.haxx.se/libcurl/c/CURLOPT_URL.html\n  set_option(CURLOPT_URL, target_.c_str());\n  set_option(CURLOPT_PRIVATE, this);\n  if (headers_) {\n    set_option(CURLOPT_HTTPHEADER, headers_);\n  }\n\n  // Curstomized write callback.\n  // https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html\n  set_option(CURLOPT_WRITEFUNCTION, curl_write);\n  set_option(CURLOPT_WRITEDATA, this);\n\n  // Uses Unix domain socket for communication\n  // https://curl.haxx.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html\n  if (!unix_socket_.empty()) {\n    set_option(CURLOPT_UNIX_SOCKET_PATH, unix_socket_.c_str());\n  }\n\n  if (!proxy_.empty()) {\n    set_option(CURLOPT_PROXY, proxy_.c_str());\n    set_option(CURLOPT_PROXYPORT, static_cast<long>(proxy_port_));\n    set_option(CURLOPT_PROXYTYPE, proxy_type_);\n  }\n\n  // Holds any error message from libcurl.\n  set_option(CURLOPT_ERRORBUFFER, error_message_);\n\n  return easy_handle_;\n}\n\nstd::unique_ptr<CurlEngine> CurlEngine::create(uv_loop_t *loop)\n{\n  std::unique_ptr<CurlEngine> engine(new CurlEngineImpl(loop));\n  return engine;\n}\n\nnamespace {\n// curl_start_timer() is a callback passed to libcurl. Libcurl uses it to\n// inform libuv to start or stop timer.\nstatic int curl_start_timer(CURLM *multi, long timeout_ms, void *userp)\n{\n  LOG::trace_in(Utility::curl, \"curl_start_timer(): {}ms\", timeout_ms);\n\n  CurlEngineImpl *engine = (CurlEngineImpl *)userp;\n  if (timeout_ms < 0) {\n    engine->stop_timer();\n  } else {\n    // timeout_ms ==0 means we can invoke the timer callback right away.\n    // But we will run it via the uv_loop moment later.\n    engine->start_timer(timeout_ms);\n  }\n  return 0;\n}\n\n// curl_handle_socket() is a callback passed to libcurl. Libcurl uses it to\n// signal that a socket_fd has become available, or is distroyed, libuv should\n// start, or remove, a poll routine.\nstatic int curl_handle_socket(CURL *easy, curl_socket_t socket_fd, int action, void *userp, void *socketp)\n{\n  LOG::trace_in(Utility::curl, \"curl_handle_socket(), action: {}\", action);\n\n  CurlEngineImpl *engine = (CurlEngineImpl *)userp;\n  uv_poll_t *poll_handle = (uv_poll_t *)(socketp);\n\n  int events = 0;\n\n  switch (action) {\n  case CURL_POLL_IN:\n  case CURL_POLL_INOUT:\n  case CURL_POLL_OUT:\n    if (action != CURL_POLL_IN) {\n      events |= UV_WRITABLE;\n    }\n\n    if (action != CURL_POLL_OUT) {\n      events |= UV_READABLE;\n    }\n    if (poll_handle == nullptr) {\n      // Start a fresh poll.\n      engine->start_poll(socket_fd, events);\n    } else {\n      // Call uv_poll_start() again to update the event masks.\n      int res = uv_poll_start(poll_handle, events, on_uv_poll);\n      if (res != 0) {\n        LOG::error(\"Cannot start poll. {}\", uv_err_name(res));\n        return 0;\n      }\n    }\n    break;\n  case CURL_POLL_REMOVE:\n    assert(poll_handle != nullptr);\n    engine->remove_poll(poll_handle);\n    break;\n  case CURL_POLL_NONE:\n    break;\n  default:\n    LOG::warn(\"Unexpected socket action {}\", action);\n  }\n\n  return 0;\n}\n\n// on_uv_timeout() is a callback from libuv to signal the timer has fired.\nstatic void on_uv_timeout(uv_timer_t *timer)\n{\n  LOG::trace_in(Utility::curl, \"on_uv_timeout()\");\n  auto *engine = (CurlEngineImpl *)timer->data;\n  engine->on_timeout();\n}\n\n// on_uv_poll() is a callback from libuv to signal the socket_fd status changes.\nstatic void on_uv_poll(uv_poll_t *poll_handle, int status, int events)\n{\n  int flags = 0;\n\n  if (events & UV_READABLE) {\n    flags |= CURL_CSELECT_IN;\n  }\n  if (events & UV_WRITABLE) {\n    flags |= CURL_CSELECT_OUT;\n  }\n\n  auto *engine = (CurlEngineImpl *)poll_handle->data;\n  engine->on_poll(poll_handle, flags);\n}\n\nCurlEngineImpl::CurlEngineImpl(uv_loop_t *loop) : loop_(loop)\n{\n  if (loop == nullptr) {\n    LOG::error(\"libuv loop is not specified.\");\n    throw std::runtime_error(\"libuv loop is not specified.\");\n    return;\n  }\n\n  if (!has_curl_global_init.test_and_set()) {\n    if (curl_global_init(CURL_GLOBAL_ALL)) {\n      LOG::error(\"Could not init curl\");\n      throw std::runtime_error(\"Could not init curl.\");\n      return;\n    }\n\n    if (atexit(curl_global_cleanup) != 0) {\n      LOG::error(\"Could not setup curl_global_cleanup.\");\n      throw std::runtime_error(\"Cloud not setup curl_global_cleanup.\");\n      return;\n    }\n  }\n\n  int res = uv_timer_init(loop_, &timer_);\n  if (res != 0) {\n    LOG::error(\"CurlEngineImpl: Cannot set up timer: {}\", uv_err_name(res));\n    throw std::runtime_error(\"Cannot set up timer.\");\n    return;\n  }\n  timer_.data = this;\n\n  curl_handle_ = curl_multi_init();\n  curl_multi_setopt(curl_handle_, CURLMOPT_SOCKETFUNCTION, curl_handle_socket);\n  curl_multi_setopt(curl_handle_, CURLMOPT_SOCKETDATA, (void *)this);\n\n  curl_multi_setopt(curl_handle_, CURLMOPT_TIMERFUNCTION, curl_start_timer);\n  curl_multi_setopt(curl_handle_, CURLMOPT_TIMERDATA, (void *)this);\n}\n\nCurlEngineImpl::~CurlEngineImpl()\n{\n  if (curl_handle_ == nullptr) {\n    return;\n  }\n\n  if (num_active_fetches_ != 0) {\n    LOG::error(\"{} on-going fetches when the CurlEngine is destroyed\", num_active_fetches_);\n    assert(num_active_fetches_ == 0); // trigger assert on debug\n    return;\n  }\n\n  curl_multi_cleanup(curl_handle_);\n}\n\n// Handles the timer event.\nvoid CurlEngineImpl::on_timeout()\n{\n  int running_handles = 0;\n  auto mcode = curl_multi_socket_action(curl_handle_, CURL_SOCKET_TIMEOUT, 0, &running_handles);\n  if (mcode != CURLM_OK) {\n    LOG::error(\"Cannot trigger CURL_SOCKET_TIMEOUT: {}\", curl_multi_strerror(mcode));\n    throw std::runtime_error(\"Cannot trigger CURL_SOCKET_TIMEOUT.\");\n    return;\n  }\n\n  handle_curl_info_queue();\n\n  // TODO: we should check if any long running transfers here.\n  //       Returns timeout status to the client.\n}\n\n// Handles the poll event.\nvoid CurlEngineImpl::on_poll(uv_poll_t *poll_handle, int flags)\n{\n  int running_handles = 0;\n  int fd = 0;\n  int res = uv_fileno((const uv_handle_t *)poll_handle, &fd);\n  if (res != 0) {\n    LOG::error(\"Cannot extract fd from poll handle. {}\", uv_err_name(res));\n    throw std::runtime_error(\"extract fd from poll handle.\");\n    return;\n  }\n\n  auto mcode = curl_multi_socket_action(curl_handle_, fd, flags, &running_handles);\n\n  if (mcode != CURLM_OK) {\n    LOG::error(\"Cannot trigger IN/OUT event: {}\", curl_multi_strerror(mcode));\n    throw std::runtime_error(\"Cannot trigger IN/OUT event\");\n    return;\n  }\n  handle_curl_info_queue();\n}\n\n// Drains and handles messages from Curl info queue.\n// Dispatches a callback to client when the transfer is done.\nvoid CurlEngineImpl::handle_curl_info_queue()\n{\n  int pending = 0;\n  CURLMsg *message = nullptr;\n  while ((message = curl_multi_info_read(curl_handle_, &pending)) != nullptr) {\n    if (message->msg != CURLMSG_DONE) {\n      LOG::warn(\"Unknown curl message {}\", static_cast<int>(message->msg));\n      continue;\n    }\n\n    CURL *easy_handle = message->easy_handle;\n    CurlEngineImpl::FetchRequest *request = nullptr;\n    curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &request);\n    assert(request != nullptr);\n\n    auto status = CurlEngineStatus::OK;\n    std::string_view error_message;\n    if (message->data.result == CURLE_OK) {\n      LOG::trace_in(Utility::curl, \"Successfully fetch from {}\", request->target());\n      error_message = \"successfull fetch\";\n    } else {\n      LOG::debug_in(Utility::curl, \"Fail to fetch from {}, {}\", request->target(), request->error_message());\n      status = CurlEngineStatus::ERROR;\n      error_message = request->error_message();\n    }\n    // Clean up before signal\n    assert(num_active_fetches_ != 0);\n    num_active_fetches_--;\n\n    if (auto mcode = curl_multi_remove_handle(curl_handle_, easy_handle); mcode != CURLM_OK) {\n      LOG::error(\"Cannot remove a handle: {}\", curl_multi_strerror(mcode));\n      throw std::runtime_error(\"Cannot remove a handle.\");\n      return;\n    }\n\n    request->done(status, message->data.result == CURLE_OK, error_message);\n  }\n}\n\nCurlEngineStatus CurlEngineImpl::schedule_fetch(FetchRequest &request)\n{\n  LOG::trace_in(Utility::curl, \"Schedule a fetch: {}\", request.target());\n\n  if (auto mcode = curl_multi_add_handle(curl_handle_, request.prepare()); mcode != CURLM_OK) {\n    LOG::error(\"Cannot schedule fetch: {}\", curl_multi_strerror(mcode));\n    request.done(CurlEngineStatus::SCHEDULE_ERROR, false, curl_multi_strerror(mcode));\n    return CurlEngineStatus::ERROR;\n  }\n\n  num_active_fetches_++;\n  return CurlEngineStatus::OK;\n}\n\nCurlEngineStatus CurlEngineImpl::cancel_fetch(FetchRequest &request)\n{\n  assert(num_active_fetches_ != 0);\n\n  LOG::trace_in(Utility::curl, \"Cancel a fetch: {}\", request.target());\n  num_active_fetches_--;\n\n  if (auto mcode = curl_multi_remove_handle(curl_handle_, *request); mcode != CURLM_OK) {\n    LOG::error(\"Cannot cancel a fetch: {}\", curl_multi_strerror(mcode));\n    return CurlEngineStatus::ERROR;\n  }\n\n  request.done(CurlEngineStatus::CANCELED, false, \"request cancelled\");\n  return CurlEngineStatus::OK;\n}\n\nvoid CurlEngineImpl::start_timer(u64 timeout_ms)\n{\n  stop_timer();\n  uv_timer_start(&timer_, on_uv_timeout, timeout_ms, 0);\n}\n\nvoid CurlEngineImpl::stop_timer()\n{\n  uv_timer_stop(&timer_);\n}\n\nvoid CurlEngineImpl::start_poll(curl_socket_t socket_fd, int events)\n{\n  LOG::trace_in(Utility::curl, \"start_poll(), events: {}\", events);\n  std::unique_ptr<uv_poll_t> poll_handle(new uv_poll_t);\n\n  int res = uv_poll_init(loop_, poll_handle.get(), socket_fd);\n  if (res != 0) {\n    LOG::error(\"Cannot init poll handle. {}\", uv_err_name(res));\n    throw std::runtime_error(\"Cannot init poll handle.\");\n    return;\n  }\n  poll_handle->data = this;\n\n  res = uv_poll_start(poll_handle.get(), events, on_uv_poll);\n  if (res != 0) {\n    LOG::error(\"Cannot start poll. {}\", uv_err_name(res));\n    throw std::runtime_error(\"Cannot start poll.\");\n    return;\n  }\n\n  auto mcode = curl_multi_assign(curl_handle_, socket_fd, poll_handle.release());\n  if (mcode != CURLM_OK) {\n    LOG::error(\"Cannot add easy handle\", curl_multi_strerror(mcode));\n    throw std::runtime_error(\"Cannot add easy handle.\");\n    return;\n  }\n}\n\nvoid CurlEngineImpl::remove_poll(uv_poll_t *poll_handle)\n{\n  LOG::trace_in(Utility::curl, \"remove_poll()\");\n\n  uv_close((uv_handle_t *)poll_handle, [](uv_handle_t *handle) { delete (uv_poll_t *)handle; });\n}\n\n} // namespace\n"
  },
  {
    "path": "util/curl_engine.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <util/enum.h>\n\n#include <curl/curl.h>\n#include <uv.h>\n\n#include <functional>\n#include <initializer_list>\n#include <memory>\n#include <string_view>\n\n#define ENUM_NAME CurlEngineStatus\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(OK, 0, \"\")                                                                                                                 \\\n  X(TIMEOUT, 1, \"\")                                                                                                            \\\n  X(ERROR, 2, \"\")                                                                                                              \\\n  X(CANCELED, 3, \"\")                                                                                                           \\\n  X(SCHEDULE_ERROR, 4, \"\")\n#define ENUM_DEFAULT ERROR\n#include <util/enum_operators.inl>\n\n// CurlEngine utilizes libcurl's multi socket API on top of libuv event loop to\n// fetch data from HTTP endpoints.\n//\n// It is expected that both CurlEngine and its clients are running on top of\n// same libuv loop.\nclass CurlEngine {\npublic:\n  CurlEngine() = default;\n  virtual ~CurlEngine() = default;\n\n  // Factory function to create CurlEngine.\n  // The caller take the ownership of the returned value.\n  static std::unique_ptr<CurlEngine> create(uv_loop_t *loop);\n\n  // Callback function to be executed when some data has been fetched\n  // and is available to consumed.\n  //\n  // This callback might be invoked multiple times, as more data is returned.\n  //\n  // |data|: returned data. The calllback does not take the ownership of |data|\n  // |data_length|: number of bytes of |data|.\n  //\n  // CurlEngine might overwrite |data| after this callback is invoked. Thus,\n  // it's client's responsiblity to make a copy of |data| if that's needed.\n  using DataAvailableFn = std::function<void(const char *data, size_t data_length)>;\n\n  // Callback function to be  executed when fetch finishes.\n  // |status|: the status of the fetch.\n  // |resonseCode|: the response code of the fetch, or -1 if not available.\n  //\n  // It is possible that DataAvailableFn is invoked multiple times,\n  // before a FetchDoneFn is invoked with status != CurlEngineStatus::OK.\n  //\n  // The associated FetchRequest is unregistered from the CurlEngine before\n  // this callback is invoked. The client is free to clean up the FetchRequest.\n  // curlError.data() is only set when status != OK, and can be used as a\n  // null-terminated string.\n  using FetchDoneFn = std::function<void(CurlEngineStatus status, long responseCode, std::string_view curlError)>;\n\n  struct FetchRequest {\n    // |target|:  the target URL we are going to fetch.\n    // |available_fn|: callback when data is available.\n    // |done_fn|: callback when fetch finishes.\n    FetchRequest(std::string target, DataAvailableFn available_fn, FetchDoneFn done_fn);\n\n    ~FetchRequest();\n\n    template <typename T> bool try_set_option(CURLoption option, T &&value)\n    {\n      return curl_easy_setopt(easy_handle_, option, std::forward<T>(value)) == CURLE_OK;\n    }\n\n    template <typename T> FetchRequest &set_option(CURLoption option, T &&value)\n    {\n      try_set_option(option, std::forward<T>(value));\n      return *this;\n    }\n\n    template <typename T> T *get_info(CURLINFO info, T &fallback)\n    {\n      return curl_easy_getinfo(easy_handle_, info, &fallback) == CURLE_OK ? &fallback : nullptr;\n    }\n\n    // adds the header in the request\n    bool add_header(char const *header);\n\n    void http_proxy(std::string host, std::uint16_t port);\n\n    // send the request through a UNIX socket\n    void unix_socket(std::string_view path);\n    void debug_mode(bool debug);\n\n    std::string_view target() { return target_; }\n    std::string_view error_message() { return error_message_; }\n\n    void done(CurlEngineStatus status, bool success, std::string_view error);\n\n    CURL const *operator*() const { return easy_handle_; }\n    CURL *operator*() { return easy_handle_; }\n\n    bool operator!() const { return !easy_handle_; }\n    explicit operator bool() const { return static_cast<bool>(easy_handle_); }\n\n    CURL *prepare();\n\n  private:\n    CURL *easy_handle_ = nullptr;\n    curl_slist *headers_ = nullptr;\n    std::string target_;\n    DataAvailableFn available_fn_;\n    FetchDoneFn done_fn_;\n\n    std::string unix_socket_;\n\n    std::string proxy_;\n    std::uint16_t proxy_port_;\n    curl_proxytype proxy_type_;\n\n    char error_message_[CURL_ERROR_SIZE] = {0};\n\n    static size_t curl_write(char *ptr, size_t size, size_t nmemb, void *userdata);\n  };\n\n  // Schedules to fetch.\n  // |request|: fetch request, it should be created by CreateFetchRequest().\n  //\n  // The done_fn callback from |request| is invoked immedidate if error occurs.\n  // The CurlEngine does not take ownership of |request|.\n  //\n  virtual CurlEngineStatus schedule_fetch(FetchRequest &request) = 0;\n\n  // Stops and cancels the ongoing fetch.\n  //\n  //\n  // The FetchDoneFn callback with be invoked with CurlEngineStatus::CANCELED status.\n  //\n  // Client must explicitly cancel the FetchRequest, (or waits until the\n  // request finishes), before destroy the FeetchRequest object.\n  //\n  // It will be a no-op if the request has not been scheduled by the CurlEngine,\n  // or it is scheduled and finishes (FetchDoneFn callback has been invoked).\n  virtual CurlEngineStatus cancel_fetch(FetchRequest &request) = 0;\n};\n"
  },
  {
    "path": "util/debug.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string_view>\n\nconstexpr std::string_view release_mode_string =\n#ifdef NDEBUG\n    \"release\"\n#else  // NDEBUG\n    \"debug\"\n#endif // NDEBUG\n    ;\n\nconstexpr bool is_release_mode =\n#ifdef NDEBUG\n    true\n#else  // NDEBUG\n    false\n#endif // NDEBUG\n    ;\n"
  },
  {
    "path": "util/defer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/preprocessor.h>\n\n#include <functional>\n\n// Defer is a convenience class for executing a function upon leaving scope\n// (similar to what Go's `defer` statement does). Saves the effort of creating\n// a custom RAII-based class for simple tasks.\n//\n// Example usage:\n//\n// void do_something() {\n//   void* data = malloc(1234);\n//   Defer free_data([&] { free(data); });\n//   ...\n// }\nclass Defer {\npublic:\n  explicit Defer(std::function<void()> cb) : cb_(std::move(cb)) {}\n  ~Defer() { cb_(); }\n\n  // Move-only.\n  Defer(const Defer &) = delete;\n  Defer(Defer &&) = default;\n\nprivate:\n  std::function<void()> cb_;\n};\n\n// DEFER is a convenience macro to declare a Defer class and associated callback function.\n//\n// Example usage:\n//\n// void do_something() {\n//   void* data = malloc(1234);\n//   DEFER([&] { free(data); });\n//   ...\n// }\n#define DEFER(cb) Defer PREPROC_CONCAT(defer_, __LINE__)(cb)\n"
  },
  {
    "path": "util/defer_test.cc",
    "content": "//\n// Copyright 2021 Splunk Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <util/defer.h>\n\n#include <platform/types.h>\n\n#include <gtest/gtest.h>\n\n#include <chrono>\n\n#include <util/common_test.h>\n\nnamespace data {\n\n  bool call;\n\n  void testCallback(){\n    call = true;\n}\n\nclass DeferTest : public CommonTest {\n  protected:\n  void SetUp(){ call = false; }\n\n//   static bool call;\n\n//   static void testCallback(){\n//     DeferTest::call = true;\n// }\n};\n\nTEST_F(DeferTest, object)\n{\n  {\n    Defer defer(testCallback);\n    EXPECT_FALSE(call);\n  }\n  EXPECT_TRUE(call);\n}\n\n\n} // namespace data"
  },
  {
    "path": "util/docker_host_config_metadata.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/docker_host_config_metadata.h>\n\n#include <util/json.h>\n\nnamespace {\nstatic std::string const CPU_SHARES = \"CpuShares\";\nstatic std::string const CPU_PERIOD = \"CpuPeriod\";\nstatic std::string const CPU_QUOTA = \"CpuQuota\";\n\nstatic std::string const MEMORY_LIMIT = \"Memory\";\nstatic std::string const MEMORY_SOFT_LIMIT = \"MemoryReservation\";\nstatic std::string const TOTAL_MEMORY_LIMIT = \"MemorySwap\";\nstatic std::string const MEMORY_SWAPPINESS = \"MemorySwappiness\";\n} // namespace\n\nDockerHostConfigMetadata::DockerHostConfigMetadata(nlohmann::json const &host_config)\n    : cpu_shares_(try_get_int<decltype(cpu_shares_)>(host_config, CPU_SHARES).value_or(0)),\n      cpu_period_(try_get_int<decltype(cpu_period_)>(host_config, CPU_PERIOD).value_or(0)),\n      cpu_quota_(try_get_int<decltype(cpu_quota_)>(host_config, CPU_QUOTA).value_or(0)),\n      memory_swappiness_(try_get_int<decltype(memory_swappiness_)>(host_config, MEMORY_LIMIT).value_or(0)),\n      memory_limit_(try_get_int<decltype(memory_limit_)>(host_config, MEMORY_SOFT_LIMIT).value_or(0)),\n      memory_soft_limit_(try_get_int<decltype(memory_soft_limit_)>(host_config, TOTAL_MEMORY_LIMIT).value_or(0)),\n      total_memory_limit_(try_get_int<decltype(total_memory_limit_)>(host_config, MEMORY_SWAPPINESS).value_or(0))\n{}\n\nDockerHostConfigMetadata::operator bool() const\n{\n  return cpu_shares_ || cpu_period_ || cpu_quota_ || memory_swappiness_ || memory_limit_ || memory_soft_limit_ ||\n         total_memory_limit_;\n}\n"
  },
  {
    "path": "util/docker_host_config_metadata.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <nlohmann/json.hpp>\n\n#include <cstdint>\n\n/**\n * Reads metadata posted by Kubernetes to the Docker engine.\n */\nclass DockerHostConfigMetadata {\npublic:\n  // `host_config` is the `.HostConfig` section from the equivalent of `docker inspect`\n  explicit DockerHostConfigMetadata(nlohmann::json const &host_config);\n\n  auto cpu_shares() const { return cpu_shares_; }\n  auto cpu_period() const { return cpu_period_; }\n  auto cpu_quota() const { return cpu_quota_; }\n\n  auto memory_swappiness() const { return memory_swappiness_; }\n  auto memory_limit() const { return memory_limit_; }\n  auto memory_soft_limit() const { return memory_soft_limit_; }\n  auto total_memory_limit() const { return total_memory_limit_; }\n\n  explicit operator bool() const;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, DockerHostConfigMetadata const &what);\n\nprivate:\n  std::uint16_t cpu_shares_;\n  std::int32_t cpu_period_;\n  std::int32_t cpu_quota_;\n\n  std::uint8_t memory_swappiness_;\n  std::uint64_t memory_limit_;\n  std::uint64_t memory_soft_limit_;\n  std::int64_t total_memory_limit_; // including swap\n};\n\n#include <util/docker_host_config_metadata.inl>\n"
  },
  {
    "path": "util/docker_host_config_metadata.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\ntemplate <typename Out> Out &&operator<<(Out &&out, DockerHostConfigMetadata const &what)\n{\n  out << \"\\\"cpu_shares\\\":\" << what.cpu_shares_ << \",\\\"cpu_period\\\":\" << what.cpu_period_ << \",\\\"cpu_quota\\\":\" << what.cpu_quota_\n      << \",\\\"memory_swappiness\\\":\" << static_cast<std::uint16_t>(what.memory_swappiness_)\n      << \",\\\"memory_limit\\\":\" << what.memory_limit_ << \",\\\"memory_soft_limit\\\":\" << what.memory_soft_limit_\n      << \",\\\"total_memory_limit\\\":\" << what.total_memory_limit_;\n\n  return std::forward<Out>(out);\n}\n"
  },
  {
    "path": "util/element_queue.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <util/element_queue.h>\n\n/**\n * Internal helper, calculates where the next read/write should be based on\n *    the buffer pointer and buffer length.\n */\nstatic inline u32 __eq_next_offset_by_len(u32 buf_offset, u32 buf_mask, u32 len)\n{\n  /* if writing the buffer will wraps around, we start at buffer's beginning */\n  if ((int)(((buf_offset + len - 1) & ~buf_mask) - (buf_offset & ~buf_mask)) > 0)\n    buf_offset = (buf_offset + len - 1) & ~buf_mask;\n\n  return buf_offset;\n}\n\nvoid eq_init_shared(struct element_queue_shared *shared)\n{\n  shared->elem_head = 0;\n  shared->elem_tail = 0;\n  shared->buf_head = 0;\n  shared->buf_tail = 0;\n}\n\nint eq_init_contig(struct element_queue *eq, u32 n_elems, u32 buf_len, void *data)\n{\n  if ((eq == NULL) || (data == NULL))\n    return -EINVAL;\n  if (n_elems & (n_elems - 1))\n    return -EINVAL;\n  if (buf_len & (buf_len - 1))\n    return -EINVAL;\n\n  eq->shared = (struct element_queue_shared *)data;\n  eq->elems = (u32 *)&eq->shared[1];\n  eq->data = (char *)&eq->elems[n_elems];\n\n  eq->elem_mask = n_elems - 1;\n  eq->buf_mask = buf_len - 1;\n\n  eq->elem_head = eq->shared->elem_head;\n  eq->elem_tail = eq->shared->elem_tail;\n  eq->buf_head = eq->shared->buf_head;\n  eq->buf_tail = eq->shared->buf_tail;\n\n  return 0;\n}\n\nu32 eq_contig_size(u32 n_elems, u32 buf_len)\n{\n  struct element_queue_shared *shared = (struct element_queue_shared *)0;\n  u32 *elems = (u32 *)&shared[1];\n  char *data = (char *)&elems[n_elems];\n  return (u32)(&data[buf_len] - (char *)0);\n}\n\nint eq_write(struct element_queue *eq, u32 len)\n{\n  u32 buf_tail;\n  u32 buf_mask;\n  u32 aligned_len = (len + 7) & ~7;\n\n  /* check input parameters */\n  if (eq == NULL)\n    return -EINVAL;\n  if (aligned_len > eq->buf_mask)\n    return -EINVAL;\n\n  /* is the element queue full? */\n  if (eq->elem_tail - eq->elem_head >= eq->elem_mask + 1)\n    return -ENOSPC;\n\n  buf_mask = eq->buf_mask;\n  buf_tail = __eq_next_offset_by_len(eq->buf_tail, buf_mask, aligned_len);\n\n  /* is there enough space in the eq? */\n  if (buf_tail + aligned_len - eq->buf_head > buf_mask + 1)\n    return -ENOSPC;\n\n  /* okay we're good to go */\n  eq->buf_tail = buf_tail + aligned_len;\n  eq->elems[(eq->elem_tail++) & eq->elem_mask] = len;\n  return (buf_tail & buf_mask);\n}\n\nint eq_peek(struct element_queue *eq)\n{\n  /* is the element queue empty? */\n  if (eq->elem_tail == eq->elem_head)\n    return -ENOENT;\n\n  return eq->elems[eq->elem_head & eq->elem_mask];\n}\n\nint eq_peek_offset(struct element_queue *eq, u32 *lenp)\n{\n  u32 len;\n  u32 aligned_len;\n  u32 offset;\n\n  /* is the element queue empty? */\n  if (eq->elem_tail == eq->elem_head) {\n    return -ENOENT;\n  }\n\n  len = eq->elems[eq->elem_head & eq->elem_mask];\n  aligned_len = (len + 7) & ~7;\n  offset = __eq_next_offset_by_len(eq->buf_head, eq->buf_mask, aligned_len);\n\n  if (lenp != NULL) {\n    *lenp = len;\n  }\n\n  return (offset & eq->buf_mask);\n}\n\nint eq_read(struct element_queue *eq, u32 *lenp)\n{\n  u32 offset;\n  u32 buf_mask;\n  u32 aligned_len;\n\n  /* check input parameters */\n  if ((eq == NULL) || (lenp == NULL))\n    return -EINVAL;\n\n  /* is the element queue empty? */\n  if (eq->elem_tail == eq->elem_head)\n    return -EAGAIN;\n\n  *lenp = eq->elems[(eq->elem_head++) & eq->elem_mask];\n  aligned_len = (*lenp + 7) & ~7;\n  buf_mask = eq->buf_mask;\n  offset = __eq_next_offset_by_len(eq->buf_head, buf_mask, aligned_len);\n  eq->buf_head = offset + aligned_len;\n\n  return (offset & buf_mask);\n}\n\nint eq_move(struct element_queue *to, struct element_queue *from)\n{\n  int read_offset;\n  int write_offset;\n  u32 len = 0;\n  int peek_len;\n\n  /* how big is this message */\n  peek_len = eq_peek(from);\n  if (peek_len < 0)\n    return -ENOENT;\n\n  /* try to get a write buffer of that size */\n  write_offset = eq_write(to, peek_len);\n  if (write_offset == -ENOSPC)\n    return -ENOSPC;\n  else if (write_offset < 0) {\n    assert(0);\n    return -ENOSYS; /* unexpected error! */\n  }\n\n  /* make the read */\n  read_offset = eq_read(from, &len);\n  if ((read_offset < 0) || (len != peek_len)) {\n    assert(0);\n    return -ENOSYS; /* unexpected error! */\n  }\n\n  /* copy packet from transmission queue to retrans queue */\n  memcpy(&to->data[write_offset], &from->data[read_offset], len);\n\n  return len;\n}\n"
  },
  {
    "path": "util/element_queue.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_H_\n#define INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_H_\n\n#include <platform/platform.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct element_queue_shared {\n  u32 elem_head;\n  u32 buf_head;\n  u32 elem_tail;\n  u32 buf_tail;\n};\n\n/**\n * A queue structure conveying discrete elements of variable length.\n *\n * Constraints:\n *  - all readers must share the same struct\n *  - all writers must share the same struct\n *  - readers can use a separate struct from writers\n *  - all reads must be surrounded by start_read_batch and finish_read_batch\n *  - all writes must be surrounded by start_write_batch and finish_read_batch\n *\n * @param buf_head: index of the next unread char in the eq\n * @param buf_tail: index following the eq's last written char\n */\nstruct element_queue {\n  u32 elem_mask;\n  u32 buf_mask;\n\n  u32 elem_head;\n  u32 buf_head;\n  u32 elem_tail;\n  u32 buf_tail;\n\n  struct element_queue_shared *shared;\n  u32 *elems;\n  char *data;\n};\n\n/**\n * Initializes a struct element_queue_shared\n */\nvoid eq_init_shared(struct element_queue_shared *shared);\n\n/**\n * Initializes the element queue using a contiguous memory area\n *\n * @param eq: the element_queue to initialize\n * @param n_elems: number of elements in shared buffer. Must be power of 2.\n * @param buf_len: number of bytes in shared buffer. Must be power of 2.\n * @param data: shared memory area\n *\n * Memory layout of eq data:\n *   element_queue_shared\n *   u32 elem[n_elems]\n *   char buf[buf_len]\n *\n * @returns 0 on success,\n *   -EINVAL on NULL pointers, non-power-of 2 sizes.\n */\nint eq_init_contig(struct element_queue *eq, u32 n_elems, u32 buf_len, void *data);\n\n/**\n * Returns the size, in bytes, of a contiguous element queue\n */\nu32 eq_contig_size(u32 n_elems, u32 buf_len);\n\n/**\n * Starts a write batch\n * @param eq: the eq to write to\n *\n * @important: the user must call finish_write after finishing the batch. The\n *   consumer can only see results after a batch is finished\n */\nstatic inline void eq_start_write_batch(struct element_queue *eq)\n{\n  eq->elem_head = ACCESS_ONCE(eq->shared->elem_head);\n  eq->buf_head = ACCESS_ONCE(eq->shared->buf_head);\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n  assert((int)eq->elem_tail - eq->elem_head >= 0);\n}\n\n/**\n * Get a buffer where data can be written to the eq\n * @param eq: the eq to write to\n * @param len: number of bytes we want to write\n *\n * @return: offset in eq where data should be written\n *   -EINVAL if trying to write more than the eq size or passed NULL pointer\n *   -ENOSPC if eq too full\n */\nint eq_write(struct element_queue *eq, u32 len);\n\n/**\n * Finishes a batch write to the element_queue\n * @param eq: the element_queue written to\n *\n * @assumes a successful start_write_batch\n */\nstatic inline void eq_finish_write_batch(struct element_queue *eq)\n{\n  /* make sure items have been committed before writing the tails */\n  smp_wmb();\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n  assert((int)eq->elem_tail - eq->elem_head >= 0);\n\n  eq->shared->buf_tail = eq->buf_tail;\n  eq->shared->elem_tail = eq->elem_tail;\n}\n\n/**\n * Starts a read batch\n * @param eq: the element_queue to read from\n *\n * @important: after reading the data, the user must call finish_read_batch\n */\nstatic inline void eq_start_read_batch(struct element_queue *eq)\n{\n  /* we don't need to read buf_tail because we trust that the sizes in the\n   * element-size array do not overflow the element_queue */\n\n  eq->elem_tail = ACCESS_ONCE(eq->shared->elem_tail);\n  smp_rmb();\n\n  assert((int)eq->elem_tail - eq->elem_head >= 0);\n}\n\n/**\n * Reads the next element's size, or -ENOENT if no element exists\n */\nint eq_peek(struct element_queue *eq);\n\n/**\n * Reads the next element's offset into data, or -ENOENT if no element exists\n * @param lenp: [out] if not NULL, where to store the element length\n */\nint eq_peek_offset(struct element_queue *eq, u32 *lenp);\n\n/**\n * Read data from a element_queue\n * @param eq: the element_queue to read from\n * @param lenp: [out] length of the buffer to read\n *\n * @return: on success, offset into the data where to read;\n *   -EINVAL if passed NULL pointers,\n *   -EAGAIN if eq is empty\n */\nint eq_read(struct element_queue *eq, u32 *lenp);\n\n/**\n * Finish the read batch, freeing space for producer\n * @param eq: the element_queue to read from\n */\nstatic inline void eq_finish_read_batch(struct element_queue *eq)\n{\n  smp_mb();\n\n  eq->shared->buf_head = eq->buf_head;\n  eq->shared->elem_head = eq->elem_head;\n\n  assert((int)eq->elem_tail - eq->elem_head >= 0);\n}\n\n/**\n * Moves one element from a source element queue to destination element queue.\n * @param to: the destination element queue\n * @param from: the source element queue\n *\n * @returns: number of bytes moved on success,\n *   -ENOENT if source is empty,\n *   -ENOSPC if destination does not have enough space,\n *   -ENOSYS on enexpected error.\n */\nint eq_move(struct element_queue *to, struct element_queue *from);\n\nstatic inline u32 eq_elem_count(const struct element_queue *eq)\n{\n  return eq->elem_tail - eq->elem_head;\n}\n\nstatic inline u32 eq_elem_capacity(const struct element_queue *eq)\n{\n  return eq->elem_mask + 1;\n}\n\nstatic inline u32 eq_buf_used(const struct element_queue *eq)\n{\n  return eq->buf_tail - eq->buf_head;\n}\n\nstatic inline u32 eq_buf_capacity(const struct element_queue *eq)\n{\n  return eq->buf_mask + 1;\n}\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_H_ */\n"
  },
  {
    "path": "util/element_queue_cpp.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_CPP_H_\n#define INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_CPP_H_\n\n#include <memory>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <util/element_queue.h>\n\nclass ElementQueue;\n\n/**\n * All the shared data for a memory-contiguous element queue\n */\nclass ElementQueueStorage {\npublic:\n  /**\n   * C'tor\n   * @param n_elems: the number of members in the circular queue holding\n   *   element sizes. Must be a power of 2\n   * @param buf_len: the number of bytes that can hold element data. Must be\n   *   a power of 2.\n   */\n  ElementQueueStorage(u32 n_elems, u32 buf_len) : n_elems_(n_elems), buf_len_(buf_len), data_(NULL) {}\n\n  virtual ~ElementQueueStorage() {}\n\n  u32 n_elems() { return n_elems_; }\n  u32 buf_len() { return buf_len_; }\n  char *data() { return data_; }\n\nprotected:\n  u32 n_elems_;\n  u32 buf_len_;\n  char *data_;\n};\n\ntypedef std::shared_ptr<ElementQueueStorage> ElementQueueStoragePtr;\n\n/**\n * All the shared data for a memory-contiguous element queue\n */\nclass MemElementQueueStorage : public ElementQueueStorage {\npublic:\n  /**\n   * C'tor\n   * @param n_elems: the number of members in the circular queue holding\n   *   element sizes. Must be a power of 2\n   * @param buf_len: the number of bytes that can hold element data. Must be\n   *   a power of 2.\n   */\n  MemElementQueueStorage(u32 n_elems, u32 buf_len);\n\n  virtual ~MemElementQueueStorage();\n};\n\nclass ElementQueue : public element_queue {\npublic:\n  ElementQueue(const ElementQueueStoragePtr &storage);\n\n  /* @see eq_start_write_batch */\n  void start_write_batch();\n\n  /**\n   * Writes the given string to the element queue\n   *\n   * @returns 0 on success, negative on error\n   * @see eq_write for more info\n   */\n  int write(const std::string &elem);\n\n  /**\n   * Writes the contents of the given stringstream to the element queue\n   *\n   * @returns 0 on success, negative on error\n   * @see eq_write for more info\n   */\n  int write(std::stringstream &ss);\n\n  /* @see eq_finish_write_batch */\n  void finish_write_batch();\n\n  /* @see eq_start_read_batch */\n  void start_read_batch();\n\n  /* @see eq_peek */\n  int peek();\n\n  /* @see eq_peek_offset */\n  int peek(char *&output);\n\n  /**\n   * Peeks a value of the specified type.\n   * @returns the size of that value on success;\n   *   -ENOENT if no element exists;\n   *   -EINVAL if there is not enough data for that type\n   */\n  template <typename T> int peek_value(T &output);\n\n  /**\n   * Reads an element from the element queue.\n   * @return an element on success. throws otherwise\n   *\n   * @see eq_read for more info\n   */\n  std::string read();\n\n  /**\n   * Reads an element from the element queue\n   *\n   * @returns number of bytes to read on success, like eq_read on error\n   * @param output: [out] a pointer to the read buffer.\n   *\n   * @assumes: length of element can fit in an int (<= 1 << 31). there is an\n   *   assert for this, but not a runtime check (when NDEBUG is not set)\n   */\n  int read(char *&output);\n\n  /* @see eq_finish_read_batch */\n  void finish_read_batch();\n\n  /**\n   * Moves an element from @from to this\n   * @see eq_move\n   * @returns see eq_move\n   */\n  int move_from(ElementQueue *from);\n\n  /**\n   * Gets the number of elements in the queue.\n   */\n  u32 elem_count() const;\n\n  /**\n   * Gets the element capacity of the queue.\n   */\n  u32 elem_capacity() const;\n\n  /**\n   * Gets the number of bytes used in the buffer.\n   */\n  u32 buf_used() const;\n\n  /**\n   * Gets the buffer capacity.\n   */\n  u32 buf_capacity() const;\n\nprotected:\n  /* the underlying storage backing the element queue */\n  ElementQueueStoragePtr storage_;\n};\n\n/*****************\n * IMPLEMENTATION\n *****************/\n\ninline MemElementQueueStorage::MemElementQueueStorage(u32 n_elems, u32 buf_len) : ElementQueueStorage(n_elems, buf_len)\n{\n  u32 size = eq_contig_size(n_elems, buf_len);\n\n  /* allocate contig memory */\n  data_ = (char *)malloc(size);\n  if (data_ == NULL)\n    throw std::runtime_error(\"Unable to allocate memory for element queue\");\n\n  /* zero it out */\n  memset(data_, 0, size);\n\n  /* Initialize shared structures */\n  eq_init_shared((element_queue_shared *)data_);\n}\n\ninline MemElementQueueStorage::~MemElementQueueStorage()\n{\n  if (data_)\n    free(data_);\n}\n\ninline ElementQueue::ElementQueue(const ElementQueueStoragePtr &storage) : storage_(storage)\n{\n  int res = eq_init_contig(this, storage->n_elems(), storage->buf_len(), storage->data());\n  if (res != 0)\n    throw std::runtime_error(\"eq_init_contig failed\");\n}\n\ninline void ElementQueue::start_write_batch()\n{\n  eq_start_write_batch(this);\n}\n\ninline int ElementQueue::write(const std::string &elem)\n{\n  int offset = eq_write(this, elem.length());\n  if (offset < 0)\n    return offset;\n\n  /* if we reached here, can write the element to offset */\n  memcpy(data + offset, elem.data(), elem.length());\n\n  return 0;\n}\n\ninline int ElementQueue::write(std::stringstream &ss)\n{\n  u32 len = ss.tellp();\n  int offset = eq_write(this, len);\n  if (offset < 0)\n    return offset;\n\n  ss.read(data + offset, len);\n  return 0;\n}\n\ninline void ElementQueue::finish_write_batch()\n{\n  eq_finish_write_batch(this);\n}\n\ninline void ElementQueue::start_read_batch()\n{\n  eq_start_read_batch(this);\n}\n\ninline int ElementQueue::peek()\n{\n  return eq_peek(this);\n}\n\ninline int ElementQueue::peek(char *&output)\n{\n  u32 len;\n  int offset = eq_peek_offset(this, &len);\n\n  if (offset < 0) {\n    return offset;\n  }\n\n  output = data + offset;\n  return len;\n}\n\ntemplate <typename T> int ElementQueue::peek_value(T &output)\n{\n  u32 len;\n  int offset = eq_peek_offset(this, &len);\n\n  if (offset < 0) {\n    return offset;\n  }\n\n  if (len < sizeof(T)) {\n    return -EINVAL;\n  }\n\n  output = *reinterpret_cast<T *>(data + offset);\n  return len;\n}\n\ninline std::string ElementQueue::read()\n{\n  u32 len;\n\n  int offset = eq_read(this, &len);\n  if (offset == -EINVAL)\n    throw std::invalid_argument(\"eq_read returned -EINVAL\");\n  if (offset == -EAGAIN)\n    throw std::out_of_range(\"queue empty\");\n  if (offset < 0)\n    throw std::runtime_error(\"unexpected return value\");\n\n  /* can copy data into string */\n  return std::string(data + offset, len);\n}\n\ninline int ElementQueue::read(char *&output)\n{\n  u32 len;\n  int offset = eq_read(this, &len);\n\n  if (offset < 0)\n    return offset;\n\n  assert((u32)((int)len) == len);\n  output = data + offset;\n  return len;\n}\n\ninline void ElementQueue::finish_read_batch()\n{\n  eq_finish_read_batch(this);\n}\n\ninline int ElementQueue::move_from(ElementQueue *from)\n{\n  return eq_move(this, from);\n}\n\ninline u32 ElementQueue::elem_count() const\n{\n  return eq_elem_count(this);\n}\n\ninline u32 ElementQueue::elem_capacity() const\n{\n  return eq_elem_capacity(this);\n}\n\ninline u32 ElementQueue::buf_used() const\n{\n  return eq_buf_used(this);\n}\n\ninline u32 ElementQueue::buf_capacity() const\n{\n  return eq_buf_capacity(this);\n}\n\n#endif /* INCLUDE_FASTPASS_UTIL_ELEMENT_QUEUE_CPP_H_ */\n"
  },
  {
    "path": "util/element_queue_writer.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"element_queue_writer.h\"\n\n#include <util/log.h>\n\n#include <unistd.h>\n\nnamespace {\n\nstatic constexpr size_t RETRY_BACKOFF_RATIO = 2;\nstatic constexpr size_t RETRY_BACKOFF_LIMIT = 512;\nstatic constexpr useconds_t RETRY_INTERVAL = 1000; // in microseconds\n\n} // namespace\n\nElementQueueWriter::ElementQueueWriter(ElementQueue &queue) : queue_(queue) {}\n\nElementQueueWriter::~ElementQueueWriter() {}\n\nExpected<u8 *, std::error_code> ElementQueueWriter::start_write(u32 length)\n{\n  queue_.start_write_batch();\n\n  size_t backoff = 1;\n  int offset = -EINVAL;\n\n  do {\n    offset = eq_write(&queue_, length);\n\n    if (offset == -ENOSPC) {\n      // sleep util there's space to write\n      queue_.finish_write_batch();\n      usleep(backoff * RETRY_INTERVAL);\n      queue_.start_write_batch();\n\n      // increase backoff geometrically\n      backoff *= RETRY_BACKOFF_RATIO;\n\n      if (backoff > RETRY_BACKOFF_LIMIT) {\n        // clamp to limit and log a warning\n        backoff = RETRY_BACKOFF_LIMIT;\n        LOG::warn(\"ElementQueueWriter: queue full, backing off\");\n      }\n\n      ++num_write_stalls_;\n    }\n  } while (offset == -ENOSPC);\n\n  if (offset < 0) {\n    return {unexpected, -offset, std::generic_category()};\n  }\n\n  return reinterpret_cast<u8 *>(queue_.data + offset);\n}\n\nvoid ElementQueueWriter::finish_write()\n{\n  queue_.finish_write_batch();\n}\n\nstd::error_code ElementQueueWriter::flush()\n{\n  // TODO\n  return {};\n}\n\nu32 ElementQueueWriter::buf_size() const\n{\n  // TODO\n  return 0;\n}\n"
  },
  {
    "path": "util/element_queue_writer.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <channel/ibuffered_writer.h>\n#include <platform/platform.h>\n#include <util/element_queue_cpp.h>\n\n// Adapter class for writing to ElementQueues through the\n// IBufferedWriter interface.\n//\nclass ElementQueueWriter : public IBufferedWriter {\npublic:\n  ElementQueueWriter(ElementQueue &queue);\n  virtual ~ElementQueueWriter();\n\n  // Starts a write operation of size \\p length.\n  //\n  // Will block until there is enough space in the queue for the write.\n  //\n  // Returns the memory where caller should write the data, or nullptr in case\n  // of an error.\n  //\n  Expected<u8 *, std::error_code> start_write(u32 length) override;\n\n  void finish_write() override;\n\n  std::error_code flush() override;\n\n  u32 buf_size() const override;\n\n  // Number of times writing has stalled because of no space in the queue.\n  u64 num_write_stalls() const { return num_write_stalls_; }\n\n  // Queue this writer is writing to.\n  ElementQueue const &queue() const { return queue_; }\n\n  bool is_writable() const override { return true; }\n\nprivate:\n  ElementQueue &queue_;\n  u64 num_write_stalls_{0};\n};\n"
  },
  {
    "path": "util/enum.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/bits.h>\n#include <util/enum.h>\n\n#include <algorithm>\n#include <array>\n#include <initializer_list>\n#include <list>\n#include <string>\n#include <string_view>\n#include <type_traits>\n\n#include <cstdint>\n\ntemplate <typename Enum> struct enum_traits;\n\ntemplate <typename Which>\nvoid parse_enum_list(std::list<std::string> &name_list, void (*callback_list)(std::list<Which> enum_list));\n\ntemplate <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> std::underlying_type_t<T> integer_value(T value)\n{\n  return static_cast<std::underlying_type_t<T>>(value);\n}\n\n// unfortunately `std::is_sorted` isn't `constexpr` on C++17\ntemplate <typename Iterator> constexpr bool is_sorted(Iterator begin, Iterator end)\n{\n  if (begin == end) {\n    return true;\n  }\n  for (auto i = begin + 1; i != end; ++i, ++begin) {\n    if (*i < *begin) {\n      return false;\n    }\n  }\n  return true;\n}\n\n/**\n * Returns a 0-based index for the given enum value.\n *\n * Returns `enum_traits<T>::count` if not found.\n */\ntemplate <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> constexpr std::size_t enum_index_of(T value)\n{\n  using traits = enum_traits<T>;\n\n  if constexpr (traits::is_contiguous) {\n    return static_cast<std::size_t>(static_cast<std::intmax_t>(value) - static_cast<std::intmax_t>(traits::min()));\n  } else {\n    static_assert(is_sorted(traits::values.begin(), traits::values.end()), \"enum values were not declared in sorted order\");\n    auto const i = std::lower_bound(traits::values.begin(), traits::values.end(), value);\n\n    return i != traits::values.end() && *i == value ? std::distance(traits::values.begin(), i) : traits::count;\n  }\n}\n\n/**\n * A space and time efficient set for storing rich enumerations.\n */\ntemplate <typename Enum> class EnumSet {\n  static_assert(std::is_enum_v<Enum>);\n  using traits = enum_traits<Enum>;\n  using int_type = smallest_unsigned_integer<traits::count>;\n\n  struct const_iterator {\n    constexpr const_iterator(int_type set = int_type{0}) : set_(set) {}\n\n    constexpr bool operator==(const_iterator const &rhs) const { return set_ == rhs.set_; }\n    constexpr bool operator!=(const_iterator const &rhs) const { return set_ != rhs.set_; }\n    const_iterator &operator++();\n    const_iterator operator++(int);\n    constexpr Enum operator*() const;\n\n  private:\n    int_type set_;\n  };\n\npublic:\n  constexpr EnumSet() = default;\n  constexpr EnumSet(Enum value) : set_(mask(value)) {}\n\n  constexpr EnumSet &add(Enum value);\n  constexpr EnumSet &add(EnumSet set);\n\n  constexpr EnumSet &operator+=(Enum value) { return add(value); }\n  constexpr EnumSet &operator+=(EnumSet set) { return add(set); }\n\n  constexpr bool contains(Enum value) const;\n  constexpr bool contains(EnumSet set) const;\n\n  constexpr std::size_t size() const { return count_bits_set(set_); }\n  constexpr bool empty() const { return !set_; }\n\n  void clear() { set_ = 0; }\n\n  constexpr const_iterator begin() const { return const_iterator{set_}; }\n  constexpr const_iterator end() const { return const_iterator{}; }\n\n  constexpr int_type bit_mask() const { return set_; }\n\n  constexpr bool operator==(EnumSet rhs) const { return set_ == rhs.set_; }\n  constexpr bool operator!=(EnumSet rhs) const { return set_ != rhs.set_; }\n\n  template <typename Out> friend Out &&operator<<(Out &&out, EnumSet set)\n  {\n    bool first = true;\n    for (auto const value : set) {\n      if (first) {\n        first = false;\n      } else {\n        out << ',';\n      }\n      out << value;\n    }\n    return std::forward<Out>(out);\n  }\n\n  static constexpr int_type mask(Enum value);\n\nprivate:\n  int_type set_ = static_cast<int_type>(0);\n};\n\n#include <util/enum.inl>\n"
  },
  {
    "path": "util/enum.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <utility>\n\n#include <cassert>\n\ntemplate <typename Enum> constexpr typename EnumSet<Enum>::int_type EnumSet<Enum>::mask(Enum value)\n{\n  return traits::is_valid(value) ? make_bit<int_type>(enum_index_of(value)) : int_type{0};\n}\n\ntemplate <typename Enum> constexpr EnumSet<Enum> &EnumSet<Enum>::add(Enum value)\n{\n  set_ |= mask(value);\n  return *this;\n}\n\ntemplate <typename Enum> constexpr EnumSet<Enum> &EnumSet<Enum>::add(EnumSet set)\n{\n  set_ |= set.set_;\n  return *this;\n}\n\ntemplate <typename Enum> constexpr bool EnumSet<Enum>::contains(Enum value) const\n{\n  return set_ & mask(value);\n}\n\ntemplate <typename Enum> constexpr bool EnumSet<Enum>::contains(EnumSet set) const\n{\n  return (set_ & set.set_) == set.set_;\n}\n\ntemplate <typename Enum> typename EnumSet<Enum>::const_iterator &EnumSet<Enum>::const_iterator::operator++()\n{\n  set_ = disable_least_significant_bit(set_);\n  return *this;\n}\n\ntemplate <typename Enum> typename EnumSet<Enum>::const_iterator EnumSet<Enum>::const_iterator::operator++(int)\n{\n  auto copy = *this;\n  ++*this;\n  return copy;\n}\n\ntemplate <typename Enum> constexpr Enum EnumSet<Enum>::const_iterator::operator*() const\n{\n  assert(set_);\n  return traits::values[least_significant_bit_index(set_)];\n}\n"
  },
  {
    "path": "util/enum_operators.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n//\n// enum_operators.inl\n//\n// define an enum that has a set of useful overloaded operators\n// automatically defines:\n//      1. the enum\n//      2. to_string/enum_from_string/try_enum_from_string\n//      3. sanitize_enum convenience function\n//      4. enum_traits for the enum\n//      5. operator<<\n//\n// to use:\n//\n// #include <util/enum.h>\n//\n// #define ENUM_NAME Foobar\n// #define ENUM_TYPE uint32_t\n// #define ENUM_ELEMENTS(X) \\.\n//            X(key_a, 0, \"\")           \\.\n//            X(key_b, 1, \"custom_b\")   \\.\n//            X(key_c, 2, \"\")           \\.\n//            ...\n// #define ENUM_DEFAULT key_a\n// #include <util/enum_operators.inl>\n//\n// When defining the elements, the third argument specifies the string for\n// the string conversion utilities.  An empty string causes a default\n// conversion using the enum element name.\n// In the example above, to_string(key_a) will return \"key_a\",\n// but to_string(key_c) will return \"custom_b\".\n// Similarly, try_enum_from_string(\"key_a\") will return Foobar::key_a,\n// but try_enum_from_string(\"key_b\") would fail - it expects \"custom_b\".\n// try_enum_from_string(\"custom_b\") would return Foobar::key_b.\n//\n// There is no need to #undef these macros as they are #undef'd in this file\n//\n\n#include <util/preprocessor.h>\n\n#ifndef ENUM_NAME\n#error \"You must declare the name of the enum in ENUM_NAME\"\n#endif\n\n#ifndef ENUM_TYPE\n#define ENUM_TYPE unsigned\n#endif\n\n#ifndef ENUM_ELEMENTS\n#error \"You must declare the list of enum element keys and values in ENUM_ELEMENTS\"\n#endif\n\n#ifdef ENUM_NAMESPACE\nnamespace ENUM_NAMESPACE {\n#define ENUM_FULLY_QUALIFIED_NAME ::ENUM_NAMESPACE::ENUM_NAME\n#else // ENUM_NAMESPACE\n#define ENUM_FULLY_QUALIFIED_NAME ::ENUM_NAME\n#endif // ENUM_NAMESPACE\n\n// Declare the enum itself\n#define ENUM_ELEMENT(K, V, S) K = V,\nenum class ENUM_NAME : ENUM_TYPE { ENUM_ELEMENTS(ENUM_ELEMENT) };\n#undef ENUM_ELEMENT\n\n#ifdef ENUM_NAMESPACE\n} // namespace ENUM_NAMESPACE\n#endif // ENUM_NAMESPACE\n\ntemplate <> struct enum_traits<ENUM_FULLY_QUALIFIED_NAME> {\n  using type = ENUM_FULLY_QUALIFIED_NAME;\n  using int_type = std::underlying_type_t<type>;\n\n#ifdef ENUM_DEFAULT\n  static constexpr type default_value = type::ENUM_DEFAULT;\n#endif // ENUM_DEFAULT\n\n  static constexpr bool has_default_value =\n#ifdef ENUM_DEFAULT\n      true\n#else  // ENUM_DEFAULT\n      false\n#endif // ENUM_DEFAULT\n      ;\n\n  static constexpr type min()\n  {\n    std::initializer_list<type> values = {\n#define LIST_ENUM_VALUES(K, V, S) type::K,\n        ENUM_ELEMENTS(LIST_ENUM_VALUES)\n#undef LIST_ENUM_VALUES\n    };\n    return std::min(values);\n  }\n\n  static constexpr type max()\n  {\n    std::initializer_list<type> values = {\n#define LIST_ENUM_VALUES(K, V, S) type::K,\n        ENUM_ELEMENTS(LIST_ENUM_VALUES)\n#undef LIST_ENUM_VALUES\n    };\n    return std::max(values);\n  }\n\n  static constexpr int_type count = [] {\n    return\n#define COUNT_ENUM_VALUES(K, V, S) 1 +\n        ENUM_ELEMENTS(COUNT_ENUM_VALUES)\n#undef COUNT_ENUM_VALUES\n            0;\n  }();\n\n  static constexpr bool is_valid(type value)\n  {\n    switch (value) {\n#define SWITCH_ENUM_TYPES(K, V, S)                                                                                             \\\n  case type::K:                                                                                                                \\\n    return true;\n      ENUM_ELEMENTS(SWITCH_ENUM_TYPES)\n#undef SWITCH_ENUM_TYPES\n    }\n    return false;\n  }\n\n  static constexpr std::array<type, count> values{\n#define LIST_ENUM_VALUES(K, V, S) type::K,\n      ENUM_ELEMENTS(LIST_ENUM_VALUES)\n#undef LIST_ENUM_VALUES\n  };\n\n  static constexpr bool is_contiguous = [] {\n    for (std::size_t i = 1; i < values.size(); ++i) {\n      if (static_cast<int_type>(values[i]) != static_cast<int_type>(values[i - 1]) + 1) {\n        return false;\n      }\n    }\n    return true;\n  }();\n\n  /**\n   * Returns an array that can be indexed with this enum.\n   *\n   * Converting the enum values to indexes into this array can be accomplished\n   * by calling `enum_index_of(value)`.\n   */\n  template <typename T> using array_map = std::array<T, count>;\n};\n\n#ifdef ENUM_NAMESPACE\nnamespace ENUM_NAMESPACE {\n#endif // ENUM_NAMESPACE\n\n// String conversion operations\nconstexpr inline std::string_view to_string(\n    ENUM_NAME value,\n#ifdef ENUM_DEFAULT\n    std::string_view fallback = PREPROC_STRINGIZE(ENUM_DEFAULT)\n#else  // ENUM_DEFAULT\n    std::string_view fallback = {}\n#endif // ENUM_DEFAULT\n)\n{\n  switch (value) {\n#define SWITCH_ENUM_TYPES(K, V, S)                                                                                             \\\n  case ENUM_NAME::K:                                                                                                           \\\n    return (std::string_view(S).empty()) ? #K : S;\n    ENUM_ELEMENTS(SWITCH_ENUM_TYPES)\n#undef SWITCH_ENUM_TYPES\n  }\n  return fallback;\n}\n\nconstexpr inline bool enum_from_string(std::string_view s, ENUM_NAME &out)\n{\n#define IF_ENUM_TYPES(K, V, S)                                                                                                 \\\n  if (std::string_view(S).empty()) {                                                                                           \\\n    if (s == #K) {                                                                                                             \\\n      out = ENUM_NAME::K;                                                                                                      \\\n      return true;                                                                                                             \\\n    }                                                                                                                          \\\n  } else {                                                                                                                     \\\n    if (s == S) {                                                                                                              \\\n      out = ENUM_NAME::K;                                                                                                      \\\n      return true;                                                                                                             \\\n    }                                                                                                                          \\\n  }\n  ENUM_ELEMENTS(IF_ENUM_TYPES)\n#undef IF_ENUM_TYPES\n  return false;\n}\n\nconstexpr inline ENUM_NAME try_enum_from_string(\n    std::string_view s,\n#ifdef ENUM_DEFAULT\n    ENUM_NAME fallback = ENUM_NAME::ENUM_DEFAULT\n#else  // ENUM_DEFAULT\n    ENUM_NAME fallback\n#endif // ENUM_DEFAULT\n)\n{\n  enum_from_string(s, fallback);\n  return fallback;\n}\n\n// Sanitizing convenience function\nconstexpr inline ENUM_NAME sanitize_enum(\n    ENUM_NAME value,\n    ENUM_NAME default_value\n#ifdef ENUM_DEFAULT\n    = ENUM_NAME::ENUM_DEFAULT\n#endif // ENUM_DEFAULT\n)\n{\n  return enum_traits<ENUM_NAME>::is_valid(value) ? value : default_value;\n}\n\n// Stream out overload\ntemplate <typename Out> Out &operator<<(Out &&out, ENUM_NAME value)\n{\n  out << to_string(value);\n  return out;\n}\n\n#ifdef ENUM_NAMESPACE\n} // namespace ENUM_NAMESPACE\n#endif // ENUM_NAMESPACE\n\n// Clean up the things we've defined\n#undef ENUM_NAME\n#undef ENUM_FULLY_QUALIFIED_NAME\n#undef ENUM_TYPE\n#undef ENUM_ELEMENTS\n#ifdef ENUM_DEFAULT\n#undef ENUM_DEFAULT\n#endif // ENUM_DEFAULT\n#ifdef ENUM_NAMESPACE\n#undef ENUM_NAMESPACE\n#endif // ENUM_NAMESPACE\n"
  },
  {
    "path": "util/enum_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/enum.h>\n\n#include <sstream>\n#include <vector>\n\n#define ENUM_NAME DenseEnum\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(a, 0, \"\")                                                                                                                  \\\n  X(b, 1, \"\")                                                                                                                  \\\n  X(c, 2, \"\")                                                                                                                  \\\n  X(d, 3, \"\")                                                                                                                  \\\n  X(e, 4, \"\")\n#define ENUM_DEFAULT a\n#include <util/enum_operators.inl>\n\n#define ENUM_NAME DenseEnum1Base\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(a, 1, \"\")                                                                                                                  \\\n  X(b, 2, \"\")                                                                                                                  \\\n  X(c, 3, \"\")                                                                                                                  \\\n  X(d, 4, \"\")                                                                                                                  \\\n  X(e, 5, \"\")\n#define ENUM_DEFAULT a\n#include <util/enum_operators.inl>\n\n#define ENUM_NAME SparseEnum\n#define ENUM_TYPE std::int8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(a, -20, \"\")                                                                                                                \\\n  X(b, -17, \"\")                                                                                                                \\\n  X(c, 0, \"\")                                                                                                                  \\\n  X(d, 1, \"\")                                                                                                                  \\\n  X(e, 18, \"\")                                                                                                                 \\\n  X(f, 90, \"\")\n#define ENUM_DEFAULT a\n#include <util/enum_operators.inl>\n\n#define ENUM_NAME EnumWithStringVal\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(a, 0, \"\")                                                                                                                  \\\n  X(b, 0x1, \"custom_b\")                                                                                                        \\\n  X(c, 0x2, \"\")                                                                                                                \\\n  X(d, 0x4, \"d\")                                                                                                               \\\n  X(e, 0x8, \"\")                                                                                                                \\\n  X(f, 0x10, \"f_str\")\n#define ENUM_DEFAULT a\n#include <util/enum_operators.inl>\n\nTEST(enum_traits_test, min)\n{\n  EXPECT_EQ(static_cast<int>(DenseEnum::a), static_cast<int>(enum_traits<DenseEnum>::min()));\n  EXPECT_EQ(static_cast<int>(DenseEnum1Base::a), static_cast<int>(enum_traits<DenseEnum1Base>::min()));\n  EXPECT_EQ(static_cast<int>(SparseEnum::a), static_cast<int>(enum_traits<SparseEnum>::min()));\n}\n\nTEST(enum_traits_test, constexpr_min)\n{\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<DenseEnum, DenseEnum::a>,\n               std::integral_constant<DenseEnum, enum_traits<DenseEnum>::min()>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<DenseEnum1Base, DenseEnum1Base::a>,\n               std::integral_constant<DenseEnum1Base, enum_traits<DenseEnum1Base>::min()>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<SparseEnum, SparseEnum::a>,\n               std::integral_constant<SparseEnum, enum_traits<SparseEnum>::min()>>));\n}\n\nTEST(enum_traits_test, max)\n{\n  EXPECT_EQ(\n      static_cast<enum_traits<DenseEnum>::int_type>(DenseEnum::e),\n      static_cast<enum_traits<DenseEnum>::int_type>(enum_traits<DenseEnum>::max()));\n  EXPECT_EQ(\n      static_cast<enum_traits<DenseEnum1Base>::int_type>(DenseEnum1Base::e),\n      static_cast<enum_traits<DenseEnum1Base>::int_type>(enum_traits<DenseEnum1Base>::max()));\n  EXPECT_EQ(\n      static_cast<enum_traits<SparseEnum>::int_type>(SparseEnum::f),\n      static_cast<enum_traits<SparseEnum>::int_type>(enum_traits<SparseEnum>::max()));\n}\n\nTEST(enum_traits_test, constexpr_max)\n{\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<DenseEnum, DenseEnum::e>,\n               std::integral_constant<DenseEnum, enum_traits<DenseEnum>::max()>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<DenseEnum1Base, DenseEnum1Base::e>,\n               std::integral_constant<DenseEnum1Base, enum_traits<DenseEnum1Base>::max()>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<SparseEnum, SparseEnum::f>,\n               std::integral_constant<SparseEnum, enum_traits<SparseEnum>::max()>>));\n}\n\nTEST(enum_traits_test, count)\n{\n  EXPECT_EQ(5, enum_traits<DenseEnum>::count);\n  EXPECT_EQ(5, enum_traits<DenseEnum1Base>::count);\n  EXPECT_EQ(6, enum_traits<SparseEnum>::count);\n}\n\nTEST(enum_traits_test, constexpr_count)\n{\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<std::size_t, 5>,\n               std::integral_constant<std::size_t, enum_traits<DenseEnum>::count>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<std::size_t, 5>,\n               std::integral_constant<std::size_t, enum_traits<DenseEnum1Base>::count>>));\n  EXPECT_TRUE((std::is_same_v<\n               std::integral_constant<std::size_t, 6>,\n               std::integral_constant<std::size_t, enum_traits<SparseEnum>::count>>));\n}\n\nTEST(enum_traits_test, values)\n{\n  constexpr std::array<DenseEnum, enum_traits<DenseEnum>::count> dense_values = {\n      DenseEnum::a, DenseEnum::b, DenseEnum::c, DenseEnum::d, DenseEnum::e};\n  EXPECT_TRUE(dense_values == enum_traits<DenseEnum>::values);\n\n  constexpr std::array<DenseEnum1Base, enum_traits<DenseEnum1Base>::count> dense1base_values = {\n      DenseEnum1Base::a, DenseEnum1Base::b, DenseEnum1Base::c, DenseEnum1Base::d, DenseEnum1Base::e};\n  EXPECT_TRUE(dense1base_values == enum_traits<DenseEnum1Base>::values);\n\n  constexpr std::array<SparseEnum, enum_traits<SparseEnum>::count> sparse_values = {\n      SparseEnum::a, SparseEnum::b, SparseEnum::c, SparseEnum::d, SparseEnum::e, SparseEnum::f};\n  EXPECT_TRUE(sparse_values == enum_traits<SparseEnum>::values);\n}\n\nTEST(enum_traits_test, is_contiguous)\n{\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_contiguous);\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_contiguous);\n  EXPECT_FALSE(enum_traits<SparseEnum>::is_contiguous);\n}\n\nTEST(enum_traits_test, index_of)\n{\n  EXPECT_EQ(0u, enum_index_of(DenseEnum::a));\n  EXPECT_EQ(1u, enum_index_of(DenseEnum::b));\n  EXPECT_EQ(2u, enum_index_of(DenseEnum::c));\n  EXPECT_EQ(3u, enum_index_of(DenseEnum::d));\n  EXPECT_EQ(4u, enum_index_of(DenseEnum::e));\n\n  EXPECT_EQ(0u, enum_index_of(DenseEnum1Base::a));\n  EXPECT_EQ(1u, enum_index_of(DenseEnum1Base::b));\n  EXPECT_EQ(2u, enum_index_of(DenseEnum1Base::c));\n  EXPECT_EQ(3u, enum_index_of(DenseEnum1Base::d));\n  EXPECT_EQ(4u, enum_index_of(DenseEnum1Base::e));\n\n  EXPECT_EQ(0u, enum_index_of(SparseEnum::a));\n  EXPECT_EQ(1u, enum_index_of(SparseEnum::b));\n  EXPECT_EQ(2u, enum_index_of(SparseEnum::c));\n  EXPECT_EQ(3u, enum_index_of(SparseEnum::d));\n  EXPECT_EQ(4u, enum_index_of(SparseEnum::e));\n}\n\nTEST(enum_traits_test, array_map)\n{\n  EXPECT_TRUE((std::is_same_v<enum_traits<DenseEnum>::array_map<int>, std::array<int, 5>>));\n  EXPECT_TRUE((std::is_same_v<enum_traits<DenseEnum>::array_map<std::string>, std::array<std::string, 5>>));\n\n  EXPECT_TRUE((std::is_same_v<enum_traits<DenseEnum1Base>::array_map<int>, std::array<int, 5>>));\n  EXPECT_TRUE((std::is_same_v<enum_traits<DenseEnum1Base>::array_map<std::string>, std::array<std::string, 5>>));\n}\n\nTEST(enum_test, to_string)\n{\n  EXPECT_EQ(\"a\", to_string(DenseEnum::a));\n  EXPECT_EQ(\"b\", to_string(DenseEnum::b));\n  EXPECT_EQ(\"c\", to_string(DenseEnum::c));\n  EXPECT_EQ(\"d\", to_string(DenseEnum::d));\n  EXPECT_EQ(\"e\", to_string(DenseEnum::e));\n  EXPECT_EQ(\"a\", to_string(static_cast<DenseEnum>(9999)));\n  EXPECT_EQ(\"invalid\", to_string(static_cast<DenseEnum>(9999), \"invalid\"));\n\n  EXPECT_EQ(\"a\", to_string(DenseEnum1Base::a));\n  EXPECT_EQ(\"b\", to_string(DenseEnum1Base::b));\n  EXPECT_EQ(\"c\", to_string(DenseEnum1Base::c));\n  EXPECT_EQ(\"d\", to_string(DenseEnum1Base::d));\n  EXPECT_EQ(\"e\", to_string(DenseEnum1Base::e));\n  EXPECT_EQ(\"a\", to_string(static_cast<DenseEnum1Base>(9999)));\n  EXPECT_EQ(\"invalid\", to_string(static_cast<DenseEnum1Base>(9999), \"invalid\"));\n\n  EXPECT_EQ(\"a\", to_string(SparseEnum::a));\n  EXPECT_EQ(\"b\", to_string(SparseEnum::b));\n  EXPECT_EQ(\"c\", to_string(SparseEnum::c));\n  EXPECT_EQ(\"d\", to_string(SparseEnum::d));\n  EXPECT_EQ(\"e\", to_string(SparseEnum::e));\n  EXPECT_EQ(\"f\", to_string(SparseEnum::f));\n  EXPECT_EQ(\"a\", to_string(static_cast<SparseEnum>(9999)));\n  EXPECT_EQ(\"invalid\", to_string(static_cast<SparseEnum>(9999), \"invalid\"));\n}\n\nTEST(enum_test, to_string_custom)\n{\n  EXPECT_EQ(\"a\", to_string(EnumWithStringVal::a));\n  EXPECT_EQ(\"custom_b\", to_string(EnumWithStringVal::b));\n  EXPECT_EQ(\"c\", to_string(EnumWithStringVal::c));\n  EXPECT_EQ(\"d\", to_string(EnumWithStringVal::d));\n  EXPECT_EQ(\"e\", to_string(EnumWithStringVal::e));\n  EXPECT_EQ(\"f_str\", to_string(EnumWithStringVal::f));\n}\n\nTEST(enum_traits_test, is_valid)\n{\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_valid(DenseEnum::a));\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_valid(DenseEnum::b));\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_valid(DenseEnum::c));\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_valid(DenseEnum::d));\n  EXPECT_TRUE(enum_traits<DenseEnum>::is_valid(DenseEnum::e));\n  EXPECT_FALSE(enum_traits<DenseEnum>::is_valid(static_cast<DenseEnum>(9999)));\n\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_valid(DenseEnum1Base::a));\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_valid(DenseEnum1Base::b));\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_valid(DenseEnum1Base::c));\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_valid(DenseEnum1Base::d));\n  EXPECT_TRUE(enum_traits<DenseEnum1Base>::is_valid(DenseEnum1Base::e));\n  EXPECT_FALSE(enum_traits<DenseEnum1Base>::is_valid(static_cast<DenseEnum1Base>(9999)));\n\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::a));\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::b));\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::c));\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::d));\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::e));\n  EXPECT_TRUE(enum_traits<SparseEnum>::is_valid(SparseEnum::f));\n  EXPECT_FALSE(enum_traits<SparseEnum>::is_valid(static_cast<SparseEnum>(9999)));\n}\n\nTEST(enum_test, sanitize_enum)\n{\n  EXPECT_TRUE(DenseEnum::a == sanitize_enum(DenseEnum::a));\n  EXPECT_TRUE(DenseEnum::b == sanitize_enum(DenseEnum::b));\n  EXPECT_TRUE(DenseEnum::c == sanitize_enum(DenseEnum::c));\n  EXPECT_TRUE(DenseEnum::d == sanitize_enum(DenseEnum::d));\n  EXPECT_TRUE(DenseEnum::e == sanitize_enum(DenseEnum::e));\n  EXPECT_TRUE(DenseEnum::a == sanitize_enum(static_cast<DenseEnum>(9999)));\n\n  EXPECT_TRUE(DenseEnum1Base::a == sanitize_enum(DenseEnum1Base::a));\n  EXPECT_TRUE(DenseEnum1Base::b == sanitize_enum(DenseEnum1Base::b));\n  EXPECT_TRUE(DenseEnum1Base::c == sanitize_enum(DenseEnum1Base::c));\n  EXPECT_TRUE(DenseEnum1Base::d == sanitize_enum(DenseEnum1Base::d));\n  EXPECT_TRUE(DenseEnum1Base::e == sanitize_enum(DenseEnum1Base::e));\n  EXPECT_TRUE(DenseEnum1Base::a == sanitize_enum(static_cast<DenseEnum1Base>(9999)));\n\n  EXPECT_TRUE(SparseEnum::a == sanitize_enum(SparseEnum::a));\n  EXPECT_TRUE(SparseEnum::b == sanitize_enum(SparseEnum::b));\n  EXPECT_TRUE(SparseEnum::c == sanitize_enum(SparseEnum::c));\n  EXPECT_TRUE(SparseEnum::d == sanitize_enum(SparseEnum::d));\n  EXPECT_TRUE(SparseEnum::e == sanitize_enum(SparseEnum::e));\n  EXPECT_TRUE(SparseEnum::f == sanitize_enum(SparseEnum::f));\n  EXPECT_TRUE(SparseEnum::a == sanitize_enum(static_cast<SparseEnum>(9999)));\n}\n\ntemplate <typename Enum> void test_enum_set_ostream_operator(EnumSet<Enum> const &set, std::string_view expected)\n{\n  std::ostringstream actual;\n\n  actual << set;\n\n  EXPECT_EQ(expected, actual.str());\n}\n\ntemplate <typename Enum> void test_enum_set_iterator(EnumSet<Enum> const &set, std::vector<Enum> expected)\n{\n  EXPECT_EQ(set.begin() == set.end(), expected.empty());\n  EXPECT_EQ(set.begin() != set.end(), !expected.empty());\n\n  {\n    auto i = set.begin();\n\n    std::size_t count = 0;\n    for (auto const e : expected) {\n      ASSERT_NE(set.end(), i);\n      EXPECT_TRUE(e == *i);\n      ++i;\n      ++count;\n      EXPECT_EQ(i == set.end(), count == expected.size());\n    }\n\n    EXPECT_EQ(set.end(), i);\n  }\n\n  {\n    auto i = set.begin();\n\n    for (auto const e : expected) {\n      ASSERT_NE(set.end(), i);\n      EXPECT_TRUE(e == *i);\n      EXPECT_NE(set.end(), i++);\n    }\n\n    EXPECT_EQ(set.end(), i);\n  }\n}\n\ntemplate <typename Enum> void test_enum_set()\n{\n  EnumSet<Enum> set;\n  EXPECT_TRUE(set.empty());\n  EXPECT_EQ(0u, set.size());\n  EXPECT_FALSE(set.contains(Enum::a));\n  EXPECT_FALSE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b00000u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{});\n  EXPECT_FALSE(set != EnumSet<Enum>{});\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {});\n  test_enum_set_ostream_operator(set, \"\");\n\n  set.add(Enum::a);\n  EXPECT_FALSE(set.empty());\n  EXPECT_EQ(1u, set.size());\n  EXPECT_TRUE(set.contains(Enum::a));\n  EXPECT_FALSE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_FALSE(set.contains(Enum::e));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b00001u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{}.add(Enum::a));\n  EXPECT_FALSE(set != EnumSet<Enum>{}.add(Enum::a));\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {Enum::a});\n  test_enum_set_ostream_operator(set, \"a\");\n\n  set.add(Enum::b);\n  EXPECT_FALSE(set.empty());\n  EXPECT_EQ(2u, set.size());\n  EXPECT_TRUE(set.contains(Enum::a));\n  EXPECT_TRUE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_FALSE(set.contains(Enum::e));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b00011u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{}.add(Enum::a).add(Enum::b));\n  EXPECT_FALSE(set != EnumSet<Enum>{}.add(Enum::a).add(Enum::b));\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {Enum::a, Enum::b});\n  test_enum_set_ostream_operator(set, \"a,b\");\n\n  set.add(Enum::a);\n  EXPECT_FALSE(set.empty());\n  EXPECT_EQ(2u, set.size());\n  EXPECT_TRUE(set.contains(Enum::a));\n  EXPECT_TRUE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_FALSE(set.contains(Enum::e));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b00011u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{}.add(Enum::a).add(Enum::b));\n  EXPECT_FALSE(set != EnumSet<Enum>{}.add(Enum::a).add(Enum::b));\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {Enum::a, Enum::b});\n  test_enum_set_ostream_operator(set, \"a,b\");\n\n  set.add(Enum::e);\n  EXPECT_FALSE(set.empty());\n  EXPECT_EQ(3u, set.size());\n  EXPECT_TRUE(set.contains(Enum::a));\n  EXPECT_TRUE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_TRUE(set.contains(Enum::e));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b10011u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{}.add(Enum::a).add(Enum::b).add(Enum::e));\n  EXPECT_FALSE(set != EnumSet<Enum>{}.add(Enum::a).add(Enum::b).add(Enum::e));\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {Enum::a, Enum::b, Enum::e});\n  test_enum_set_ostream_operator(set, \"a,b,e\");\n\n  set.add(static_cast<Enum>(9999));\n  EXPECT_FALSE(set.empty());\n  EXPECT_EQ(3u, set.size());\n  EXPECT_TRUE(set.contains(Enum::a));\n  EXPECT_TRUE(set.contains(Enum::b));\n  EXPECT_FALSE(set.contains(Enum::c));\n  EXPECT_FALSE(set.contains(Enum::d));\n  EXPECT_TRUE(set.contains(Enum::e));\n  EXPECT_FALSE(set.contains(static_cast<Enum>(9999)));\n  EXPECT_EQ(0b10011u, set.bit_mask());\n  EXPECT_TRUE(set == EnumSet<Enum>{}.add(Enum::a).add(Enum::b).add(Enum::e));\n  EXPECT_FALSE(set != EnumSet<Enum>{}.add(Enum::a).add(Enum::b).add(Enum::e));\n  EXPECT_FALSE(set == EnumSet<Enum>{}.add(Enum::d));\n  EXPECT_TRUE(set != EnumSet<Enum>{}.add(Enum::d));\n  test_enum_set_iterator(set, {Enum::a, Enum::b, Enum::e});\n  test_enum_set_ostream_operator(set, \"a,b,e\");\n}\n\nTEST(enum_set_test, DenseEnum)\n{\n  test_enum_set<DenseEnum>();\n}\n\nTEST(enum_set_test, DenseEnum1Base)\n{\n  test_enum_set<DenseEnum1Base>();\n}\n\nTEST(enum_set_test, SparseEnum)\n{\n  test_enum_set<SparseEnum>();\n}\n"
  },
  {
    "path": "util/environment_variables.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/environment_variables.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <stdexcept>\n\n#include <cstdlib>\n\nstd::string_view try_get_env_var(char const *name, std::string_view fallback)\n{\n  auto const value = std::getenv(name);\n  return value ? value : fallback;\n}\n\nstd::string get_env_var(char const *name)\n{\n  if (auto const value = try_get_env_var(name); !value.empty()) {\n    return std::string(value.data(), value.size());\n  }\n\n  throw std::invalid_argument(fmt::format(\"missing / empty environment variable {}\", name));\n}\n"
  },
  {
    "path": "util/environment_variables.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n#include <string_view>\n\n/**\n * This file contains utilities for dealing with environment variables.\n */\n\n/**\n * Reads the environment variable named `name` and returns its value. Name must be\n * a null-terminated string.\n *\n * If the variable is not found then `fallback` is refurned instead (defaults to\n * empty `string_view`).\n *\n * NOTE: this function reads environment variables so it's advisable to call it\n * before any thread is created, given that reading/writing to environment\n * variables is not thread safe and we can't control 3rd party libraries.\n */\nstd::string_view try_get_env_var(char const *name, std::string_view fallback = {});\n\n/**\n * Reads the environment variable named `name` and returns its value converted\n * to type `T`. Name must be a null-terminated string.\n *\n * If the variable is not found or its value can't be converted to `T` then\n * `fallback` is refurned instead (defaults to value initialized `T`).\n *\n * NOTE: this function reads environment variables so it's advisable to call it\n * before any thread is created, given that reading/writing to environment\n * variables is not thread safe and we can't control 3rd party libraries.\n */\ntemplate <typename T> T try_get_env_value(char const *name, T fallback = {});\n\n/**\n * Reads the environment variable named `name` and returns its value. Name must be\n * a null-terminated string.\n *\n * If the variable is not found an `invalid_argument` exception is thrown.\n *\n * NOTE: this function reads environment variables so it's advisable to call it\n * before any thread is created, given that reading/writing to environment\n * variables is not thread safe and we can't control 3rd party libraries.\n */\nstd::string get_env_var(char const *name);\n\n#include <util/environment_variables.inl>\n"
  },
  {
    "path": "util/environment_variables.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/string.h>\n\ntemplate <typename T> T try_get_env_value(char const *name, T fallback)\n{\n  if (auto const value = std::getenv(name)) {\n    return try_from_string<T>(value, fallback);\n  }\n\n  return fallback;\n}\n"
  },
  {
    "path": "util/error_handling.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/error_handling.h\"\n\n#include \"util/defer.h\"\n#include \"util/log.h\"\n\n#include <absl/debugging/symbolize.h>\n#include <absl/strings/str_cat.h>\n\n#include <execinfo.h>\n#include <iostream>\n#include <sstream>\n#include <string>\n\nnamespace {\n\nvoid stacktrace(std::ostream &os)\n{\n  // Get the list of PCs\n  static const int kMaxPcBufferSize = 1000;\n  void *pc_buffer[kMaxPcBufferSize];\n  const int pc_entries = backtrace(pc_buffer, kMaxPcBufferSize);\n\n  // Print out the symbols for each stack entry.\n  char **backtrace_names = backtrace_symbols(pc_buffer, pc_entries);\n  if (backtrace_names != nullptr) {\n    Defer free_names([backtrace_names] { free(backtrace_names); });\n\n    static const int kMaxSymbolLength = 1024;\n    static const char kUnknownSymbol[] = \"(unknown)\";\n    for (int i = 0; i < pc_entries; i++) {\n      const void *const pc = pc_buffer[i];\n\n      char symbol_buffer[kMaxSymbolLength];\n      const char *symbol = kUnknownSymbol;\n      if (absl::Symbolize(pc, symbol_buffer, kMaxSymbolLength)) {\n        symbol = symbol_buffer;\n      }\n\n      os << \"  \" << backtrace_names[i] << \" - \" << symbol << std::endl;\n    }\n  }\n}\n\n} // namespace\n\nnamespace internal {\n\nPanicker::~Panicker()\n{\n  std::stringstream message_stream;\n\n  // Buffer the preamble and custom message (if there is one).\n  message_stream << preamble_;\n\n  if (!custom_message_.empty()) {\n    message_stream << \" - \" << custom_message_;\n  }\n  message_stream << std::endl;\n\n  // Buffer  the stack trace.\n  stacktrace(message_stream);\n\n  LOG::critical(message_stream.str());\n  std::exit(1);\n}\n\n} // namespace internal\n"
  },
  {
    "path": "util/error_handling.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This file contains helpers for performing error handling in code.\n\n#include <absl/strings/str_cat.h>\n#include <spdlog/fmt/fmt.h>\n\n#include <iostream>\n#include <sstream>\n#include <string>\n\n// This macro is like a more expressive version of the built-in assert function.\n// It will evaluate the provided expression and exit if it's false, otherwise\n// it does nothing. This will still evaluate, even if not compiled in debug mode\n// (unlike `assert(...)`. Among the details included in the error message are:\n//   - The time of failure (via spdlog)\n//   - The file + line of the ASSUME statement\n//   - A stacktrace including demangled symbols\n//\n// Furthermore, custom messages can be added useing the `else_log` function,\n// which uses spdlog-compatible format strings.\n// Examples:\n//\n//    bool success = should_always_return_true();\n//    ASSUME(success);\n//\n//    int size = compute_size_of_thing(thing);\n//    ASSUME(size == 0).else_log(\"Size of thing is {} instead of zero!\", size);\n//\n// None of the expressions in the custom message are evaluated if the\n// conditional evaluates to true.\n//\n#define ASSUME(...)                                                                                                            \\\n  (__VA_ARGS__) || ::internal::Panicker(::absl::StrCat(\"Assumption `\", #__VA_ARGS__, \"` failed at \", __FILE__, \":\", __LINE__))\n\n// DEBUG_ASSUME - version of the ASSUME macro enabled only on debug builds.\n// To be used when the evaluation of the provided expression is too costly for\n// release builds.\n//\n#ifdef NDEBUG\n#define DEBUG_ASSUME(...) (::internal::NopPanicker{})\n#else\n#define DEBUG_ASSUME ASSUME\n#endif // NDEBUG\n\nnamespace internal {\n\n// Internal class that prints a message. Exits and dumps everything in the\n// destructor.\nstruct Panicker {\n  // The first line of the error message (the remaining ones being the stack\n  // trace).\n  explicit Panicker(std::string preamble) : preamble_(std::move(preamble)) {}\n\n  // Program exits here.\n  [[noreturn]] ~Panicker();\n\n  // Custom message formatter. Uses spdlog-like formatting.\n  template <typename... Args> Panicker &else_log(fmt::format_string<Args...> format, Args &&... args)\n  {\n    fmt::memory_buffer out;\n#if FMT_VERSION >= 60000\n    fmt::format_to(std::back_inserter(out), format, std::forward<Args>(args)...);\n#else\n    fmt::format_to(out, format, std::forward<Args>(args)...);\n#endif\n    custom_message_ = fmt::to_string(out);\n    return *this;\n  }\n\n  // Needed so that the `(expression) || Panicker(...)` statement compiles.\n  operator bool() const { return true; }\n\nprivate:\n  const std::string preamble_;\n  std::string custom_message_;\n};\n\n#ifdef NDEBUG\n// Dummy panicker used by DEBUG_ASSUME on non-debug builds.\nstruct NopPanicker {\n  template <typename... Args> NopPanicker &else_log(Args &&... args) { return *this; }\n  operator bool() const { return true; }\n};\n#endif // NDEBUG\n\n} // namespace internal\n"
  },
  {
    "path": "util/expected.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <exception>\n#include <memory>\n#include <stdexcept>\n#include <system_error>\n#include <type_traits>\n#include <variant>\n\n#include <cassert>\n#include <cstdlib>\n\n/**\n * Expected: a wrapper of return values for return-based error handling.\n */\n\n////////////////////\n// error policies //\n////////////////////\n\n/**\n * An error policy for `Expected` for throwing unexpected errors (e.g.:\n * exceptions).\n */\ntemplate <typename Error> class ThrowableExpectedErrorPolicy {\npublic:\n  using error_type = Error;\n\n  /**\n   * Throws the given error.\n   *\n   * This function should not return.\n   */\n  [[noreturn]] static void raise(error_type const &error) { throw error; }\n\n  /**\n   * Tells whether this is a throwing error policy or not.\n   */\n  static constexpr bool throwing() { return true; }\n};\n\n/**\n * An error policy for `Expected` for value-based errors (e.g.: error codes)\n * which doesn't support throwing..\n */\ntemplate <typename Error> class ErrorValueExpectedErrorPolicy {\npublic:\n  using error_type = Error;\n\n  /**\n   * Tells whether this is a throwing error policy or not.\n   */\n  static constexpr bool throwing() { return false; }\n};\n\n/**\n * Resolves to either `ThrowableExpectedErrorPolicy` if `Error` is a subclass of\n * `std::exception` or `ErrorValueExpectedErrorPolicy` otherwise.\n */\ntemplate <typename Error>\nusing DefaultExpectedErrorPolicy = std::conditional_t<\n    std::is_base_of_v<std::exception, Error>,\n    ThrowableExpectedErrorPolicy<Error>,\n    ErrorValueExpectedErrorPolicy<Error>>;\n\n////////////////////\n// check policies //\n////////////////////\n\n/**\n * A check policy for `Expected` that asserts pre-conditions.\n */\nclass AssertExpectedCheckPolicy {\npublic:\n  /**\n   * Asserts that `expected` contains a value.\n   */\n  template <typename Expected> static void check_is_value(Expected const &expected) { assert(expected.has_value()); }\n\n  /**\n   * Asserts that `expected` contains an error.\n   */\n  template <typename Expected> static void check_is_error(Expected const &expected) { assert(!expected.has_value()); }\n};\n\n/**\n * A check policy for `expected` that throws on failed pre-conditions.\n */\nclass ThrowingExpectedCheckPolicy {\npublic:\n  template <typename Expected>\n  /**\n   * Checks that `expected` contains a value and throws a `std::logic_error` if\n   * not.\n   */\n  static void check_is_value(Expected const &expected)\n  {\n    if (!expected.has_value()) {\n      throw std::logic_error(\"attempting to retrieve the value out of an unexpected error\");\n    }\n  }\n\n  /**\n   * Checks that `expected` contains an error and throws a `std::logic_error` if\n   * not.\n   */\n  template <typename Expected> static void check_is_error(Expected const &expected)\n  {\n    if (expected.has_value()) {\n      throw std::logic_error(\"attempting to retrieve the error out of an expected value\");\n    }\n  }\n};\n\nusing DefaultExpectedCheckPolicy = AssertExpectedCheckPolicy;\n\n/**\n * A tag for unexcpected error conditions.\n *\n * Used in conjunction with `Expected`.\n */\nclass Unexpected {\n};\n\nconstexpr Unexpected unexpected = {};\n\ntemplate <typename, typename...> struct is_safe_overload : std::true_type {\n};\n\ntemplate <typename Class, typename T>\nstruct is_safe_overload<Class, T>\n    : std::integral_constant<\n          bool,\n          !std::is_base_of<Class, typename std::remove_cv<typename std::remove_reference<T>::type>::type>::value> {\n};\n\n/**\n * A wrapper of return values for return-based error handling.\n *\n * Allows the representation of both expected return values and unexpected error\n * conditions.\n */\ntemplate <\n    typename T,\n    typename Error,\n    typename CheckPolicy = DefaultExpectedCheckPolicy,\n    template <typename> typename ErrorPolicy = DefaultExpectedErrorPolicy>\nclass Expected {\npublic:\n  using value_type = T;\n  using error_type = Error;\n  using error_policy = ErrorPolicy<Error>;\n  using check_policy = CheckPolicy;\n\nprivate:\n  class ErrorContainer {\n  public:\n    template <typename... UArgs, typename = std::enable_if_t<is_safe_overload<ErrorContainer, UArgs...>::value>>\n    explicit ErrorContainer(UArgs &&... args) : error_(std::forward<UArgs>(args)...)\n    {}\n\n    ErrorContainer(ErrorContainer const &) = default;\n    ErrorContainer(ErrorContainer &&) = default;\n\n    /**\n     * Getter for the wrapped error.\n     */\n    auto const &get() const { return error_; }\n\n    /**\n     * Getter for the wrapped error.\n     */\n    auto &get() { return error_; }\n\n  private:\n    error_type error_;\n  };\n\npublic:\n  /**\n   * Constructs an `Expected` with inplace construction of value from the given\n   * arguments.\n   */\n  /* implicit */\n  template <\n      typename... UArgs,\n      typename = std::enable_if_t<is_safe_overload<Expected, UArgs...>::value>,\n      typename = std::enable_if_t<sizeof...(UArgs) != 1 || !(std::is_same_v<Unexpected, UArgs> && ... && true)>>\n  Expected(UArgs &&... args) : data_(std::in_place_type<value_type>, std::forward<UArgs>(args)...)\n  {}\n\n  /**\n   * Constructs an `Expected` with an unexpected error.\n   */\n  template <typename... UArgs>\n  Expected(Unexpected, UArgs &&... args) : data_(std::in_place_type<ErrorContainer>, std::forward<UArgs>(args)...)\n  {}\n\n  Expected(Expected const &) = default;\n  Expected(Expected &&) = default;\n\n  /**\n   * Getter for the wrapped value.\n   *\n   * Calls the check policy to ensure this object contains a value.\n   */\n  value_type const &value() const\n  {\n    check_policy::check_is_value(*this);\n    return std::get<value_type>(data_);\n  }\n\n  /**\n   * Getter for the wrapped value.\n   *\n   * Calls the check policy to ensure this object contains a value.\n   */\n  value_type &value()\n  {\n    check_policy::check_is_value(*this);\n    return std::get<value_type>(data_);\n  }\n\n  /**\n   * Conditional getter for the wrapped value.\n   *\n   * Returns a pointer to the value if present, or `nullptr` otherwise.\n   */\n  value_type const *try_value() const\n  {\n    if (!has_value()) {\n      return nullptr;\n    }\n    return std::addressof(value());\n  }\n\n  /**\n   * Conditional getter for the wrapped value.\n   *\n   * Returns a pointer to the value if present, or `nullptr` otherwise.\n   */\n  value_type *try_value()\n  {\n    if (!has_value()) {\n      return nullptr;\n    }\n    return std::addressof(value());\n  }\n\n  /**\n   * Getter for the wrapped error.\n   *\n   * Calls the check policy to ensure this object contains an error.\n   */\n  error_type const &error() const\n  {\n    check_policy::check_is_error(*this);\n    return std::get<ErrorContainer>(data_).get();\n  }\n\n  /**\n   * Getter for the wrapped error object.\n   *\n   * Calls the check policy to ensure this object contains an error.\n   */\n  error_type &error()\n  {\n    check_policy::check_is_error(*this);\n    return std::get<ErrorContainer>(data_).get();\n  }\n\n  /**\n   * Conditional getter for the wrapped error.\n   *\n   * Returns a pointer to the error if present, or `nullptr` otherwise.\n   */\n  error_type const *try_error() const\n  {\n    if (has_value()) {\n      return nullptr;\n    }\n    return std::addressof(error());\n  }\n\n  /**\n   * Conditional getter for the wrapped error.\n   *\n   * Returns a pointer to the error if present, or `nullptr` otherwise.\n   */\n  error_type *try_error()\n  {\n    if (has_value()) {\n      return nullptr;\n    }\n    return std::addressof(error());\n  }\n\n  /**\n   * Returns `true` if a value is present, otherwise returns `false` as an error\n   * is.\n   */\n  bool has_value() const { return !data_.index(); }\n\n  /**\n   * Stores a value in-place constructed from the given arguments.\n   */\n  template <typename... UArgs> Expected &set_value(UArgs &&... args)\n  {\n    data_.template emplace<0>(std::forward<UArgs>(args)...);\n    return *this;\n  }\n\n  /**\n   * Stores an error in-place constructed from the given arguments.\n   */\n  template <typename... UArgs> Expected &set_error(UArgs &&... args)\n  {\n    data_.template emplace<1>(std::forward<UArgs>(args)...);\n    return *this;\n  }\n\n  /**\n   * Calls the given function `fn` if a value is present, otherwise doesn't do\n   * anything.\n   *\n   * `fn` will be given the value as its sole argument if such overload exists.\n   */\n  template <typename Fn> Expected const &on_value(Fn &&fn) const\n  {\n    if (has_value()) {\n      std::forward<Fn>(fn)(value());\n    }\n\n    return *this;\n  }\n\n  /**\n   * Calls the given function `fn` if a value is present, otherwise doesn't do\n   * anything.\n   *\n   * `fn` will be given the value as its sole argument if such overload exists.\n   */\n  template <typename Fn> Expected &on_value(Fn &&fn)\n  {\n    if (has_value()) {\n      std::forward<Fn>(fn)(value());\n    }\n\n    return *this;\n  }\n\n  /**\n   * Calls the given function `fn` if an error is present, otherwise doesn't do\n   * anything.\n   *\n   * `fn` will be given the error as its sole argument if such overload exists.\n   */\n  template <typename Fn> Expected const &on_error(Fn &&fn) const\n  {\n    if (!has_value()) {\n      std::forward<Fn>(fn)(error());\n    }\n\n    return *this;\n  }\n\n  /**\n   * Calls the given function `fn` if an error is present, otherwise doesn't do\n   * anything.\n   *\n   * `fn` will be given the error as its sole argument if such overload exists.\n   */\n  template <typename Fn> Expected &on_error(Fn &&fn)\n  {\n    if (!has_value()) {\n      std::forward<Fn>(fn)(error());\n    }\n\n    return *this;\n  }\n\n  /**\n   * Returns the value if present, or the result of calling the given function\n   * `fn` if an error is present.\n   *\n   * `fn` will be given the error as its sole argument if such overload exists.\n   */\n  template <typename Fn> value_type recover(Fn &&fn) const\n  {\n    if (has_value()) {\n      return value();\n    }\n\n    return std::forward<Fn>(fn)(error());\n  }\n\n  /**\n   * Returns the value if present, or the result of calling the given function\n   * `fn` if an error is present.\n   *\n   * `fn` will be given the error as its sole argument if such overload exists.\n   */\n  template <typename Fn> value_type recover(Fn &&fn)\n  {\n    if (has_value()) {\n      return value();\n    }\n\n    return std::forward<Fn>(fn)(error());\n  }\n\n  /**\n   * Returns the value if present, or the `with` if an error is present.\n   */\n  template <typename Value> value_type recover_with(Value &&with) const\n  {\n    if (has_value()) {\n      return value();\n    }\n\n    return std::forward<Value>(with);\n  }\n\n  /**\n   * Returns the value if present, or the `with` if an error is present.\n   */\n  template <typename Value> value_type recover_with(Value &&with)\n  {\n    if (has_value()) {\n      return value();\n    }\n\n    return std::forward<Value>(with);\n  }\n\n  /**\n   * This method can't be called on non-throwable error policies.\n   */\n  [[noreturn]] void raise() const\n  {\n    check_policy::check_is_error(*this);\n    if constexpr (throwing()) {\n      error_policy::raise(error());\n    } else {\n      static_assert(throwing(), \"can't raise on a non-throwing error policy\");\n    }\n\n    // ensure we won't return in case of a misbehaving error policy\n    std::abort();\n  }\n\n  Expected const &try_raise() const\n  {\n    if (has_value()) {\n      return *this;\n    }\n    raise();\n  }\n\n  Expected &try_raise()\n  {\n    if (has_value()) {\n      return *this;\n    }\n    raise();\n  }\n\n  value_type const &operator*() const { return value(); }\n  value_type &operator*() { return value(); }\n\n  value_type const *operator->() const { return try_value(); }\n  value_type *operator->() { return try_value(); }\n\n  explicit operator bool() const { return has_value(); }\n  bool operator!() const { return !has_value(); }\n\n  Expected &operator=(Expected const &rhs)\n  {\n    if (rhs.has_value()) {\n      set_value(rhs.value());\n    } else {\n      set_error(rhs.error());\n    }\n    return *this;\n  }\n\n  Expected &operator=(Expected &&rhs)\n  {\n    if (rhs.has_value()) {\n      set_value(std::move(rhs.value()));\n    } else {\n      set_error(std::move(rhs.error()));\n    }\n    return *this;\n  }\n\n  constexpr static bool throwing() { return error_policy::throwing(); }\n\nprivate:\n  std::variant<value_type, ErrorContainer> data_;\n};\n\n/**\n * Convenient alias for an `Expected` that uses `ThrowingExpectedCheckPolicy`.\n */\ntemplate <typename T, typename Error, template <typename> typename ErrorPolicy = DefaultExpectedErrorPolicy>\nusing CheckedExpected = Expected<T, Error, ThrowingExpectedCheckPolicy, ErrorPolicy>;\n\n// specializations\n\ntemplate <> class ErrorValueExpectedErrorPolicy<std::error_code> : public ThrowableExpectedErrorPolicy<std::error_code> {\npublic:\n  [[noreturn]] static void raise(error_type const &error) { throw std::system_error(error); }\n};\n\ntemplate <> class ErrorValueExpectedErrorPolicy<std::errc> : public ThrowableExpectedErrorPolicy<std::errc> {\npublic:\n  [[noreturn]] static void raise(error_type const &error)\n  {\n    throw std::system_error{static_cast<std::underlying_type_t<error_type>>(error), std::generic_category()};\n  }\n};\n"
  },
  {
    "path": "util/expected_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/expected.h>\n\n#include <gtest/gtest.h>\n\n#include <unordered_map>\n#include <vector>\n\nstruct CustomError : public std::exception {\n  CustomError() = default;\n\n  template <typename... Args>\n  explicit CustomError(std::string_view message, Args &&... args) : message_(message), code_((0 + ... + args))\n  {}\n\n  CustomError(CustomError const &) = default;\n  CustomError(CustomError &&) = default;\n\n  bool operator!=(CustomError const &rhs) const { return !(*this == rhs); }\n  bool operator==(CustomError const &rhs) const { return message_ == rhs.message_ && code_ == rhs.code_; }\n\n  auto const &message() const { return message_; }\n\nprivate:\n  std::string_view const message_;\n  int const code_ = 0;\n};\n\nTEST(expected, success_example)\n{\n  // simulates a function that returns a success value through `expected`\n  auto computation = []() -> Expected<int, CustomError> { return 10; };\n\n  EXPECT_NO_THROW({\n    auto result = computation();\n\n    if (!result) {\n      EXPECT_TRUE(false); // unreachable\n    } else {\n      EXPECT_EQ(10, result.value());\n    }\n\n    result.try_raise();\n  });\n}\n\nTEST(expected, success_with_arguments_example)\n{\n  // simulates a function that returns a success value constructed with\n  // arguments through `expected`\n  auto computation = []() -> Expected<std::string, CustomError> { return {\"test string\"}; };\n\n  EXPECT_NO_THROW({\n    auto result = computation();\n\n    if (!result) {\n      EXPECT_TRUE(false); // unreachable\n    } else {\n      EXPECT_EQ(\"test string\", result.value());\n    }\n\n    result.try_raise();\n  });\n}\n\nTEST(expected, success_with_multiple_arguments_example)\n{\n  // simulates a function that returns a success value constructed with multiple\n  // arguments through `expected`\n  auto computation = []() -> Expected<std::pair<std::string, int>, CustomError> { return {\"test string\", 100}; };\n\n  EXPECT_NO_THROW({\n    auto result = computation();\n\n    if (!result) {\n      EXPECT_TRUE(false); // unreachable\n    } else {\n      EXPECT_EQ(\"test string\", result.value().first);\n      EXPECT_EQ(100, result.value().second);\n    }\n\n    result.try_raise();\n  });\n}\n\nTEST(expected, failure_code_example)\n{\n  // simulates a function that returns an error code through `expected`\n  auto computation = []() -> Expected<int, long> { return {unexpected, -1}; };\n\n  EXPECT_NO_THROW({\n    auto result = computation();\n\n    if (!result) {\n      EXPECT_EQ(-1, result.error());\n    } else {\n      EXPECT_TRUE(false); // unreachable\n    }\n\n    // we can't call `result.raise()` nor `result.try_raise()` because that\n    // would fail to compile - this is intended because the default policy for\n    // non-exception error types is to not allow throwing\n  });\n}\n\nTEST(expected, default_constructed_failure_example)\n{\n  // simulates a function that returns a default constructed error through\n  // `expected`\n  auto computation = []() -> Expected<int, CustomError> { return unexpected; };\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        if (!result) {\n          EXPECT_EQ(CustomError(), result.error());\n\n          result.raise();\n        } else {\n          EXPECT_TRUE(false); // unreachable\n        }\n      },\n      CustomError);\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        result.try_raise();\n\n        EXPECT_TRUE(false); // unreachable\n      },\n      CustomError);\n}\n\nTEST(expected, default_constructed_success_example)\n{\n  // simulates a function that returns a default constructed value through\n  // `expected`\n  auto computation = []() -> Expected<int, CustomError> { return {}; };\n\n  EXPECT_NO_THROW({\n    auto result = computation();\n\n    if (!result) {\n      EXPECT_TRUE(false); // unreachable\n    } else {\n      EXPECT_EQ(int{}, result.value());\n    }\n\n    result.try_raise();\n  });\n}\n\nTEST(expected, failure_with_arguments_example)\n{\n  // simulates a function that returns an error constructed with arguments\n  // through `expected`\n  auto computation = []() -> Expected<int, CustomError> { return {unexpected, \"error message\"}; };\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        if (!result) {\n          EXPECT_EQ(CustomError(\"error message\"), result.error());\n\n          result.raise();\n        } else {\n          EXPECT_TRUE(false); // unreachable\n        }\n      },\n      CustomError);\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        result.try_raise();\n\n        EXPECT_TRUE(false); // unreachable\n      },\n      CustomError);\n}\n\nTEST(expected, failure_with_multiple_arguments_example)\n{\n  // simulates a function that returns an error constructed with multiple\n  // arguments through `expected`\n  auto computation = []() -> Expected<int, CustomError> { return {unexpected, \"error message\", 1, 2, 3}; };\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        if (!result) {\n          EXPECT_EQ(CustomError(\"error message\", 1, 2, 3), result.error());\n\n          result.raise();\n        } else {\n          EXPECT_TRUE(false); // unreachable\n        }\n      },\n      CustomError);\n\n  EXPECT_THROW(\n      {\n        auto result = computation();\n\n        result.try_raise();\n\n        EXPECT_TRUE(false); // unreachable\n      },\n      CustomError);\n}\n\nTEST(expected, default_ctor_int__int)\n{\n  Expected<int, int> e;\n\n  EXPECT_TRUE(e.has_value());\n  EXPECT_NE(nullptr, e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, value_int__int)\n{\n  Expected<int, int> e(10);\n\n  EXPECT_TRUE(e.has_value());\n\n  EXPECT_EQ(10, e.value());\n\n  ASSERT_NE(nullptr, e.try_value());\n  EXPECT_EQ(10, *e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, error_int__int)\n{\n  Expected<int, int> e(unexpected, 5);\n\n  EXPECT_FALSE(e.has_value());\n\n  ASSERT_EQ(nullptr, e.try_value());\n\n  EXPECT_NE(nullptr, e.try_error());\n  EXPECT_EQ(5, *e.try_error());\n}\n\nTEST(expected, default_ctor_int__custom_error)\n{\n  Expected<int, CustomError> e;\n\n  EXPECT_TRUE(e.has_value());\n  EXPECT_NE(nullptr, e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, value_int__custom_error)\n{\n  Expected<int, CustomError> e(10);\n\n  EXPECT_TRUE(e.has_value());\n\n  EXPECT_EQ(10, e.value());\n\n  ASSERT_NE(nullptr, e.try_value());\n  EXPECT_EQ(10, *e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, error_int__custom_error)\n{\n  Expected<int, CustomError> e(unexpected, \"message\");\n\n  EXPECT_FALSE(e.has_value());\n\n  ASSERT_EQ(nullptr, e.try_value());\n\n  EXPECT_NE(nullptr, e.try_error());\n  EXPECT_EQ(CustomError(\"message\"), *e.try_error());\n\n  EXPECT_THROW({ e.raise(); }, CustomError);\n}\n\nTEST(expected, default_ctor_custom_error__int)\n{\n  Expected<CustomError, int> e;\n\n  EXPECT_TRUE(e.has_value());\n  EXPECT_NE(nullptr, e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, value_custom_error__int)\n{\n  Expected<CustomError, int> e(\"result\");\n\n  EXPECT_TRUE(e.has_value());\n\n  EXPECT_EQ(CustomError(\"result\"), e.value());\n\n  ASSERT_NE(nullptr, e.try_value());\n  EXPECT_EQ(CustomError(\"result\"), *e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, error_custom_error__int)\n{\n  Expected<CustomError, int> e(unexpected, 5);\n\n  EXPECT_FALSE(e.has_value());\n\n  ASSERT_EQ(nullptr, e.try_value());\n\n  EXPECT_NE(nullptr, e.try_error());\n  EXPECT_EQ(5, *e.try_error());\n}\n\nTEST(expected, default_ctor_custom_error__custom_error)\n{\n  Expected<CustomError, CustomError> e;\n\n  EXPECT_TRUE(e.has_value());\n  EXPECT_NE(nullptr, e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, value_custom_error__custom_error)\n{\n  Expected<CustomError, CustomError> e(\"result\");\n\n  EXPECT_TRUE(e.has_value());\n\n  EXPECT_EQ(CustomError(\"result\"), e.value());\n\n  ASSERT_NE(nullptr, e.try_value());\n  EXPECT_EQ(CustomError(\"result\"), *e.try_value());\n\n  EXPECT_EQ(nullptr, e.try_error());\n}\n\nTEST(expected, error_custom_error__custom_error)\n{\n  Expected<CustomError, CustomError> e(unexpected, \"message\");\n\n  EXPECT_FALSE(e.has_value());\n\n  ASSERT_EQ(nullptr, e.try_value());\n\n  EXPECT_NE(nullptr, e.try_error());\n  EXPECT_EQ(CustomError(\"message\"), *e.try_error());\n\n  EXPECT_THROW({ e.raise(); }, CustomError);\n}\n\nTEST(expected, on_error_with_value)\n{\n  auto computation = []() -> Expected<int, CustomError> { return 10; };\n\n  EXPECT_NO_THROW({ computation().on_error([](auto &error) { throw CustomError(); }); });\n}\n\nTEST(expected, on_error_with_error__no_arguments)\n{\n  auto computation = []() -> Expected<int, CustomError> { return {unexpected, \"error message\"}; };\n\n  EXPECT_THROW({ computation().on_error([](auto &error) { throw CustomError(); }); }, CustomError);\n}\n\nTEST(expected, on_error_with_error__error_argument)\n{\n  auto computation = []() -> Expected<int, CustomError> { return {unexpected, \"error message\"}; };\n\n  EXPECT_THROW({ computation().on_error([](CustomError const &e) { throw e; }); }, CustomError);\n}\n\nTEST(expected, recover_with_value_const)\n{\n  Expected<int, CustomError> const e{10};\n\n  EXPECT_EQ(10, e.recover([](auto &error) { return 5; }));\n}\n\nTEST(expected, recover_with_value)\n{\n  Expected<int, CustomError> e{10};\n\n  EXPECT_EQ(10, e.recover([](auto &error) { return 5; }));\n}\n\nTEST(expected, recover_with_error_const)\n{\n  Expected<int, CustomError> const e{unexpected, \"error message\"};\n\n  EXPECT_EQ(5, e.recover([](auto &error) { return 5; }));\n}\n\nTEST(expected, recover_with_error)\n{\n  Expected<int, CustomError> e{unexpected, \"error message\"};\n\n  EXPECT_EQ(5, e.recover([](auto &error) { return 5; }));\n}\n\nTEST(expected, recover_with_with_value_const)\n{\n  Expected<int, CustomError> const e{10};\n\n  EXPECT_EQ(10, e.recover_with(5));\n}\n\nTEST(expected, recover_with_with_value)\n{\n  Expected<int, CustomError> e{10};\n\n  EXPECT_EQ(10, e.recover_with(5));\n}\n\nTEST(expected, recover_with_with_error_const)\n{\n  Expected<int, CustomError> const e{unexpected, \"error message\"};\n\n  EXPECT_EQ(5, e.recover_with(5));\n}\n\nTEST(expected, recover_with_with_error)\n{\n  Expected<int, CustomError> e{unexpected, \"error message\"};\n\n  EXPECT_EQ(5, e.recover_with(5));\n}\n\nTEST(expected, try_raise_on_success)\n{\n  Expected<int, CustomError> e{10};\n\n  EXPECT_EQ(10, e.try_raise().value());\n}\n\nTEST(expected, try_raise_on_error)\n{\n  Expected<int, CustomError> e{unexpected, \"error message\"};\n\n  EXPECT_THROW({ EXPECT_EQ(10, e.try_raise().value()); }, CustomError);\n}\n\nTEST(expected, value_CheckedExpected)\n{\n  CheckedExpected<int, int> e;\n\n  EXPECT_NO_THROW({ e.value(); });\n  EXPECT_THROW({ e.error(); }, std::logic_error);\n}\n\nTEST(expected, error_CheckedExpected)\n{\n  CheckedExpected<int, int> e(unexpected);\n\n  EXPECT_THROW({ e.value(); }, std::logic_error);\n  EXPECT_NO_THROW({ e.error(); });\n}\n"
  },
  {
    "path": "util/fast_div.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cstdint>\n#include <math.h>\n\n/**\n * Fast approximate division of uint64_t's by multiplying then right-shifting.\n * \n * Motivation. Integer division is relatively expensive on modern CPUs. Using Agner Fog's instruction tables [1]\n *   as reference, the 64-bit DIV instruction (unsigned divide, see [2] for instruction documentation) has a latency of\n *   35-88 cycles, and reciprocal throughput of 21-83 cycles/operation on the Skylake architecture (15 and 10 on Ice Lake). \n *   Code that performs a lot of this type of division in tightly dependent instruction chains can benefit from cheaper \n *   division.\n * \n * For example, in some telemetry/monitoring use-cases, there are millions of events per second, each with a nanosecond-scale\n *   timestamp. The monitoring system wants to aggregate to seconds. Given a measurement, the code needs to quickly find which \n *   1-second bin the measurement belongs to. When the telemetry streams have a minor amount of jitter from collection to aggregation,\n *   say up to 60 seconds (i.e., \"close to real time\"), the result needs to disambiguate the second a sample belongs to, but \n *   the result does not need to be unique across all seconds since the epoch. For example, the low 16-bits of the division is \n *   sufficient. The division can also be approximate: if the division puts every 999,999,998.5 nanoseconds together rather than\n *   exactly 1e9, the system still produces good aggregations in practice.\n * \n * Other instruction combinations are much cheaper than DIV. For example 64-bit MUL (integer multiplication) and SHRX (shift right \n *   without affecting flags):\n *    * MUL's latency is 3 cycles and reciprocal throughput is 1 on Skylake and Ice Lake\n *    * SHRX's latency is 1 cycle and reciprocal throughput is 0.5 on Skylake and Ice Lake\n * \n * Idea. We can approximate the 64-bit division with a multiplication followed by a shift. To divide by D, if we find two \n *   numbers M and S such that:\n *       D ~= 2^S / M                   (say up to an error of 0.00001%)\n *   then for a given x,\n *       x / D ~=  x * M / 2^S          (up to an error of (1 / (1 +- 0.00001%)), which would be close in practice to 0.00001%).\n * \n * So how should we choose M and S? Given a value for S, we can compute\n *    M = ((double)2^S / D) (where M is a u32 or u64)\n * With the trucation to make M an integer, we have\n *    M = 2^S / D - epsilon      where epsilon in [0, 1]\n * \n * Dividing the above by M and shuffling terms around, we can get the relative error:\n *    (2^S / M) / D   =   1 + epsilon / M\n * When 2^S is sufficiently larger than D, then M is large and the relative error (epsilon / M) is small.\n * \n * So large S is advantageous for precision. However, we cannot choose S to be arbitrarily large, because we are working with 64-bit \n *   arithmetic. \n * \n * The user specifies how many least-significant bits they need from the fast_div, B. After the shift, 64-S bits remain, so we need \n *   64-S >= B. We could set S = 64 - B to be the largest amount.\n * \n * The code below also fits M into a 32 bit value (nb, cannot see a reason why a u64 would be significanly less desireable).\n *   To get 2^S / D <= 2^32, we want D >= 2^(S-32), or log_2(D) >= S-32. The code computes\n *      S = min(64-B, floor(log_2(D))+32).    (because floor(log_2(D)) <= log_2(D))\n * \n * Examples:\n *    Say we want to divide a nanosecond timestamp to timeslots of 5 seconds, i.e., 5e9 nanos, and the \n *      application needs 16-bits of precision.\n * \n *    We have D = 5e9, B = 16. \n *         log_2(D) = 32.219280948873624\n *         floor(log_2(D)) = 32\n *         S = min(64-16, 32+32) = 48\n *         M = u32(2^48 / 5e9) = u32(56294.9953421312) = 56294\n *    The relative error is .9953421312 / 56294 ~ 0.0017%\n *    The division ends up being 2^S/M = 5000088405.703201, i.e., 5 seconds and around 88 microseconds\n * \n *    Note that in the above example, a lot of precision is lost from casting to u32. If instead the code rounded to the nearest\n *      integer, we would have\n *         M = round(2^48 / 5e9) = round(56294.9953421312) = 56295\n *         2^S/M = 4999999586.29818, i.e., 5 seconds less 414 nanoseconds\n *         and the relative error would be less than one part per million (PPM)\n *  \n *    Also, if the code only required 8 bits from the result:\n *         D = 5e9, B = 8. \n *         S = min(64-8, 32+32) = 56\n *         M = u32(2^56 / 5e9) = u32(14411518.807585588) = 14411518\n *         relative error is 0.807585588 / 14411518 ~ 5.6e-8, and 2^S/M is 5000000280.1875515, i.e., 5 seconds and 280 nanoseconds\n *         and with round() instead of u32(): 4999999933.242841 i.e., 5 seconds less 77 nanoseconds.\n * \n * NB when using fast_div with time. With fast_div, the time interval in the divisor is changed up to a small epsilon, for example \n *   5 seconds versus 5.000089 seconds as above. One side effect is that the boundaries between intervals also change, and do not land\n *   on full seconds. For example, when looking at nanoseconds since the Epoch (midnight, Jan 1, 1970), today's 5 second timeslots\n *   boundaries might land 3.5 seconds into the \"round\" 5 seconds. This is because the 89 microsecond difference really adds up over decades.\n *   Each application should consider whether this is acceptable.\n * \n * [1] https://www.agner.org/optimize/instruction_tables.pdf\n * [2] https://cdrdv2-public.intel.com/671110/325383-sdm-vol-2abcd.pdf\n */\nclass fast_div {\npublic:\n  /**\n   * C'tor with known mul, shift\n   */\n  fast_div(uint32_t mul, uint32_t shift);\n\n  /**\n   * C'tor that computes the mul and shift from the division amount\n   * @param amt: the amount by which to divide\n   * @param required_bits: the number of bits to be used from result\n   */\n  fast_div(double amt, uint32_t required_bits);\n\n  uint32_t mul() { return mul_; }\n  uint32_t shift() { return shift_; }\n\n  /**\n   * Returns the multiplication factor that is the estimated inverse of this\n   * division.\n   */\n  double estimated_reciprocal() const;\n\nprivate:\n  friend uint32_t operator/(uint64_t x, const fast_div &divisor);\n\n  uint32_t mul_;\n  uint32_t shift_;\n};\n\ninline fast_div::fast_div(uint32_t _mul, uint32_t _shift) : mul_(_mul), shift_(_shift) {}\n\ninline fast_div::fast_div(double amt, uint32_t required_bits)\n{\n  /* division by @amt will remove at least this many bits from the dividend */\n  uint32_t bits_removed = (uint32_t)log2(amt);\n\n  /* most precision obtained if right shift all but the required bits */\n  shift_ = 64 - required_bits;\n\n  /* but the multiplier must fit in uint32_t */\n  shift_ = std::min(shift_, 32 + bits_removed);\n\n  mul_ = (uint32_t)(((double)(1ull << shift_)) / amt);\n}\n\ninline double fast_div::estimated_reciprocal() const\n{\n  return (double)(1ull << shift_) / mul_;\n}\n\ninline uint32_t operator/(uint64_t x, const fast_div &divisor)\n{\n  return (uint32_t)((x * divisor.mul_) >> divisor.shift_);\n}\n"
  },
  {
    "path": "util/file_ops.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/file_ops.h>\n\n#include <util/defer.h>\n#include <util/log.h>\n#include <util/string_view.h>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <deque>\n#include <filesystem>\n#include <string>\n#include <vector>\n\n#include <cassert>\n#include <cstdint>\n\nnamespace {\n\n// RAII-based directory closer.\nstruct DirectoryCloser {\n  DIR *const dp;\n  ~DirectoryCloser()\n  {\n    if (dp)\n      closedir(dp);\n  }\n};\n\n} // namespace\n\nbool file_exists(char const *path, std::initializer_list<FileAccess> modes)\n{\n  int mode = F_OK;\n  for (auto i : modes) {\n    mode |= static_cast<int>(i);\n  }\n  return access(path, mode) == 0;\n}\n\nExpected<char const *, std::error_code> create_directory(char const *directory)\n{\n  if (auto const error = ::mkdir(directory, S_IRWXU); !error || errno == EEXIST) {\n    return directory;\n  } else {\n    return {unexpected, std::error_code{errno, std::generic_category()}};\n  }\n}\n\nFileMeta FileMeta::from_stat(std::string path, const struct stat &statbuf)\n{\n  FileMeta info;\n  info.path = std::move(path);\n  info.size_bytes = statbuf.st_size;\n  info.modify_nanotimestamp = statbuf.st_mtim.tv_sec * 1000000000LL + statbuf.st_mtim.tv_nsec;\n  return info;\n}\n\nDirMeta DirMeta::from_stat(std::string path, const struct stat &statbuf)\n{\n  DirMeta info;\n  info.path = std::move(path);\n  info.modify_nanotimestamp = statbuf.st_mtim.tv_sec * 1000000000LL + statbuf.st_mtim.tv_nsec;\n  return info;\n}\n\nvoid list_directory_contents(char const *directory, std::vector<FileMeta> *files, std::vector<DirMeta> *subdirs)\n{\n  DIR *const dp = opendir(directory);\n  DirectoryCloser closer = {dp};\n\n  if (dp) {\n    // Get the list of file names in the directory.\n    std::vector<std::string> file_names;\n    struct dirent *entry;\n    while ((entry = readdir(dp)) != nullptr) {\n      file_names.emplace_back(entry->d_name);\n    }\n\n    // Stat each file to get metadata.\n    for (const std::string &file_name : file_names) {\n      auto full_path = fmt::format(\"{}/{}\", directory, file_name);\n      struct stat statbuf;\n\n      const int status = stat(full_path.c_str(), &statbuf);\n      if (status < 0) {\n        LOG::warn(\"Failed to stat file {} : {}\", full_path, strerror(errno));\n        continue;\n      }\n\n      switch (statbuf.st_mode & S_IFMT) {\n      case S_IFREG:\n        if (files) {\n          files->push_back(FileMeta::from_stat(std::move(full_path), statbuf));\n        }\n        break;\n      case S_IFDIR:\n        if (subdirs) {\n          if ((file_name != \".\") && (file_name != \"..\")) {\n            subdirs->push_back(DirMeta::from_stat(std::move(full_path), statbuf));\n          }\n        }\n        break;\n      default:\n        continue;\n      }\n    }\n  }\n}\n\nstd::vector<FileMeta> list_directory_files(char const *directory)\n{\n  std::vector<FileMeta> result;\n  list_directory_contents(directory, &result, nullptr);\n  return result;\n}\n\nstd::vector<DirMeta> list_directory_subdirs(char const *directory)\n{\n  std::vector<DirMeta> result;\n  list_directory_contents(directory, nullptr, &result);\n  return result;\n}\n\nuint64_t calculate_directory_size(char const *directory, size_t max_depth)\n{\n  struct Dir {\n    std::string path;\n    size_t depth;\n  };\n\n  std::deque<Dir> dirs;\n  dirs.push_back({.path = directory, .depth = 0});\n\n  uint64_t total_size = 0;\n\n  while (!dirs.empty()) {\n    auto current = dirs.front();\n    dirs.pop_front();\n\n    std::vector<FileMeta> files;\n    std::vector<DirMeta> subdirs;\n    list_directory_contents(current.path.c_str(), &files, &subdirs);\n\n    for (auto const &file : files) {\n      if (file.size_bytes > 0) {\n        total_size += file.size_bytes;\n      }\n    }\n\n    if (current.depth < max_depth) {\n      for (auto const &subdir : subdirs) {\n        dirs.push_back({.path = subdir.path, .depth = current.depth + 1});\n      }\n    }\n  }\n\n  return total_size;\n}\n\nvoid cleanup_directory(char const *directory, const int64_t max_file_count, const int64_t max_total_size_bytes)\n{\n  // Get the contents of dir in reverse chronological order.\n  std::vector<FileMeta> files = list_directory_files(directory);\n  std::sort(files.begin(), files.end(), [](const FileMeta &lhs, const FileMeta &rhs) {\n    return lhs.modify_nanotimestamp > rhs.modify_nanotimestamp;\n  });\n\n  // Find the list of files to delete.\n  std::vector<const FileMeta *> files_to_delete;\n  int64_t file_count = 0;\n  int64_t total_size_bytes = 0;\n  for (const FileMeta &file : files) {\n    file_count += 1;\n    total_size_bytes += file.size_bytes;\n\n    if (file_count > max_file_count || total_size_bytes > max_total_size_bytes) {\n      files_to_delete.push_back(&file);\n    }\n  }\n\n  // Delete the files.\n  for (const FileMeta *file : files_to_delete) {\n    const int status = remove(file->path.c_str());\n    if (status == 0 /* success */) {\n      LOG::info(\"Deleted file {}\", file->path);\n    } else {\n      LOG::warn(\"Failed to delete file {} : {}\", file->path, strerror(errno));\n    }\n  }\n}\n\nvoid cleanup_directory_subdirs(\n    char const *directory,\n    uint64_t const max_subdir_count,\n    uint64_t const max_total_size_bytes,\n    size_t max_depth,\n    std::string_view suffix)\n{\n  std::vector<DirMeta> subdirs = list_directory_subdirs(directory);\n\n  // Only consider subdirs that end with the specified suffix.\n  if (!suffix.empty()) {\n    auto filtered = std::remove_if(\n        std::begin(subdirs), std::end(subdirs), [suffix](auto &&subdir) { return !views::ends_with(subdir.path, suffix); });\n    subdirs.erase(filtered, std::end(subdirs));\n  }\n\n  // Sort subdirs in the reverse chronological order.\n  std::sort(subdirs.begin(), subdirs.end(), [](const DirMeta &lhs, const DirMeta &rhs) {\n    return lhs.modify_nanotimestamp > rhs.modify_nanotimestamp;\n  });\n\n  std::vector<const DirMeta *> subdirs_to_delete;\n  uint64_t subdir_count = 0;\n  uint64_t total_size_bytes = 0;\n  for (const DirMeta &subdir : subdirs) {\n    subdir_count += 1;\n    total_size_bytes += calculate_directory_size(subdir.path.c_str(), max_depth);\n\n    if (subdir_count > max_subdir_count || total_size_bytes > max_total_size_bytes) {\n      subdirs_to_delete.push_back(&subdir);\n    }\n  }\n\n  for (auto const *subdir : subdirs_to_delete) {\n    std::error_code ec;\n    if (std::filesystem::remove_all(subdir->path, ec)) {\n      LOG::info(\"Deleted directory {}\", subdir->path);\n    } else {\n      const std::string error_message = ec.message();\n      LOG::warn(\"Failed to delete directory {} : {} ({})\", subdir->path, error_message, ec.value());\n    }\n  }\n}\n\nExpected<std::vector<std::uint8_t>, std::error_code> read_file(char const *path)\n{\n  std::vector<std::uint8_t> buffer;\n\n  if (auto result = read_file(path, buffer); !result) {\n    return {unexpected, std::move(result.error())};\n  }\n\n  // legit move - NRVO wouldn't apply because the return type differs\n  return std::move(buffer);\n}\n\nExpected<std::string, std::error_code> read_file_as_string(char const *path)\n{\n  auto result = read_file(path);\n  if (!result) {\n    return {unexpected, result.error()};\n  }\n  return std::string(reinterpret_cast<char const *>(result->data()), result->size());\n}\n\nExpected<std::size_t, std::error_code> read_file(char const *path, std::vector<std::uint8_t> &buffer)\n{\n  FileDescriptor fd;\n\n  if (auto error = fd.open(path, FileDescriptor::Access::read_only, FileDescriptor::Positioning::beginning)) {\n    return {unexpected, std::move(error)};\n  }\n\n  if (auto const result = fd.read_all(buffer)) {\n    return *result;\n  } else {\n    return {unexpected, result.error()};\n  }\n}\n\nstd::error_code write_file(char const *path, std::string_view data)\n{\n  FileDescriptor fd;\n\n  if (auto error = fd.create(path, FileDescriptor::Access::write_only)) {\n    return error;\n  }\n\n  return fd.write_all(data);\n}\n\nFileDescriptor::~FileDescriptor()\n{\n  if (valid()) {\n    close();\n  }\n}\n\nExpected<std::size_t, std::error_code> FileDescriptor::read(void *buffer, std::size_t size)\n{\n  ssize_t const result = ::read(fd_, buffer, size);\n\n  if (result < 0) {\n    return {unexpected, std::error_code(errno, std::generic_category())};\n  }\n\n  return result;\n}\n\nExpected<std::size_t, std::error_code> FileDescriptor::read_all(char *buffer, std::size_t size)\n{\n  auto const begin = buffer;\n\n  for (auto const end = buffer + size;;) {\n    assert(buffer <= end);\n    if (auto const partial = ::read(fd_, buffer, end - buffer); partial < 0) {\n      return {unexpected, std::error_code(errno, std::generic_category())};\n    } else if (partial) {\n      buffer += partial;\n    } else {\n      // EOF reached\n      break;\n    }\n  }\n\n  return buffer - begin;\n}\n\nExpected<std::size_t, std::error_code> FileDescriptor::read_all(std::vector<std::uint8_t> &out)\n{\n  std::size_t const offset = out.size();\n\n  // failure to stat doesn't prevent attempting to read the file\n  // it simply prevents pre-allocating the whole buffer at once\n  if (struct stat info; !::fstat(fd_, &info)) {\n    out.resize(offset + info.st_size);\n  }\n\n  constexpr std::size_t const chunk_size = 1024;\n  std::size_t total = offset;\n\n  for (;;) {\n    // should only happen if stat fails or if the file grows after that\n    if (out.size() == total) {\n      out.resize(total + chunk_size);\n    }\n\n    assert(total < out.size());\n    if (auto const partial = ::read(fd_, out.data() + total, out.size() - total); partial < 0) {\n      return {unexpected, std::error_code(errno, std::generic_category())};\n    } else if (partial) {\n      total += partial;\n    } else {\n      // EOF reached\n      break;\n    }\n  }\n\n  // truncate excess buffer\n  out.resize(total);\n\n  return total - offset;\n}\n\nstd::error_code FileDescriptor::write_all(std::string_view buffer)\n{\n  auto begin = buffer.data();\n  auto const end = begin + buffer.size();\n\n  while (begin < end) {\n    auto const size = end - begin;\n    auto const written = ::write(fd_, begin, size);\n\n    if (written < 0) {\n      return std::error_code(errno, std::generic_category());\n    }\n\n    assert(written <= size);\n    begin += written;\n  }\n\n  return {};\n}\n\nstd::error_code FileDescriptor::flush_data()\n{\n  return std::error_code{::fdatasync(fd_) ? errno : 0, std::generic_category()};\n}\n\nstd::error_code FileDescriptor::flush()\n{\n  return std::error_code{::fsync(fd_) ? errno : 0, std::generic_category()};\n}\n\nstd::error_code FileDescriptor::close()\n{\n  auto const error = ::close(fd_) ? errno : 0;\n  fd_ = INVALID_FD;\n  return {error, std::generic_category()};\n}\n\nnamespace {\n\nstatic constexpr int USER_PERMISSIONS_SELECTOR[] = {\n    0, (S_IRUSR), (S_IWUSR), (S_IRUSR | S_IWUSR), (S_IXUSR), (S_IRUSR | S_IXUSR), (S_IWUSR | S_IXUSR), (S_IRWXU)};\n\nstatic constexpr int GROUP_PERMISSIONS_SELECTOR[] = {\n    0, (S_IRGRP), (S_IWGRP), (S_IRGRP | S_IWGRP), (S_IXGRP), (S_IRGRP | S_IXGRP), (S_IWGRP | S_IXGRP), (S_IRWXG)};\n\nstatic constexpr int OTHERS_PERMISSIONS_SELECTOR[] = {\n    0, (S_IROTH), (S_IWOTH), (S_IROTH | S_IWOTH), (S_IXOTH), (S_IROTH | S_IXOTH), (S_IWOTH | S_IXOTH), (S_IRWXO)};\n\n} // namespace\n\nstd::error_code FileDescriptor::open(char const *path, Access access, Positioning position, Mode mode)\n{\n  assert(!valid());\n\n  int const flags = static_cast<int>(access) | static_cast<int>(position) | static_cast<int>(mode);\n\n  fd_ = ::open(path, flags);\n\n  return std::error_code{fd_ == INVALID_FD ? errno : 0, std::generic_category()};\n}\n\nstd::error_code FileDescriptor::create(\n    char const *path,\n    Access access,\n    Positioning position,\n    Permission user_permission,\n    Permission group_permission,\n    Permission others_permission,\n    Mode mode)\n{\n  assert(!valid());\n\n  int const flags = static_cast<int>(access) | static_cast<int>(position) | static_cast<int>(mode) | (O_CREAT);\n\n  int const permissions = USER_PERMISSIONS_SELECTOR[static_cast<int>(user_permission)] |\n                          GROUP_PERMISSIONS_SELECTOR[static_cast<int>(group_permission)] |\n                          OTHERS_PERMISSIONS_SELECTOR[static_cast<int>(others_permission)];\n\n  fd_ = ::open(path, flags, permissions);\n\n  return std::error_code{fd_ == INVALID_FD ? errno : 0, std::generic_category()};\n}\n"
  },
  {
    "path": "util/file_ops.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This file contains helper functions for file- and filesystem-based\n// operations.\n\n#include <util/expected.h>\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <initializer_list>\n#include <string>\n#include <string_view>\n#include <system_error>\n#include <vector>\n\n#include <cstdint>\n\nconstexpr auto MAX_PID_PROC_PATH = \"/proc/sys/kernel/pid_max\";\n\nenum class FileAccess : int { read = R_OK, write = W_OK, execute = X_OK };\n\n// Tells whether the file specified by `path` exists\nbool file_exists(char const *path, std::initializer_list<FileAccess> modes = {});\n\n// Creates the directory `directory` with user read/write/exec permissions.\n// Returns `directory` if the directory was created or already existed, or the\n// error code if the directory failed to be created.\nExpected<char const *, std::error_code> create_directory(char const *directory);\n\n// Struct containing several pieces of file metadata. Feel free to extend this\n// with new fields if desired.\nstruct FileMeta {\n  std::string path;\n  int64_t size_bytes = 0;\n  int64_t modify_nanotimestamp = 0;\n\n  // Constructs a FileMeta object from a stat result.\n  static FileMeta from_stat(std::string path, const struct stat &statbuf);\n};\n\n// Struct containing directory metadata.\nstruct DirMeta {\n  std::string path;\n  int64_t modify_nanotimestamp = 0;\n\n  // Constructs a DirMeta object from a stat result.\n  static DirMeta from_stat(std::string path, const struct stat &statbuf);\n};\n\n// Lists files and subdirectories of `directory`, with no guarantee of ordering.\n// Files and subdirectories are appended to `files` and `subdirs`, respectively.\n// Existing content of `files` and `subdirs` will not be touched.\n// Special directory entries `.` and `..` will not be included in the list.\n// Does not throw on failure.\nvoid list_directory_contents(char const *directory, std::vector<FileMeta> *files, std::vector<DirMeta> *subdirs);\n\n// Lists file contents of `directory`.\nstd::vector<FileMeta> list_directory_files(char const *directory);\n\n// Lists subdirectories `directory`.\nstd::vector<DirMeta> list_directory_subdirs(char const *directory);\n\n// Calculates the total directory size by summing up files sizes.\n// Descends into subdirectories up to `max_depth` levels (does not descend into\n// subdirectories if `max_depth` is 0).\nuint64_t calculate_directory_size(char const *directory, size_t max_depth);\n\n// Cleans up the contents of `dir` such that it contains no more than\n// `max_file_count` files and a total size less than `max_total_size_bytes`.\n// Removes older files first.\nvoid cleanup_directory(char const *directory, int64_t max_file_count, int64_t max_total_size_bytes);\n\n// Cleans up the contents of `directory` such that it contains no more than\n// `max_subdir_count` subdirectories, and that subdirectories take no more than\n// `max_total_size_bytes` of space.\n//\n// The `max_depth` parameter controls the depth to which subdirectories are\n// descendend into when their sizes are calculated\n// (see `calculate_directory_size` function).\n//\n// If `suffix` is specified, then only subdirectories that end with that suffix\n// are considered.\n//\n// Removes older subdirectories first.\nvoid cleanup_directory_subdirs(\n    char const *directory,\n    uint64_t max_subdir_count,\n    uint64_t max_total_size_bytes,\n    size_t max_depth,\n    std::string_view suffix = {});\n\n// reads the contents of given file - no decoding is performed\n// returns the file contents on success, or the error information otherwise\nExpected<std::vector<std::uint8_t>, std::error_code> read_file(char const *path);\n\n// reads the contents of given file - no decoding is performed\n// returns the file contents on success, or the error information otherwise\nExpected<std::string, std::error_code> read_file_as_string(char const *path);\n\n// reads the contents of given file into the given buffer\n// no decoding is performed, the string's characters represent the file's bytes\n// returns the amount of bytes read on success, or the error information otherwise\n// note: the buffer won't be cleared beforehand, the file's contents will be appended\nExpected<std::size_t, std::error_code> read_file(char const *path, std::vector<std::uint8_t> &buffer);\n\nstd::error_code write_file(char const *path, std::string_view data);\n\nclass FileDescriptor {\npublic:\n  static constexpr int INVALID_FD = -1;\n\n  FileDescriptor() : fd_(INVALID_FD) {}\n\n  explicit FileDescriptor(int fd) : fd_(fd) {}\n\n  FileDescriptor(FileDescriptor const &) = delete;\n  FileDescriptor(FileDescriptor &&other) : fd_(other.fd_) { other.fd_ = INVALID_FD; }\n\n  ~FileDescriptor();\n\n  enum class Access : int { read_only = (O_RDONLY), write_only = (O_WRONLY), read_write = (O_RDWR) };\n\n  enum class Positioning : int { beginning = 0, append = (O_APPEND), truncate = (O_TRUNC) };\n\n  enum class Mode : int { none = 0, close_on_exec = (O_CLOEXEC) };\n\n  enum class Permission : int {\n    none = 0,\n    read = 1,\n    write = 2,\n    read_write = (read | write),\n    exec = 4,\n    read_exec = (read | exec),\n    write_exec = (write | exec),\n    read_write_exec = (read | write | exec)\n  };\n\n  // reads the contents of this file into the given buffer\n  // performs as many read operations as possible in order to fill the buffer\n  // no decoding is performed, the file's bytes are returned raw\n  // returns the amount of bytes read on success, or the error information otherwise\n  // if less than `size` bytes were returned then this function has reached EOF\n  Expected<std::size_t, std::error_code> read_all(char *buffer, std::size_t size);\n\n  // reads the contents of this file into the given buffer, growing it as needed\n  // no decoding is performed, the file's bytes are returned raw\n  // returns the amount of bytes read on success, or the error information otherwise\n  Expected<std::size_t, std::error_code> read_all(std::vector<std::uint8_t> &out);\n\n  // writes the contents of `buffer` to file `fd`\n  // returns 0 on success or an error code on failure (see write(2) for details)\n  std::error_code write_all(std::string_view buffer);\n\n  int fd() const { return fd_; }\n  bool valid() const { return fd_ != INVALID_FD; }\n\n  // flushes modified data but avoids flushing metadata - fdatasync(2)\n  // the return value evaluates to false on success or represents the error otherwise\n  std::error_code flush_data();\n\n  // flushes modified data and metadata - fsync(2)\n  // the return value evaluates to false on success or represents the error otherwise\n  std::error_code flush();\n\n  int operator*() const { return fd(); }\n  explicit operator bool() const { return valid(); }\n  bool operator!() const { return !valid(); }\n\n  FileDescriptor &operator=(FileDescriptor &&other)\n  {\n    if (valid()) {\n      close();\n    }\n\n    std::swap(fd_, other.fd_);\n    return *this;\n  }\n\n  std::error_code\n  open(char const *path, Access access, Positioning position = Positioning::beginning, Mode mode = Mode::close_on_exec);\n  std::error_code create(\n      char const *path,\n      Access access,\n      Positioning position = Positioning::truncate,\n      Permission user_permission = Permission::read_write,\n      Permission group_permission = Permission::read_write,\n      Permission others_permission = Permission::none,\n      Mode mode = Mode::close_on_exec);\n\n  std::error_code close();\n\n  Expected<std::size_t, std::error_code> read(void *buffer, std::size_t size);\n\n  static FileDescriptor std_in() { return FileDescriptor{0}; }\n  static FileDescriptor std_out() { return FileDescriptor{1}; }\n  static FileDescriptor std_err() { return FileDescriptor{2}; }\n\nprivate:\n  int fd_;\n};\n"
  },
  {
    "path": "util/fixed_hash.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/iterable_bitmap.h>\n#include <util/pool.h>\n\n#ifndef NDEBUG_SANITIZER\n#include <absl/container/flat_hash_map.h>\n#else\n#include <unordered_map>\n#endif\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <functional>\n#include <string.h>\n#include <type_traits>\n\ntemplate <\n    class Key,\n    class T,\n    std::size_t ELEM_POOL_SZ,\n    class Hash,\n    class KeyEqual = std::equal_to<Key>,\n    class Allocator = std::allocator<T>>\nclass FixedHash {\npublic:\n  using key_type = Key;\n  using value_type = T;\n  using pool_type = Pool<value_type, ELEM_POOL_SZ, Allocator>;\n  using index_type = typename pool_type::index_type;\n  using size_type = std::size_t;\n  // Use default allocator for the map to avoid allocator rebind issues with Abseil.\n#ifndef NDEBUG_SANITIZER\n  using map_type = absl::flat_hash_map<key_type, index_type, Hash, KeyEqual>;\n#else\n  using map_type = std::unordered_map<key_type, index_type, Hash, KeyEqual>;\n#endif\n  using bitmap_type = typename pool_type::bitmap_type;\n  using position = typename pool_type::position;\n  static constexpr index_type invalid = pool_type::invalid;\n\n  /**\n   * c'tor\n   */\n  FixedHash() {}\n\n  bool empty() const { return pool_.empty(); }\n  bool full() const { return pool_.full(); }\n  size_type size() const { return pool_.size(); }\n  size_type max_size() const { return pool_.max_size(); }\n  size_type capacity() const { return pool_.capacity(); }\n  value_type const &operator[](index_type index) const { return pool_[index]; }\n  value_type &operator[](index_type index) { return pool_[index]; }\n  bitmap_type allocated() const { return pool_.allocated(); }\n\n  template <typename K> bool contains(const K &key) const { return map_.count(key) == 1; }\n\n  /**\n   * Finds the given element.\n   * @returns: the index of the element, or {invalid,nullptr} if not found\n   */\n  template <typename K> position find(const K &key)\n  {\n    auto it = map_.find(key);\n    if (it == map_.end())\n      return {invalid, nullptr};\n\n    /* get the found entry */\n    u32 index = it->second;\n    return {(index_type)index, (value_type *)&pool_[index]};\n  }\n\n  /**\n   * Inserts the value into the hash with given key.\n   * @returns position of the new value, or {invalid,nullptr} if key exists or\n   *  the container is full.\n   */\n  template <typename K, typename... Args> position insert(K &&key, Args &&... args)\n  {\n    if (full())\n      return {invalid, nullptr};\n\n    /* add to the hash */\n    auto it = map_.insert({key, -1});\n    if (!it.second)\n      return {invalid, nullptr};\n    bool disarm = false;\n    DisarmGuard map_guard(disarm, [this, &key] { map_.erase(key); });\n\n    /* add the object to the pool, might throw! */\n    auto pos = pool_.emplace(std::forward<Args>(args)...);\n    if (pos.index == invalid)\n      return {invalid, nullptr}; /* map_guard will free */\n\n    /* okay, we're good! */\n    it.first->second = pos.index; /* save the index in the map */\n    disarm = true;                /* don't want to deallocate */\n    return pos;\n  }\n\n  /**\n   * Erase the value pointed to by key\n   * @return true on success, false if key not found\n   */\n  template <typename K> bool erase(const K &key)\n  {\n    /* find the key */\n    auto it = map_.find(key);\n    if (it == map_.end())\n      return false;\n\n    u32 index = it->second;\n\n    /* remove from pool */\n    pool_.remove(index);\n    /* remove from hash */\n    map_.erase(key);\n\n    return true;\n  }\n\n  typename map_type::const_iterator begin() const { return map_.begin(); }\n  typename map_type::const_iterator end() const { return map_.end(); }\n\n  struct value_iterator {\n    value_iterator(typename map_type::const_iterator i, pool_type &pool) : i_(i), pool_(pool) {}\n\n    value_iterator &operator++()\n    {\n      ++i_;\n      return *this;\n    }\n\n    value_iterator operator++(int)\n    {\n      auto copy = *this;\n      ++*this;\n      return copy;\n    }\n\n    value_type const *operator->() const { return &pool_[i_->second]; }\n    value_type *operator->() { return &pool_[i_->second]; }\n\n    value_type const &operator*() const { return pool_[i_->second]; }\n    value_type &operator*() { return pool_[i_->second]; }\n\n    bool operator==(value_iterator const &rhs) const { return i_ == rhs.i_; }\n    bool operator!=(value_iterator const &rhs) const { return i_ != rhs.i_; }\n\n  private:\n    typename map_type::const_iterator i_;\n    pool_type &pool_;\n  };\n\n  struct values_iterable {\n    values_iterable(typename map_type::const_iterator begin, typename map_type::const_iterator end, pool_type &pool)\n        : begin_(begin), end_(end), pool_(pool)\n    {}\n\n    value_iterator begin() { return {begin_, pool_}; }\n    value_iterator end() { return {end_, pool_}; }\n\n  private:\n    typename map_type::const_iterator begin_;\n    typename map_type::const_iterator end_;\n    pool_type &pool_;\n  };\n\n  values_iterable values() { return {map_.begin(), map_.end(), pool_}; }\n\nprivate:\n  struct DisarmGuard {\n    template <typename F> DisarmGuard(bool &disarm_b, F fn) : fn_(fn), disarm_(disarm_b) {}\n    ~DisarmGuard()\n    {\n      if (!disarm_)\n        fn_();\n    }\n\n  private:\n    std::function<void(void)> fn_;\n    bool &disarm_;\n  };\n\n  map_type map_;\n  pool_type pool_;\n};\n"
  },
  {
    "path": "util/fixed_hash_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/fixed_hash.h>\n\n#include <gtest/gtest.h>\n\n#include <set>\n\ntemplate <typename Hash, typename... Values> void test_values_iteration(Values... values)\n{\n  Hash hash;\n  std::set<typename Hash::value_type> inserted;\n\n  // trick to expand the variadic argument in insert() calls using fold expressions\n  std::size_t const n = ((hash.insert(values, values), inserted.insert(values), 1) + ... + 0);\n  EXPECT_EQ(sizeof...(values), n);\n\n  EXPECT_EQ(sizeof...(values), hash.size());\n  EXPECT_EQ(sizeof...(values), inserted.size());\n\n  std::set<typename Hash::value_type> found;\n  for (auto const &value : hash.values()) {\n    found.insert(value);\n  }\n\n  EXPECT_EQ(inserted.size(), found.size());\n  auto f = found.begin();\n  for (auto i = inserted.begin(); i != inserted.end(); ++i) {\n    ASSERT_NE(f, found.end());\n    EXPECT_EQ(*i, *f);\n    ++f;\n  }\n  EXPECT_EQ(f, found.end());\n}\n\nTEST(fixed_hash, values)\n{\n  test_values_iteration<FixedHash<int, int, 100, std::hash<int>>>();\n\n  test_values_iteration<FixedHash<int, int, 100, std::hash<int>>>(0, 1, 2, 3);\n\n  test_values_iteration<FixedHash<int, int, 100, std::hash<int>>>(0, 10, 20, 30, 40, 50, 60, 70);\n}\n"
  },
  {
    "path": "util/fmt_extensions.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <spdlog/fmt/ostr.h>\n#include <filesystem>\n#include <atomic>\n#include <cstdint>\n\n// Forward declarations to allow specializing fmt::formatter without including\n// the enum headers everywhere.\nenum class PortProtocol : std::uint8_t;\nnamespace reducer {\nenum class TsdbFormat : std::uint16_t;\n}\n\n// Provide fmt::formatter specializations for our lightweight logging wrappers\n// so they are always considered formattable by {fmt}/spdlog, even with\n// stricter fmt v12 checks.\n\nnamespace fmt {\n\n// waived_t<T>: treat as streamable via ostream_formatter\ntemplate <typename T>\nstruct formatter<logger::impl::waived_t<T>> : ostream_formatter {};\n\n// either_t<...>\ntemplate <typename WhenTrue, typename WhenFalse>\nstruct formatter<logger::impl::either_t<WhenTrue, WhenFalse>> : ostream_formatter {};\n\n// surrounded_t<T, Open, Close>\ntemplate <typename T, char Open, char Close>\nstruct formatter<logger::impl::surrounded_t<T, Open, Close>> : ostream_formatter {};\n\n// kv_pair_t<Key, Value, Separator, Open, Close>\ntemplate <typename Key, typename Value, char Separator, char Open, char Close>\nstruct formatter<logger::impl::kv_pair_t<Key, Value, Separator, Open, Close>> : ostream_formatter {};\n\n// callable_t<Fn>\ntemplate <typename Fn>\nstruct formatter<logger::impl::callable_t<Fn>> : ostream_formatter {};\n\n// ClientType enum: stream using its operator<< (defined by enum utilities)\ntemplate <typename Char>\nstruct formatter<ClientType, Char> : ostream_formatter {};\n\n// PortProtocol enum: stream using its operator<< (defined by enum utilities)\ntemplate <typename Char>\nstruct formatter<PortProtocol, Char> : ostream_formatter {};\n\n// reducer::TsdbFormat enum: stream using its operator<< (defined by enum utilities)\ntemplate <typename Char>\nstruct formatter<reducer::TsdbFormat, Char> : ostream_formatter {};\n\n// std::filesystem::path: stream via operator<<\ntemplate <typename Char>\nstruct formatter<std::filesystem::path, Char> : ostream_formatter {};\n\n// Support formatting std::atomic<T> by formatting the contained value.\n// Restrict to arithmetic types to avoid surprising behavior for complex T.\ntemplate <typename T, typename Char>\nstruct formatter<std::atomic<T>, Char> : fmt::formatter<T, Char> {\n  template <typename FormatContext>\n  auto format(std::atomic<T> const &a, FormatContext &ctx) const {\n    if constexpr (std::is_arithmetic_v<T>) {\n      T v = a.load(std::memory_order_relaxed);\n      return fmt::formatter<T, Char>::format(v, ctx);\n    } else {\n      // Fallback: print address if non-arithmetic to avoid compile errors\n      return fmt::formatter<const void *, Char>::format(static_cast<const void *>(&a), ctx);\n    }\n  }\n};\n\n} // namespace fmt\n"
  },
  {
    "path": "util/gauge.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <utility>\n\n#include <cstdlib>\n\nnamespace data {\n\ntemplate <typename T> struct Gauge {\n  Gauge() = default;\n\n  Gauge(T data) : value_(data), min_(data), max_(data), sum_(std::move(data)), count_(1), changed_(true) {}\n\n  template <typename Out = double> Out average() const;\n\n  T const &value() const { return value_; }\n  T const &min() const { return min_; }\n  T const &max() const { return max_; }\n  T const &sum() const { return sum_; }\n\n  Gauge &operator+=(T const &value);\n  Gauge &operator+=(Gauge const &value);\n\n  bool changed() const { return changed_; }\n\n  void reset();\n\n  std::size_t count() const { return count_; }\n  bool empty() const { return !count_; }\n\nprivate:\n  T value_ = {};\n  T min_ = {};\n  T max_ = {};\n  T sum_ = {};\n  std::size_t count_ = 0;\n  bool changed_ = false;\n};\n\n} // namespace data\n\n#include <util/gauge.inl>\n"
  },
  {
    "path": "util/gauge.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\nnamespace data {\n\ntemplate <typename T> template <typename Out> Out Gauge<T>::average() const\n{\n  switch (count_) {\n  case 0:\n    return Out{};\n  case 1:\n    return static_cast<Out>(sum_);\n  default:\n    return static_cast<Out>(sum_) / count_;\n  }\n}\n\ntemplate <typename T> Gauge<T> &Gauge<T>::operator+=(T const &value)\n{\n  changed_ = value_ != value;\n  value_ = value;\n\n  if (empty()) {\n    min_ = value;\n    max_ = value;\n  } else {\n    if (value < min_) {\n      min_ = value;\n    }\n    if (value > max_) {\n      max_ = value;\n    }\n  }\n\n  sum_ += value;\n  ++count_;\n\n  return *this;\n}\n\ntemplate <typename T> Gauge<T> &Gauge<T>::operator+=(Gauge const &value)\n{\n  changed_ = value_ != value.value_ || min_ != value.min_ || max_ != value.max_ || sum_ != value.sum_ || count_ != value.count_;\n\n  value_ = value.value_;\n\n  if (value.min_ < min_) {\n    min_ = value.min_;\n  }\n  if (value.max_ > max_) {\n    max_ = value.max_;\n  }\n  sum_ += value.sum_;\n  count_ += value.count_;\n\n  return *this;\n}\n\ntemplate <typename T> void Gauge<T>::reset()\n{\n  value_ = {};\n  min_ = {};\n  max_ = {};\n  sum_ = {};\n  count_ = 0;\n}\n\ntemplate <typename Out, typename T> Out &&operator<<(Out &&out, Gauge<T> const &value)\n{\n  out << \"{value=\" << value.value() << \" min=\" << value.min() << \" max=\" << value.max() << \" avg=\" << value.average() << '}';\n\n  return std::forward<Out>(out);\n}\n\n} // namespace data\n"
  },
  {
    "path": "util/gauge_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/gauge.h>\n\n#include <platform/types.h>\n\n#include <gtest/gtest.h>\n\n#include <chrono>\n\nnamespace data {\n\nTEST(gauge_int, default_ctor)\n{\n  Gauge<int> const gauge;\n  EXPECT_TRUE(gauge.empty());\n  EXPECT_EQ(0ul, gauge.count());\n  EXPECT_EQ(0, gauge.min());\n  EXPECT_EQ(0, gauge.max());\n  EXPECT_EQ(0, gauge.sum());\n  EXPECT_EQ(0.0, gauge.average());\n}\n\nTEST(gauge_int, cast_constructor)\n{\n  Gauge<int> const gauge(10);\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(1ul, gauge.count());\n  EXPECT_EQ(10, gauge.min());\n  EXPECT_EQ(10, gauge.max());\n  EXPECT_EQ(10, gauge.sum());\n  EXPECT_EQ(10.0, gauge.average());\n}\n\nTEST(gauge_int, default_ctor_add_4_values)\n{\n  Gauge<int> gauge;\n  gauge += 10;\n  gauge += 20;\n  gauge += 30;\n  gauge += 40;\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(4ul, gauge.count());\n  EXPECT_EQ(10, gauge.min());\n  EXPECT_EQ(40, gauge.max());\n  EXPECT_EQ(100, gauge.sum());\n  EXPECT_EQ(25.0, gauge.average());\n}\n\nTEST(gauge_int, cast_ctor_add_4_values)\n{\n  Gauge<int> gauge(60);\n  gauge += 90;\n  gauge += 70;\n  gauge += 50;\n  gauge += 80;\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(5ul, gauge.count());\n  EXPECT_EQ(50, gauge.min());\n  EXPECT_EQ(90, gauge.max());\n  EXPECT_EQ(350, gauge.sum());\n  EXPECT_EQ(70.0, gauge.average());\n}\n\nTEST(gauge_int, merge_assignment)\n{\n  Gauge<int> gauge;\n  gauge += 10;\n  gauge += 20;\n  gauge += 30;\n  gauge += 40;\n\n  Gauge<int> other(60);\n  other += 90;\n  other += 70;\n  other += 50;\n  other += 80;\n\n  gauge += other;\n\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(9ul, gauge.count());\n  EXPECT_EQ(10, gauge.min());\n  EXPECT_EQ(90, gauge.max());\n  EXPECT_EQ(450, gauge.sum());\n  EXPECT_EQ(50.0, gauge.average());\n\n  EXPECT_FALSE(other.empty());\n  EXPECT_EQ(5ul, other.count());\n  EXPECT_EQ(50, other.min());\n  EXPECT_EQ(90, other.max());\n  EXPECT_EQ(350, other.sum());\n  EXPECT_EQ(70.0, other.average());\n}\n\nTEST(gauge_int, reset)\n{\n  Gauge<int> gauge(60);\n  gauge += 90;\n  gauge += 70;\n  gauge += 50;\n  gauge += 80;\n\n  gauge.reset();\n  EXPECT_TRUE(gauge.empty());\n  EXPECT_EQ(0ul, gauge.count());\n  EXPECT_EQ(0, gauge.min());\n  EXPECT_EQ(0, gauge.max());\n  EXPECT_EQ(0, gauge.sum());\n  EXPECT_EQ(0.0, gauge.average());\n}\n\nTEST(gauge_chrono, default_ctor)\n{\n  Gauge<std::chrono::seconds> const gauge;\n  EXPECT_TRUE(gauge.empty());\n  EXPECT_EQ(0ul, gauge.count());\n  EXPECT_EQ(0s, gauge.min());\n  EXPECT_EQ(0s, gauge.max());\n  EXPECT_EQ(0s, gauge.sum());\n  EXPECT_EQ(0ms, gauge.average<std::chrono::seconds>());\n}\n\nTEST(gauge_chrono, cast_constructor)\n{\n  Gauge<std::chrono::seconds> const gauge(10s);\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(1ul, gauge.count());\n  EXPECT_EQ(10s, gauge.min());\n  EXPECT_EQ(10s, gauge.max());\n  EXPECT_EQ(10s, gauge.sum());\n  EXPECT_EQ(10s, gauge.average<std::chrono::seconds>());\n}\n\nTEST(gauge_chrono, default_ctor_add_4_values)\n{\n  Gauge<std::chrono::seconds> gauge;\n  gauge += 10s;\n  gauge += 20s;\n  gauge += 30s;\n  gauge += 40s;\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(4ul, gauge.count());\n  EXPECT_EQ(10s, gauge.min());\n  EXPECT_EQ(40s, gauge.max());\n  EXPECT_EQ(100s, gauge.sum());\n  EXPECT_EQ(25s, gauge.average<std::chrono::seconds>());\n}\n\nTEST(gauge_chrono, cast_ctor_add_4_values)\n{\n  Gauge<std::chrono::seconds> gauge(60s);\n  gauge += 90s;\n  gauge += 70s;\n  gauge += 50s;\n  gauge += 80s;\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(5ul, gauge.count());\n  EXPECT_EQ(50s, gauge.min());\n  EXPECT_EQ(90s, gauge.max());\n  EXPECT_EQ(350s, gauge.sum());\n  EXPECT_EQ(70s, gauge.average<std::chrono::seconds>());\n}\n\nTEST(gauge_chrono, merge_assignment)\n{\n  Gauge<std::chrono::seconds> gauge;\n  gauge += 10s;\n  gauge += 20s;\n  gauge += 30s;\n  gauge += 40s;\n\n  Gauge<std::chrono::seconds> other(60s);\n  other += 90s;\n  other += 70s;\n  other += 50s;\n  other += 80s;\n\n  gauge += other;\n\n  EXPECT_FALSE(gauge.empty());\n  EXPECT_EQ(9ul, gauge.count());\n  EXPECT_EQ(10s, gauge.min());\n  EXPECT_EQ(90s, gauge.max());\n  EXPECT_EQ(450s, gauge.sum());\n  EXPECT_EQ(50s, gauge.average<std::chrono::seconds>());\n\n  EXPECT_FALSE(other.empty());\n  EXPECT_EQ(5ul, other.count());\n  EXPECT_EQ(50s, other.min());\n  EXPECT_EQ(90s, other.max());\n  EXPECT_EQ(350s, other.sum());\n  EXPECT_EQ(70s, other.average<std::chrono::seconds>());\n}\n\nTEST(gauge_chrono, reset)\n{\n  Gauge<std::chrono::seconds> gauge(60s);\n  gauge += 90s;\n  gauge += 70s;\n  gauge += 50s;\n  gauge += 80s;\n\n  gauge.reset();\n  EXPECT_TRUE(gauge.empty());\n  EXPECT_EQ(0ul, gauge.count());\n  EXPECT_EQ(0s, gauge.min());\n  EXPECT_EQ(0s, gauge.max());\n  EXPECT_EQ(0s, gauge.sum());\n  EXPECT_EQ(0s, gauge.average<std::chrono::seconds>());\n}\n\n} // namespace data\n"
  },
  {
    "path": "util/gcp_instance_metadata.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/gcp_instance_metadata.h>\n\n#include <common/cloud_platform.h>\n#include <util/json.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/restful.h>\n#include <util/string.h>\n\n#include <optional>\n#include <utility>\n\n#include <cassert>\n\nnamespace {\n\n// metadata fetching described in TODO\nstatic constexpr std::string_view METADATA_FLAVOR_HEADER = \"Metadata-Flavor: Google\";\nstatic constexpr std::string_view METADATA_URL = \"http://metadata.google.internal/\"\n                                                 \"computeMetadata/v1/instance/\"\n                                                 \"?recursive=true\";\n\nstatic constexpr auto METADATA_QUERY_TIMEOUT = 1s;\n\n} // namespace\n\nGcpServiceAccount::GcpServiceAccount(std::string name, std::string email, std::vector<std::string> scopes)\n    : name_(std::move(name)), email_(std::move(email)), scopes_(std::move(scopes))\n{}\n\nvoid GcpServiceAccount::print() const\n{\n  LOG::debug_in(CloudPlatform::gcp, \"    name: {}\", name_);\n  LOG::debug_in(CloudPlatform::gcp, \"    email: {}\", email_);\n  LOG::debug_in(CloudPlatform::gcp, \"    scopes: {}\", scopes_.size());\n  for (std::size_t i = 0; i < scopes_.size(); ++i) {\n    LOG::debug_in(CloudPlatform::gcp, \"    - {}\", scopes_[i]);\n  }\n}\n\nGcpNetworkInterface::GcpNetworkInterface(\n    std::string vpc_id, std::string mac, ip_address_t ip, std::vector<IPv4Address> public_ips)\n    : vpc_id_(std::move(vpc_id)), mac_(std::move(mac)), ip_(std::move(ip)), public_ips_(std::move(public_ips))\n{}\n\nvoid GcpNetworkInterface::print() const\n{\n  LOG::debug_in(CloudPlatform::gcp, \"    vpc id: {}\", vpc_id_);\n  LOG::debug_in(CloudPlatform::gcp, \"    mac: {}\", mac_);\n  std::visit(\n      [&](auto const &address) { LOG::debug_in(CloudPlatform::gcp, \"    ip: {}\", address); },\n      ip_);\n  LOG::debug_in(CloudPlatform::gcp, \"    public ips: {}\", public_ips_.size());\n  for (std::size_t i = 0; i < public_ips_.size(); ++i) {\n    LOG::debug_in(CloudPlatform::gcp, \"    - {}\", public_ips_[i]);\n  }\n}\n\nGcpInstanceMetadata::GcpInstanceMetadata(\n    std::string cluster_name,\n    std::string cluster_location,\n    std::string image,\n    std::string hostname,\n    std::string name,\n    std::int64_t id,\n    std::string az,\n    std::string role,\n    std::string type,\n    std::vector<GcpNetworkInterface> network_interfaces,\n    std::vector<GcpServiceAccount> service_accounts)\n    : cluster_name_(std::move(cluster_name)),\n      cluster_location_(std::move(cluster_location)),\n      image_(std::move(image)),\n      hostname_(std::move(hostname)),\n      name_(std::move(name)),\n      id_(id),\n      az_(std::move(az)),\n      role_(std::move(role)),\n      type_(std::move(type)),\n      network_interfaces_(std::move(network_interfaces)),\n      service_accounts_(std::move(service_accounts))\n{}\n\nvoid GcpInstanceMetadata::print() const\n{\n  LOG::debug_in(CloudPlatform::gcp, \"GCP metadata:\");\n  LOG::debug_in(CloudPlatform::gcp, \"  cluster name: {}\", cluster_name_);\n  LOG::debug_in(CloudPlatform::gcp, \"  cluster location: {}\", cluster_location_);\n  LOG::debug_in(CloudPlatform::gcp, \"  instance image: {}\", image_);\n  LOG::debug_in(CloudPlatform::gcp, \"  hostname: {}\", hostname_);\n  LOG::debug_in(CloudPlatform::gcp, \"  name: {}\", name_);\n  LOG::debug_in(CloudPlatform::gcp, \"  id: {}\", id_);\n  LOG::debug_in(CloudPlatform::gcp, \"  az: {}\", az_);\n  LOG::debug_in(CloudPlatform::gcp, \"  role: {}\", role_);\n  LOG::debug_in(CloudPlatform::gcp, \"  type: {}\", type_);\n\n  LOG::debug_in(CloudPlatform::gcp, \"  network interfaces: {}\", network_interfaces_.size());\n  for (std::size_t i = 0; i < network_interfaces_.size(); ++i) {\n    LOG::debug_in(CloudPlatform::gcp, \"  - network interface: {}\", i);\n    network_interfaces_[i].print();\n  }\n\n  LOG::debug_in(CloudPlatform::gcp, \"  service accounts: {}\", service_accounts_.size());\n  for (std::size_t i = 0; i < service_accounts_.size(); ++i) {\n    LOG::debug_in(CloudPlatform::gcp, \"  - service account: {}\", i);\n    service_accounts_[i].print();\n  }\n}\n\nExpected<GcpInstanceMetadata, std::runtime_error> GcpInstanceMetadata::fetch(std::chrono::microseconds timeout)\n{\n  RestfulFetcher fetcher({METADATA_FLAVOR_HEADER});\n\n  auto const metadata = fetcher.sync_fetch<nlohmann::json>(\n      \"Google Cloud Platform instance metadata\",\n      std::string(METADATA_URL),\n      [](std::string_view json) -> Expected<nlohmann::json, std::runtime_error> {\n        try {\n          return nlohmann::json::parse(json);\n        } catch (std::exception const &e) {\n          return {unexpected, e.what()};\n        }\n      },\n      METADATA_QUERY_TIMEOUT);\n\n  if (!metadata) {\n    return {unexpected, std::move(metadata.error())};\n  }\n\n  std::vector<GcpNetworkInterface> network_interfaces;\n  if (auto const interfaces = follow_path(*metadata, \"networkInterfaces\"); interfaces && interfaces->is_array()) {\n    for (auto const &interface : *interfaces) {\n      std::vector<IPv4Address> public_ips;\n      if (auto const access_configs = follow_path(interface, \"accessConfigs\"); access_configs && access_configs->is_array()) {\n        for (auto const &access_config : *access_configs) {\n          if (auto public_ip = IPv4Address::parse(get_zstring_view(access_config, \"externalIp\").data())) {\n            public_ips.push_back(std::move(*public_ip));\n          }\n        }\n      }\n\n      // TODO: double-check how ipv6 manifests itself\n      std::optional<GcpNetworkInterface::ip_address_t> ip_address;\n      {\n        auto const ip = get_zstring_view(interface, \"ip\");\n        if (auto ipv4 = IPv4Address::parse(ip.data())) {\n          ip_address.emplace(std::in_place_type<IPv4Address>, std::move(ipv4.value()));\n        } else if (auto ipv6 = IPv6Address::parse(ip.data())) {\n          assert(public_ips.empty());\n          ip_address.emplace(std::in_place_type<IPv6Address>, std::move(ipv6.value()));\n        } else {\n          continue;\n        }\n      }\n\n      network_interfaces.emplace_back(\n          std::string(last_token(get_string_view(interface, \"network\"), '/')),\n          std::string(get_string_view(interface, \"mac\")),\n          std::move(*ip_address),\n          std::move(public_ips));\n    }\n  }\n\n  std::vector<GcpServiceAccount> service_accounts;\n  if (auto const accounts = follow_path(*metadata, \"serviceAccounts\"); accounts && accounts->is_object()) {\n    for (auto const &account_item : accounts->items()) {\n      std::vector<std::string> scopes;\n      auto const &account = account_item.value();\n      if (auto const account_scopes = follow_path(account, \"scopes\"); account_scopes && account_scopes->is_array()) {\n        for (auto const &scope : *account_scopes) {\n          if (auto const scope_string = try_get_string(scope)) {\n            scopes.push_back(*scope_string);\n          }\n        }\n      }\n\n      service_accounts.emplace_back(\n          std::string(get_string_view(account_item.key())), std::string(get_string_view(account, \"email\")), std::move(scopes));\n    }\n  }\n\n  auto const name = get_string_view(*metadata, \"name\");\n  auto const id = try_get_int(*metadata, \"id\");\n\n  return GcpInstanceMetadata{\n      std::string(get_string_view(*metadata, \"attributes\", \"cluster-name\")),\n      std::string(get_string_view(*metadata, \"attributes\", \"cluster-location\")),\n      std::string(get_string_view(*metadata, \"image\")),\n      std::string(get_string_view(*metadata, \"hostname\")),\n      std::string(name),\n      id.value_or(0),\n      std::string(last_token(get_string_view(*metadata, \"zone\"), '/')),\n      // is this the best attribute to use as role?\n      service_accounts.empty() ? std::string(name) : service_accounts.front().name(),\n      std::string(last_token(get_string_view(*metadata, \"machineType\"), '/')),\n      std::move(network_interfaces),\n      std::move(service_accounts)\n\n  };\n}\n"
  },
  {
    "path": "util/gcp_instance_metadata.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/expected.h>\n#include <util/ip_address.h>\n\n#include <chrono>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <variant>\n#include <vector>\n\nclass GcpServiceAccount {\npublic:\n  GcpServiceAccount(std::string name, std::string email, std::vector<std::string> scopes);\n\n  std::string const &name() const { return name_; }\n  std::string const &email() const { return email_; }\n  std::vector<std::string> const &scopes() const { return scopes_; }\n\n  void print() const;\n\nprivate:\n  std::string name_;\n  std::string email_;\n  std::vector<std::string> scopes_;\n};\n\nclass GcpNetworkInterface {\npublic:\n  using ip_address_t = std::variant<IPv4Address, IPv6Address>;\n\n  GcpNetworkInterface(std::string vpc_id, std::string mac, ip_address_t ip, std::vector<IPv4Address> public_ips);\n\n  std::string const &vpc_id() const { return vpc_id_; }\n  std::string const &mac() const { return mac_; }\n\n  // nullptr if not an ipv4\n  IPv4Address const *ipv4() const { return std::holds_alternative<IPv4Address>(ip_) ? &std::get<IPv4Address>(ip_) : nullptr; }\n\n  // nullptr if not an ipv6\n  IPv6Address const *ipv6() const { return std::holds_alternative<IPv6Address>(ip_) ? &std::get<IPv6Address>(ip_) : nullptr; }\n\n  std::vector<IPv4Address> const &public_ips() const { return public_ips_; }\n\n  void print() const;\n\nprivate:\n  std::string vpc_id_;\n  std::string mac_;\n  ip_address_t ip_;\n  std::vector<IPv4Address> public_ips_;\n};\n\nclass GcpInstanceMetadata {\npublic:\n  GcpInstanceMetadata(\n      std::string cluster_name,\n      std::string cluster_location,\n      std::string image,\n      std::string hostname,\n      std::string name,\n      std::int64_t id,\n      std::string az,\n      std::string role,\n      std::string type,\n      std::vector<GcpNetworkInterface> network_interfaces,\n      std::vector<GcpServiceAccount> service_accounts);\n\n  std::string const &cluster_name() const { return cluster_name_; }\n  std::string const &cluster_location() const { return cluster_location_; }\n  std::string const &image() const { return image_; }\n  std::string const &hostname() const { return hostname_; }\n  std::string const &name() const { return name_; }\n  std::int64_t id() const { return id_; }\n  std::string const &az() const { return az_; }\n  std::string const &role() const { return role_; }\n  std::string const &type() const { return type_; }\n  std::vector<GcpNetworkInterface> const &network_interfaces() const { return network_interfaces_; }\n  std::vector<GcpServiceAccount> const &service_accounts() const { return service_accounts_; }\n\n  void print() const;\n\n  static Expected<GcpInstanceMetadata, std::runtime_error> fetch(std::chrono::microseconds timeout);\n\nprivate:\n  std::string cluster_name_;\n  std::string cluster_location_;\n  std::string image_;\n  std::string hostname_;\n  std::string name_;\n  std::int64_t id_;\n  std::string az_;\n  std::string role_;\n  std::string type_;\n  std::vector<GcpNetworkInterface> network_interfaces_;\n  std::vector<GcpServiceAccount> service_accounts_;\n};\n"
  },
  {
    "path": "util/histogram.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_HISTOGRAM_H_\n#define INCLUDE_FASTPASS_UTIL_HISTOGRAM_H_\n\n/**\n * @returns the bin the item should go into, if the histogram has n_bins\n * @important: n_bins must be a power of 2\n */\nstatic inline __attribute__((always_inline)) unsigned int histogram_bin(unsigned int n_bins, int item)\n{\n  unsigned int mask = n_bins - 1;\n  /* if item is above h->mask, overflow mask will be ~0, otherwise 0 */\n  unsigned int overflow_mask = ((int)(mask)-item) >> 31;\n  /* if item is negative, shifting right will produce ~0, otherwise 0 */\n  unsigned int neg_mask = (item >> 31);\n  /* if overflow, clasped will be ~0, if negative, clasp will be 0 */\n  unsigned int clasped = (((unsigned int)item) & (~neg_mask)) | overflow_mask;\n  /* increment bin */\n  return (clasped & mask);\n}\n\n#endif /* INCLUDE_FASTPASS_UTIL_HISTOGRAM_H_ */\n"
  },
  {
    "path": "util/ip_address.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/ip_address.h\"\n\n#include <algorithm>\n#include <arpa/inet.h>\n#include <array>\n#include <iostream>\n#include <string>\n\nIPv6Address IPv6Address::from(const std::array<uint64_t, 2> &buffer)\n{\n  return from(reinterpret_cast<const uint8_t *>(buffer.data()));\n}\n\nIPv6Address IPv6Address::from(const std::array<uint32_t, 4> &buffer)\n{\n  return from(reinterpret_cast<const uint8_t *>(buffer.data()));\n}\n\nIPv6Address IPv6Address::from_host_hextets(const Hextets &hextets)\n{\n  Hextets network_hextets;\n  std::transform(hextets.begin(), hextets.end(), network_hextets.begin(), htons);\n  return IPv6Address(network_hextets);\n}\n\nIPv6Address IPv6Address::from(const uint8_t buffer[16])\n{\n  const uint16_t *const buffer16 = reinterpret_cast<const uint16_t *>(buffer);\n  Hextets hextets;\n  std::copy(buffer16, buffer16 + 8, hextets.begin());\n  return IPv6Address(hextets);\n}\n\nExpected<IPv6Address, std::error_code> IPv6Address::parse(char const *ip_string)\n{\n  struct in6_addr address;\n  switch (inet_pton(AF_INET6, ip_string, &address)) {\n  case 0:\n    return {unexpected, std::make_error_code(std::errc::invalid_argument)};\n\n  case 1:\n    return IPv6Address::from(address);\n\n  default:\n    return {unexpected, std::make_error_code(std::errc::address_family_not_supported)};\n  }\n}\n\nu128 IPv6Address::as_int() const\n{\n  u128 result;\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n  // nothing to do\n  result = *reinterpret_cast<u128 const *>(hextets_.data());\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n  auto src = reinterpret_cast<u8 const *>(hextets_.data());\n  auto dst = reinterpret_cast<u8 *>(&result);\n  std::reverse_copy(src, src + sizeof(result), dst);\n#else // __BYTE_ORDER__\n#error \"unsupported byte order\"\n#endif // __BYTE_ORDER__\n  return result;\n}\n\nvoid IPv6Address::write_to(std::array<uint64_t, 2> *const buffer) const\n{\n  write_to(reinterpret_cast<uint8_t *>(buffer->data()));\n}\n\nvoid IPv6Address::write_to(std::array<uint32_t, 4> *const buffer) const\n{\n  write_to(reinterpret_cast<uint8_t *>(buffer->data()));\n}\n\nvoid IPv6Address::write_to(uint8_t buffer[16]) const\n{\n  std::copy(hextets_.begin(), hextets_.end(), reinterpret_cast<uint16_t *>(buffer));\n}\n\nstd::string IPv6Address::str() const\n{\n  char dst[INET6_ADDRSTRLEN];\n  struct in6_addr src;\n  write_to(src.s6_addr);\n\n  if (inet_ntop(AF_INET6, &src, dst, INET6_ADDRSTRLEN) == nullptr) {\n    return \"\";\n  }\n  return std::string(dst);\n}\n\nstd::string IPv6Address::tidy_string() const\n{\n  if (auto ipv4 = to_ipv4(); ipv4.has_value()) {\n    return ipv4->str();\n  } else {\n    return str();\n  }\n}\n\nbool IPv6Address::is_localhost() const\n{\n  return *this == localhost();\n}\n\nbool IPv6Address::is_zero() const\n{\n  for (auto const i : hextets_) {\n    if (i) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool IPv6Address::is_ipv4() const\n{\n  // Is IPv4 if it is in the form of ::FFFF:{octet1}:{octet2}:{octet3}:{octet4}\n  for (int i = 0; i < 5; i++) {\n    if (hextets_[i] != 0)\n      return false;\n  }\n  return hextets_[5] == 0xFFFF;\n}\n\nstd::optional<IPv4Address> IPv6Address::to_ipv4() const\n{\n  if (!is_ipv4()) {\n    return std::nullopt;\n  }\n\n  uint32_t const *quadlet = reinterpret_cast<uint32_t const *>(&hextets_[6]);\n\n  return IPv4Address::from(*quadlet);\n}\n\nbool IPv6Address::operator==(const IPv6Address &rhs) const\n{\n  return hextets_ == rhs.hextets_;\n}\n\nstd::ostream &operator<<(std::ostream &os, const IPv6Address &ipv6)\n{\n  return os << ipv6.str();\n}\n\nIPv4Address IPv4Address::from(const uint32_t buffer)\n{\n  union {\n    uint32_t int_val;\n    std::array<uint8_t, 4> bytes;\n  } rep;\n  rep.int_val = buffer;\n  return IPv4Address(rep.bytes);\n}\n\nExpected<IPv4Address, std::error_code> IPv4Address::parse(char const *ip_string)\n{\n  struct in_addr address;\n  switch (inet_pton(AF_INET, ip_string, &address)) {\n  case 0:\n    return {unexpected, std::make_error_code(std::errc::invalid_argument)};\n\n  case 1:\n    return IPv4Address::from(address);\n\n  default:\n    return {unexpected, std::make_error_code(std::errc::address_family_not_supported)};\n  }\n}\n\nuint32_t IPv4Address::as_int() const\n{\n  return *reinterpret_cast<const uint32_t *>(octets_.data());\n}\n\nstd::string IPv4Address::str() const\n{\n  char dst[INET_ADDRSTRLEN];\n  struct in_addr src {\n    .s_addr = as_int(),\n  };\n\n  if (inet_ntop(AF_INET, &src, dst, INET_ADDRSTRLEN) == nullptr) {\n    return \"\";\n  }\n  return std::string(dst);\n}\n\nbool IPv4Address::is_localhost() const\n{\n  const uint32_t kLocalhostPrefix = IPv4Address({127, 0, 0, 0}).as_int();\n  const uint32_t kLocalhostMask = IPv4Address({0xff, 0xff, 0xff, 0}).as_int();\n  return (as_int() & kLocalhostMask) == kLocalhostPrefix;\n}\n\nbool IPv4Address::is_zero() const\n{\n  for (auto const i : octets_) {\n    if (i) {\n      return false;\n    }\n  }\n  return true;\n}\n\nIPv6Address IPv4Address::to_ipv6() const\n{\n  if (is_localhost()) {\n    return IPv6Address::localhost();\n  }\n  uint8_t buffer[16] = {};\n  std::copy(octets_.begin(), octets_.end(), &buffer[12]);\n  buffer[10] = 0xff;\n  buffer[11] = 0xff;\n  return IPv6Address::from(buffer);\n}\n\nbool IPv4Address::operator==(const IPv4Address &rhs) const\n{\n  return octets_ == rhs.octets_;\n}\n\nstd::ostream &operator<<(std::ostream &os, const IPv4Address &ipv4)\n{\n  return os << ipv4.str();\n}\n"
  },
  {
    "path": "util/ip_address.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This library contains classes for performing conversions to/from IP\n// addresses and other representations.\n\n#include <platform/types.h>\n#include <util/expected.h>\n#include <util/short_string.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <array>\n#include <iostream>\n#include <netinet/in.h>\n#include <optional>\n#include <string>\n#include <system_error>\n\nclass IPv4Address;\n\n// Class for managing conversions to/from IPv6 addresses.\nclass IPv6Address {\npublic:\n  using Hextets = std::array<uint16_t, 8>;\n\n  // Constructs an all-zero (i.e. \"::\") IPv6 address.\n  constexpr IPv6Address() {}\n\n  // Constructs an address from the provided host byte order hextets.\n  static IPv6Address from_host_hextets(const Hextets &hextets);\n\n  // Parses the IPv6 address from the buffer. Assumes network byte order.\n  static IPv6Address from(const std::array<uint64_t, 2> &buffer);\n  static IPv6Address from(const std::array<uint32_t, 4> &buffer);\n  static IPv6Address from(const uint8_t buffer[16]);\n\n  static IPv6Address from(struct in6_addr const &address) { return IPv6Address::from(address.s6_addr); }\n\n  static IPv6Address localhost() { return IPv6Address::from_host_hextets({0, 0, 0, 0, 0, 0, 0, 1}); }\n\n  // Parses the null-terminated string representation of an IPv6 address.\n  //\n  // Returns the `IPv6Address` representation if the string represents a valid IPv6 address,\n  // or an error code otherwise.\n  //\n  // Example:\n  //\n  //  auto ip_string = \"::1\";\n  //  auto ip = IPv6Address::parse(ip_string);\n  //\n  //  if (!ip) {\n  //    LOG::error(\"failed to parse ip address with error code {}\", ip.error());\n  //  } else {\n  //    assert(raw_ip->is_localhost());\n  //  }\n  static Expected<IPv6Address, std::error_code> parse(char const *ip_string);\n\n  // Writes the IPv6 address to the buffers. Writes in network byte order.\n  void write_to(std::array<uint64_t, 2> *buffer) const;\n  void write_to(std::array<uint32_t, 4> *buffer) const;\n  void write_to(uint8_t buffer[16]) const;\n\n  // Returns the 128-bit integer representation of this address.\n  u128 as_int() const;\n\n  // A human-readable string representation in IPv6 format.\n  std::string str() const;\n\n  // A human-readable string representation in IPv6 format for IPv6 addresses\n  // and IPv4 format for IPv4 addresses.\n  std::string tidy_string() const;\n\n  // True if this address represents localhost.\n  bool is_localhost() const;\n\n  // tells whether all bytes in this address are 0\n  bool is_zero() const;\n\n  // True if this is an IPv6 of an IPv4 address.\n  bool is_ipv4() const;\n\n  // If IPv6-encoded IPv4 address, returns corresponding IPv4 address.\n  // Returns nullopt otherwise.\n  std::optional<IPv4Address> to_ipv4() const;\n\n  short_string<16> bytes_view() const { return {reinterpret_cast<char const *>(hextets_.data()), 16}; }\n\n  bool operator==(const IPv6Address &rhs) const;\n\n  template <typename H> friend H AbslHashValue(H hash_state, const IPv6Address &addr);\n\nprivate:\n  // Expects the hextets to be network byte order.\n  explicit IPv6Address(const Hextets &hextets) : hextets_(hextets) {}\n\n  Hextets hextets_ = {};\n};\n\ntemplate <typename H> H AbslHashValue(H hash_state, const IPv6Address &addr)\n{\n  return H::combine(std::move(hash_state), addr.hextets_);\n}\n\nstd::ostream &operator<<(std::ostream &os, const IPv6Address &ipv6);\n\n// A libfmt formatter for IPv6Address, used to print addresses in LOG::trace\n// etc.\nnamespace fmt {\ntemplate <> struct formatter<IPv6Address> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n\n  template <typename FormatContext> auto format(const IPv6Address &p, FormatContext &ctx) const\n  {\n    return format_to(ctx.out(), \"{}\", p.str());\n  }\n};\n} // namespace fmt\n\n// Class for managing conversions to/from IPv4 addresses.\nclass IPv4Address {\npublic:\n  using Octets = std::array<uint8_t, 4>;\n\n  // Constructs an all-zero (i.e. \"0.0.0.0\") IPv4 address.\n  constexpr IPv4Address() {}\n  constexpr explicit IPv4Address(const Octets &octets) : octets_(octets) {}\n\n  // Parses the IPv4 address from the buffer. Assumes network byte order.\n  static IPv4Address from(uint32_t buffer);\n\n  static IPv4Address from(struct in_addr const &address) { return IPv4Address::from(address.s_addr); }\n\n  // Parses the null-terminated string representation of an IPv4 address.\n  //\n  // Returns the `IPv4Address` representation if the string represents a valid IPv4 address,\n  // or an error code otherwise.\n  //\n  // Example:\n  //\n  //  auto ip_string = \"127.0.0.1\";\n  //  auto ip = IPv4Address::parse(ip_string);\n  //\n  //  if (!ip) {\n  //    LOG::error(\"failed to parse ip address with error code {}\", ip.error());\n  //  } else {\n  //    assert(raw_ip->is_localhost());\n  //  }\n  static Expected<IPv4Address, std::error_code> parse(char const *ip_string);\n\n  // Returns the 32-bit integer representation of this address, in network\n  // byte order.\n  uint32_t as_int() const;\n\n  // A human-readable string representation.\n  std::string str() const;\n\n  // True if this address represents localhost.\n  bool is_localhost() const;\n\n  // tells whether all bytes in this address are 0\n  bool is_zero() const;\n\n  // Converts this to an IPv6 address representation.\n  IPv6Address to_ipv6() const;\n\n  /**\n   * Localhost addresses are represented by 127.0.0.0/8.\n   *\n   * The `least_significant_octet` parameter can be used to customize the resulting address to\n   * something other than the default 127.0.0.1.\n   */\n  static constexpr IPv4Address localhost(uint8_t least_significant_octet = 1)\n  {\n    return IPv4Address({127, 0, 0, least_significant_octet});\n  }\n\n  short_string<4> bytes_view() const { return {reinterpret_cast<char const *>(octets_.data()), 4}; }\n\n  bool operator==(const IPv4Address &rhs) const;\n\n  template <typename H> friend H AbslHashValue(H hash_state, const IPv4Address &addr);\n\nprivate:\n  Octets octets_ = {};\n};\n\ntemplate <typename H> H AbslHashValue(H hash_state, const IPv4Address &addr)\n{\n  return H::combine(std::move(hash_state), addr.octets_);\n}\n\nstd::ostream &operator<<(std::ostream &os, const IPv4Address &ipv4);\n\nnamespace fmt {\ntemplate <> struct formatter<IPv4Address> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n\n  template <typename FormatContext> auto format(const IPv4Address &address, FormatContext &ctx) const\n  {\n    return format_to(ctx.out(), \"{}\", address.str());\n  }\n};\n} // namespace fmt\n"
  },
  {
    "path": "util/ip_address_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/ip_address.h\"\n\n#include <absl/strings/str_format.h>\n#include <array>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <iostream>\n\nnamespace server {\nnamespace {\n\nusing ::testing::ContainerEq;\nusing ::testing::Property;\n\nTEST(IPv6AddressTest, WriteToInt64Buffer)\n{\n  IPv6Address address = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  std::array<uint64_t, 2> expected = {htobe64(0x0011223344556677ull), htobe64(0x8899aabbccddeeffull)};\n  if (htole16(0xff00) != 0xff00) {\n    std::swap(expected[0], expected[1]);\n  }\n\n  std::array<uint64_t, 2> actual;\n  address.write_to(&actual);\n  EXPECT_THAT(actual, ContainerEq(expected));\n}\n\nTEST(IPv6AddressTest, WriteToInt32Buffer)\n{\n  IPv6Address address = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  std::array<uint32_t, 4> expected = {htonl(0x00112233ul), htonl(0x44556677ul), htonl(0x8899aabbul), htonl(0xccddeefful)};\n  std::array<uint32_t, 4> actual;\n  address.write_to(&actual);\n  EXPECT_THAT(actual, ContainerEq(expected));\n}\n\nTEST(IPv6AddressTest, WriteToByteBuffer)\n{\n  IPv6Address address = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  std::array<uint8_t, 16> expected = {\n      0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};\n  std::array<uint8_t, 16> actual;\n  address.write_to(actual.data());\n  EXPECT_THAT(actual, ContainerEq(expected));\n}\n\nTEST(IPv6AddressTest, FromInt64Buffer)\n{\n  std::array<uint64_t, 2> buffer = {htobe64(0x0011223344556677ull), htobe64(0x8899aabbccddeeffull)};\n  if (htole16(0xff00) != 0xff00) {\n    std::swap(buffer[0], buffer[1]);\n  }\n\n  IPv6Address actual = IPv6Address::from(buffer);\n  IPv6Address expected = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(IPv6AddressTest, FromInt32Buffer)\n{\n  std::array<uint32_t, 4> buffer = {htonl(0x00112233ul), htonl(0x44556677ul), htonl(0x8899aabbul), htonl(0xccddeefful)};\n  IPv6Address actual = IPv6Address::from(buffer);\n  IPv6Address expected = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(IPv6AddressTest, FromByteBuffer)\n{\n  std::array<uint8_t, 16> buffer = {\n      0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};\n  IPv6Address actual = IPv6Address::from(buffer.data());\n  IPv6Address expected = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(IPv6AddressTest, FromByteBuffer_ByteAlignment)\n{\n  struct {\n    uint64_t a = 0;\n    uint8_t b = 0xff;\n    uint8_t buffer[16] = {\n        0x00,\n        0x11,\n        0x22,\n        0x33,\n        0x44,\n        0x55,\n        0x66,\n        0x77,\n        0x88,\n        0x99,\n        0xaa,\n        0xbb,\n        0xcc,\n        0xdd,\n        0xee,\n        0xff,\n    };\n  } test_struct;\n\n  IPv6Address actual = IPv6Address::from(test_struct.buffer);\n  IPv6Address expected = IPv6Address::from_host_hextets({0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff});\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(IPv6AddressTest, AsInt)\n{\n  {\n    u8 bytes[] = {\n        0x00,\n        0x11,\n        0x22,\n        0x33,\n        0x44,\n        0x55,\n        0x66,\n        0x77,\n        0x88,\n        0x99,\n        0xaa,\n        0xbb,\n        0xcc,\n        0xdd,\n        0xee,\n        0xff,\n    };\n    auto const address = IPv6Address::from(bytes);\n    EXPECT_EQ((std::uint64_t)(address.as_int() >> 64), 0x0011223344556677ull);\n    EXPECT_EQ((std::uint64_t)(address.as_int() & UINT64_MAX), 0x8899aabbccddeeffull);\n  }\n\n  {\n    u8 bytes[] = {0xf3, 0xe4, 0xd5, 0xc6, 0xb7, 0xa8, 0x99, 0x8a, 0x7b, 0x6c, 0x5d, 0x4e, 0x3f, 0x20, 0x11, 0x02};\n    auto const address = IPv6Address::from(bytes);\n    EXPECT_EQ((std::uint64_t)(address.as_int() >> 64), 0xf3e4d5c6b7a8998aull);\n    EXPECT_EQ((std::uint64_t)(address.as_int() & UINT64_MAX), 0x7b6c5d4e3f201102ull);\n  }\n}\n\nTEST(IPv6AddressTest, Str)\n{\n  EXPECT_EQ(IPv6Address().str(), \"::\");\n  EXPECT_EQ(IPv6Address::localhost().str(), \"::1\");\n  EXPECT_EQ(IPv6Address::from_host_hextets({0xdead, 0, 0, 0, 0, 0, 0, 0xbeef}).str(), \"dead::beef\");\n}\n\nTEST(IPv6AddressTest, bytes_view)\n{\n  EXPECT_TRUE(as_short_string(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1) == IPv6Address::localhost().bytes_view());\n\n  EXPECT_TRUE(\n      as_short_string(0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbe, 0xef) ==\n      IPv6Address::from_host_hextets({0xdead, 0, 0, 0, 0, 0, 0, 0xbeef}).bytes_view());\n}\n\nTEST(IPv6AddressTest, IsLocalhost)\n{\n  EXPECT_FALSE(IPv6Address().is_localhost());\n  EXPECT_TRUE(IPv6Address::localhost().is_localhost());\n  EXPECT_FALSE(IPv6Address::from_host_hextets({0xdead, 0, 0, 0, 0, 0, 0, 0xbeef}).is_localhost());\n}\n\nTEST(IPv6AddressTest, IsIpv4)\n{\n  EXPECT_FALSE(IPv6Address().is_ipv4());\n  EXPECT_FALSE(IPv6Address::localhost().is_ipv4());\n  EXPECT_FALSE(IPv6Address::from_host_hextets({0xdead, 0, 0, 0, 0, 0, 0, 0xbeef}).is_ipv4());\n  EXPECT_TRUE(IPv6Address::from_host_hextets({0, 0, 0, 0, 0, 0xffff, 0xdead, 0xbeef}).is_ipv4());\n}\n\nTEST(IPv6AddressTest, ToIpv4)\n{\n  auto expect_addr = IPv6Address::parse(\"::ffff:127.0.0.1\");\n  EXPECT_TRUE(expect_addr.has_value());\n  auto addr = expect_addr.value();\n  EXPECT_TRUE(addr.is_ipv4());\n  EXPECT_TRUE(addr.to_ipv4().has_value());\n  EXPECT_TRUE(addr.to_ipv4()->is_localhost());\n}\n\nTEST(IPv6AddressTest, localhost)\n{\n  auto const localhost = IPv6Address::localhost();\n  EXPECT_TRUE(localhost.is_localhost());\n}\n\nTEST(IPv6AddressTest, parse_localhost)\n{\n  auto const result = IPv6Address::parse(\"localhost\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv6AddressTest, parse___1)\n{\n  auto const result = IPv6Address::parse(\"::1\");\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(IPv6Address::localhost(), ip);\n\n  EXPECT_TRUE(ip.is_localhost());\n}\n\nTEST(IPv6AddressTest, parse___)\n{\n  auto const result = IPv6Address::parse(\"::\");\n  auto const expected = IPv6Address::from({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv6AddressTest, parse_2607_f8b0_4000_811__2004)\n{\n  auto const result = IPv6Address::parse(\"2607:f8b0:4000:811::2004\");\n  auto const expected = IPv6Address::from({0x26, 0x07, 0xf8, 0xb0, 0x40, 0, 0x8, 0x11, 0, 0, 0, 0, 0, 0, 0x20, 0x04});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv6AddressTest, parse_127_0_0_1)\n{\n  auto const result = IPv6Address::parse(\"127.0.0.1\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv6AddressTest, parse_192_168_0_1)\n{\n  auto const result = IPv6Address::parse(\"192.168.0.1\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv6AddressTest, parse_123_456_789_0)\n{\n  auto const result = IPv6Address::parse(\"123.456.789.0\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv6AddressTest, parse_hello_world)\n{\n  auto const result = IPv6Address::parse(\"hello, world!\");\n  EXPECT_FALSE(result);\n}\n\n// ipv4\n\nTEST(IPv4AddressTest, FromInt)\n{\n  IPv4Address actual = IPv4Address::from(0x33221100);\n  IPv4Address expected({0x00, 0x11, 0x22, 0x33});\n  EXPECT_EQ(actual, expected);\n}\n\nTEST(IPv4AddressTest, AsInt)\n{\n  IPv4Address address({0x00, 0x11, 0x22, 0x33});\n  EXPECT_EQ(address.as_int(), 0x33221100U) << absl::StrFormat(\"actual = %s (0x%08x)\", address.str(), address.as_int());\n}\n\nTEST(IPv4AddressTest, Str)\n{\n  EXPECT_EQ(IPv4Address().str(), \"0.0.0.0\");\n  EXPECT_EQ(IPv4Address({50, 100, 150, 200}).str(), \"50.100.150.200\");\n}\n\nTEST(IPv4AddressTest, bytes_view)\n{\n  EXPECT_TRUE(as_short_string(127, 0, 0, 1) == IPv4Address::localhost().bytes_view());\n\n  EXPECT_TRUE(as_short_string(0xde, 0xad, 0xbe, 0xef) == IPv4Address({0xde, 0xad, 0xbe, 0xef}).bytes_view());\n}\n\nTEST(IPv4AddressTest, IsLocalhost)\n{\n  EXPECT_FALSE(IPv4Address().is_localhost());\n  EXPECT_FALSE(IPv4Address({128, 0, 0, 1}).is_localhost());\n  EXPECT_TRUE(IPv4Address({127, 0, 0, 1}).is_localhost());\n}\n\nTEST(IPv4AddressTest, ToIpv6)\n{\n  IPv6Address actual = IPv4Address({1, 2, 3, 4}).to_ipv6();\n  IPv6Address expected = IPv6Address::from_host_hextets({0, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304});\n  EXPECT_EQ(expected, actual);\n  EXPECT_TRUE(actual.is_ipv4());\n}\n\nTEST(IPv4AddressTest, localhost)\n{\n  auto const localhost = IPv4Address::localhost();\n  EXPECT_TRUE(localhost.is_localhost());\n  EXPECT_EQ(IPv4Address({127, 0, 0, 1}), localhost);\n}\n\nTEST(IPv4AddressTest, localhost_0)\n{\n  auto const localhost = IPv4Address::localhost(0);\n  EXPECT_TRUE(localhost.is_localhost());\n  EXPECT_EQ(IPv4Address({127, 0, 0, 0}), localhost);\n}\n\nTEST(IPv4AddressTest, localhost_10)\n{\n  auto const localhost = IPv4Address::localhost(10);\n  EXPECT_TRUE(localhost.is_localhost());\n  EXPECT_EQ(IPv4Address({127, 0, 0, 10}), localhost);\n}\n\nTEST(IPv4AddressTest, localhost_255)\n{\n  auto const localhost = IPv4Address::localhost(255);\n  EXPECT_TRUE(localhost.is_localhost());\n  EXPECT_EQ(IPv4Address({127, 0, 0, 255}), localhost);\n}\n\nTEST(IPv4AddressTest, localhost_to_ipv6)\n{\n  auto const ipv4_localhost = IPv4Address::localhost();\n  auto const ipv6_localhost = ipv4_localhost.to_ipv6();\n  EXPECT_FALSE(ipv6_localhost.is_ipv4());\n  EXPECT_TRUE(ipv6_localhost.is_localhost());\n}\n\nTEST(IPv4AddressTest, parse_localhost)\n{\n  auto const result = IPv4Address::parse(\"localhost\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv4AddressTest, parse_127_0_0_1)\n{\n  auto const result = IPv4Address::parse(\"127.0.0.1\");\n  auto const expected = IPv4Address({127, 0, 0, 1});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n\n  EXPECT_TRUE(ip.is_localhost());\n  EXPECT_EQ(IPv4Address::localhost(), ip);\n}\n\nTEST(IPv4AddressTest, parse_127_0_0_0)\n{\n  auto const result = IPv4Address::parse(\"127.0.0.0\");\n  auto const expected = IPv4Address({127, 0, 0, 0});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n\n  EXPECT_TRUE(ip.is_localhost());\n}\n\nTEST(IPv4AddressTest, parse_127_0_0_255)\n{\n  auto const result = IPv4Address::parse(\"127.0.0.255\");\n  auto const expected = IPv4Address({127, 0, 0, 255});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n\n  EXPECT_TRUE(ip.is_localhost());\n}\n\nTEST(IPv4AddressTest, parse_192_168_0_1)\n{\n  auto const result = IPv4Address::parse(\"192.168.0.1\");\n  auto const expected = IPv4Address({192, 168, 0, 1});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv4AddressTest, parse_10_0_0_1)\n{\n  auto const result = IPv4Address::parse(\"10.0.0.1\");\n  auto const expected = IPv4Address({10, 0, 0, 1});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv4AddressTest, parse_1_2_3_4)\n{\n  auto const result = IPv4Address::parse(\"1.2.3.4\");\n  auto const expected = IPv4Address({1, 2, 3, 4});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv4AddressTest, parse_255_255_255_255)\n{\n  auto const result = IPv4Address::parse(\"255.255.255.255\");\n  auto const expected = IPv4Address({255, 255, 255, 255});\n  ASSERT_TRUE(result);\n\n  auto const ip = result.value();\n  EXPECT_EQ(expected, ip);\n}\n\nTEST(IPv4AddressTest, parse_123_456_789_0)\n{\n  auto const result = IPv4Address::parse(\"123.456.789.0\");\n  EXPECT_FALSE(result);\n}\n\nTEST(IPv4AddressTest, parse_hello_world)\n{\n  auto const result = IPv4Address::parse(\"hello, world!\");\n  EXPECT_FALSE(result);\n}\n\n} // namespace\n} // namespace server\n"
  },
  {
    "path": "util/iterable_bitmap.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <platform/generic.h>\n\ntemplate <std::size_t SIZE> class IterableBitmap {\npublic:\n  typedef std::size_t size_type;\n  static constexpr size_type size = SIZE;\n  /* at l0 we have one word for every 2^6 bits */\n  static constexpr size_type l0 = ((size + 63) / 64);\n  /* at l1 we have one word for every 2^12 bits */\n  static constexpr size_type l1 = ((l0 == 1) ? 0 : ((l0 + 63) / 64));\n  /* at l2 we have one word for every 2^18 bits */\n  static constexpr size_type l2 = ((l1 == 1) ? 0 : ((l1 + 63) / 64));\n  /* at l3 we have one word for every 2^24 bits */\n  static constexpr size_type l3 = ((l2 == 1) ? 0 : ((l2 + 63) / 64));\n  /* at l4 we have one word for every 2^30 bits */\n  static constexpr size_type l4 = ((l3 == 1) ? 0 : ((l3 + 63) / 64));\n  /* number of levels */\n  static constexpr size_type levels = ((l4 > 0) ? 5 : ((l3 > 0) ? 4 : ((l2 > 0) ? 3 : ((l1 > 0) ? 2 : 1))));\n\n  IterableBitmap() : mask0{}, mask1{}, mask2{}, mask3{}, mask4{}\n  {\n    static_assert(size > 0, \"IterableBitmap must have > 0 elements\");\n    static_assert(l4 <= 1, \"IterableBitmap supports <= (1<<30) elements\");\n  }\n\n  class iterator {\n  public:\n    size_type operator*() const { return i[0]; }\n\n    /* we only support != where rhs is end() */\n    bool operator!=(const iterator &rhs) const { return mask[levels - 1] != 0; }\n\n    iterator &operator++()\n    {\n      mask[0] &= mask[0] - 1;\n      if (mask[0] == 0) {\n        if (levels == 1)\n          return *this;\n        mask[1] &= mask[1] - 1;\n        if (mask[1] == 0) {\n          if (levels == 2)\n            return *this;\n          mask[2] &= mask[2] - 1;\n          if (mask[2] == 0) {\n            if (levels == 3)\n              return *this;\n\n            mask[3] &= mask[3] - 1;\n            if (mask[3] == 0) {\n              if (levels == 4)\n                return *this;\n\n              mask[4] &= mask[4] - 1;\n              if (mask[4] == 0)\n                /* levels must be == 5 */\n                return *this;\n\n              i[4] = __builtin_ctzll(mask[4]);\n              mask[3] = bitmap->mask3[i[4]];\n              i[4] <<= 6;\n            }\n            i[3] = __builtin_ctzll(mask[3]) + ((l4 > 0) ? i[4] : 0);\n            mask[2] = bitmap->mask2[i[3]];\n            i[3] <<= 6;\n          }\n          i[2] = __builtin_ctzll(mask[2]) + ((l3 > 0) ? i[3] : 0);\n          mask[1] = bitmap->mask1[i[2]];\n          i[2] <<= 6;\n        }\n        i[1] = __builtin_ctzll(mask[1]) + ((l2 > 0) ? i[2] : 0);\n        mask[0] = bitmap->mask0[i[1]];\n        i[1] <<= 6;\n      }\n      i[0] = __builtin_ctzll(mask[0]) + ((l1 > 0) ? i[1] : 0);\n      return *this;\n    }\n\n  private:\n    friend class IterableBitmap;\n    const IterableBitmap *bitmap;\n    std::array<size_type, levels> i;\n    std::array<u64, levels> mask;\n  };\n\n  /**\n   * Marks the given bit.\n   * @assumes index < SIZE.\n   */\n  void set(size_type index)\n  {\n    __set_bit64(index, mask0.data());\n    if (l1 > 0)\n      __set_bit64(index >> 6, mask1.data());\n    if (l2 > 0)\n      __set_bit64(index >> 12, mask2.data());\n    if (l3 > 0)\n      __set_bit64(index >> 18, mask3.data());\n    if (l4 > 0)\n      __set_bit64(index >> 24, mask4.data());\n  }\n\n  /**\n   * unmarks the given bit.\n   * @assumes index < SIZE.\n   */\n  void clear(size_type index)\n  {\n    __clear_bit64(index, mask0.data());\n    if ((l1 > 0) && (mask0[index >> 6] == 0))\n      __clear_bit64(index >> 6, mask1.data());\n    if ((l2 > 0) && (mask1[index >> 12] == 0))\n      __clear_bit64(index >> 12, mask2.data());\n    if ((l3 > 0) && (mask2[index >> 18] == 0))\n      __clear_bit64(index >> 18, mask3.data());\n    if ((l4 > 0) && (mask3[index >> 24] == 0))\n      __clear_bit64(index >> 24, mask4.data());\n  }\n\n  int get(size_type index) const { return test_bit(index, mask0.data()); }\n\n  iterator begin() const\n  {\n    iterator res;\n    /* first, get the root */\n    u64 root;\n    if (l4 > 0) {\n      root = mask4[0];\n    } else if (l3 > 0) {\n      root = mask3[0];\n    } else if (l2 > 0) {\n      root = mask2[0];\n    } else if (l1 > 0) {\n      root = mask1[0];\n    } else /* l0 > 0 */ {\n      root = mask0[0];\n    }\n    res.mask[levels - 1] = root;\n\n    /* empty set? */\n    if (root == 0)\n      return res;\n\n    res.bitmap = this;\n\n    /* now resolve the masks and indices on the path to the leaf */\n    if (l4 > 0) {\n      res.i[4] = __builtin_ctzll(res.mask[4]);\n      res.mask[3] = mask3[res.i[4]];\n      res.i[4] <<= 6;\n    }\n    if (l3 > 0) {\n      res.i[3] = __builtin_ctzll(res.mask[3]) + ((l4 > 0) ? res.i[4] : 0);\n      res.mask[2] = mask2[res.i[3]];\n      res.i[3] <<= 6;\n    }\n    if (l2 > 0) {\n      res.i[2] = __builtin_ctzll(res.mask[2]) + ((l3 > 0) ? res.i[3] : 0);\n      res.mask[1] = mask1[res.i[2]];\n      res.i[2] <<= 6;\n    }\n    if (l1 > 0) {\n      res.i[1] = __builtin_ctzll(res.mask[1]) + ((l2 > 0) ? res.i[2] : 0);\n      ;\n      res.mask[0] = mask0[res.i[1]];\n      res.i[1] <<= 6;\n    }\n    res.i[0] = __builtin_ctzll(res.mask[0]) + ((l1 > 0) ? res.i[1] : 0);\n    return res;\n  }\n\n  iterator end() const { return iterator(); }\n\nprivate:\n  friend class iterator;\n  std::array<u64, l0> mask0;\n  std::array<u64, l1> mask1;\n  std::array<u64, l2> mask2;\n  std::array<u64, l3> mask3;\n  std::array<u64, l4> mask4;\n};\n"
  },
  {
    "path": "util/jitter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <chrono>\n#include <type_traits>\n\n/**\n * Computes a random jitter amount in the range [`jitter_lower_bound`, `jitter_upper_bound`].\n *\n * `{Lower|Upper}Bound` must be an instantiation of class template `std::chrono::duration`.\n *\n * This is a lossless function so the return type has the finer grain between `LowerBound`\n * and `UpperBound`.\n */\ntemplate <typename LowerBound, typename UpperBound>\nstd::common_type_t<LowerBound, UpperBound> compute_jitter(LowerBound jitter_lower_bound, UpperBound jitter_upper_bound);\n\n/**\n * Adjusts the given time point with a random jitter amount in the range\n * [`jitter_lower_bound`, `jitter_upper_bound`].\n *\n * `{Lower|Upper}Bound` must be an instantiation of class template `std::chrono::duration`.\n *\n * `timepoint` must be either of type `std::chrono::duration` or `std::chrono::timepoint`.\n *\n * This is a lossless function so the return type has the finer grain between\n * `timepoint`, `LowerBound` * and `UpperBound`.\n *\n * Example:\n *\n *  timer.defer(add_jitter(timeout, -5s, 5s), [] {\n *    // task code goes here\n *  });\n */\ntemplate <typename TimePointRep, typename TimePointPeriod, typename LowerBound, typename UpperBound>\nstd::common_type_t<std::chrono::duration<TimePointRep, TimePointPeriod>, LowerBound, UpperBound> add_jitter(\n    std::chrono::duration<TimePointRep, TimePointPeriod> timepoint,\n    LowerBound jitter_lower_bound,\n    UpperBound jitter_upper_bound);\n\ntemplate <typename Clock, typename TimePointDuration, typename LowerBound, typename UpperBound>\nstd::chrono::time_point<Clock, std::common_type_t<TimePointDuration, LowerBound, UpperBound>> add_jitter(\n    std::chrono::time_point<Clock, TimePointDuration> timepoint, LowerBound jitter_lower_bound, UpperBound jitter_upper_bound);\n\n#include <util/jitter.inl>\n"
  },
  {
    "path": "util/jitter.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/random.h>\n\ntemplate <typename LowerBound, typename UpperBound>\nstd::common_type_t<LowerBound, UpperBound> compute_jitter(LowerBound jitter_lower_bound, UpperBound jitter_upper_bound)\n{\n  using finer_grain_duration = std::common_type_t<LowerBound, UpperBound>;\n  return finer_grain_duration{RNG<std::make_unsigned_t<typename finer_grain_duration::rep>>::next(\n      jitter_lower_bound.count(), jitter_upper_bound.count())};\n}\n\ntemplate <typename TimePointRep, typename TimePointPeriod, typename LowerBound, typename UpperBound>\nstd::common_type_t<std::chrono::duration<TimePointRep, TimePointPeriod>, LowerBound, UpperBound> add_jitter(\n    std::chrono::duration<TimePointRep, TimePointPeriod> timepoint,\n    LowerBound jitter_lower_bound,\n    UpperBound jitter_upper_bound)\n{\n  return timepoint + compute_jitter(jitter_lower_bound, jitter_upper_bound);\n}\n\ntemplate <typename Clock, typename TimePointDuration, typename LowerBound, typename UpperBound>\nstd::chrono::time_point<Clock, std::common_type_t<TimePointDuration, LowerBound, UpperBound>> add_jitter(\n    std::chrono::time_point<Clock, TimePointDuration> timepoint, LowerBound jitter_lower_bound, UpperBound jitter_upper_bound)\n{\n  auto duration = add_jitter(timepoint.time_since_epoch(), jitter_lower_bound, jitter_upper_bound);\n  return std::chrono::time_point<Clock, decltype(duration)>{duration};\n}\n"
  },
  {
    "path": "util/jitter_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/jitter.h>\n\n#include <platform/types.h>\n\nconstexpr std::size_t TIGHT_LOOP_ITERATIONS = 1'000'000;\n\nTEST(jitter, compute_jitter_same_type)\n{\n  constexpr std::chrono::milliseconds lower_bound = -10s;\n  constexpr std::chrono::milliseconds upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = compute_jitter(lower_bound, upper_bound);\n    EXPECT_GE(result, lower_bound);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n\nTEST(jitter, compute_jitter_different_types)\n{\n  constexpr std::chrono::milliseconds lower_bound = -10s;\n  constexpr std::chrono::seconds upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = compute_jitter(lower_bound, upper_bound);\n    EXPECT_GE(result, lower_bound);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_duration_same_type)\n{\n  std::chrono::milliseconds const timepoint = 100s;\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::milliseconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_duration_same_type_jitter)\n{\n  auto const timepoint = 100s;\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::milliseconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_duration_different_types)\n{\n  auto const timepoint = 100s;\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::seconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_timepoint_same_type)\n{\n  auto const timepoint = std::chrono::system_clock::now();\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::milliseconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_timepoint_same_type_jitter)\n{\n  auto const timepoint = std::chrono::system_clock::now();\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::milliseconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n\nTEST(jitter, add_jitter_timepoint_different_types)\n{\n  auto const timepoint = std::chrono::system_clock::now();\n  std::chrono::milliseconds const lower_bound = -10s;\n  std::chrono::seconds const upper_bound = +10s;\n\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = add_jitter(timepoint, lower_bound, upper_bound);\n    EXPECT_GE(result, timepoint + lower_bound);\n    EXPECT_LE(result, timepoint + upper_bound);\n  }\n}\n"
  },
  {
    "path": "util/json.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <nlohmann/json.hpp>\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include <cstdlib>\n\n#include <optional>\n\nnamespace nlohmann {\n    template <typename T>\n    struct adl_serializer<std::optional<T>> {\n        // Convert from JSON to optional\n        static std::optional<T> from_json(const json& j) {\n            if (j.is_null()) {\n                return std::nullopt;\n            }\n            return j.get<T>();\n        }\n\n        // Convert from optional to JSON\n        static void to_json(json& j, const std::optional<T>& opt) {\n            if (opt) {\n                j = *opt;\n            } else {\n                j = nullptr;\n            }\n        }\n    };\n\n    // Custom parsing function for optional strings\n    template <typename T = json>\n    T parse_optional(const std::optional<std::string>& opt) {\n        if (opt && !opt->empty()) {\n            return T::parse(*opt);\n        }\n        return T();\n    }\n}\n\n// Add custom input adapter for optional types\nnamespace nlohmann::detail {\n    template <typename T>\n    auto input_adapter(std::optional<T>& opt) {\n        if (opt && !opt->empty()) {\n            return input_adapter(*opt);\n        }\n        // Return an empty string input adapter if optional is empty\n        return input_adapter(std::string{});\n    }\n\n    // Input adapter for const optional references\n    template <typename T>\n    auto input_adapter(const std::optional<T>& opt) {\n        if (opt && !opt->empty()) {\n            return input_adapter(*opt);\n        }\n        // Return an empty string input adapter if optional is empty\n        return input_adapter(std::string{});\n    }\n}\n\ninline nlohmann::json const *follow_path(nlohmann::json const &object)\n{\n  return &object;\n}\n\n// nullptr if not an object or not found\ntemplate <typename... Path, typename Key>\nnlohmann::json const *follow_path(nlohmann::json const &object, Key &&key, Path &&... path)\n{\n  if (!object.is_object()) {\n    return nullptr;\n  }\n\n  if (auto i = object.find(key); i != object.end()) {\n    return follow_path(*i, std::forward<Path>(path)...);\n  }\n\n  return nullptr;\n}\n\n// nullptr if not found\ntemplate <typename... Path> std::string const *try_get_string(nlohmann::json const &object, Path &&... path)\n{\n  auto value = follow_path(object, std::forward<Path>(path)...);\n\n  if (!value) {\n    return nullptr;\n  }\n\n  return value->template get_ptr<std::string const *>();\n}\n\n// empty if not found\ntemplate <typename... Path> std::string_view get_string_view(nlohmann::json const &object, Path &&... path)\n{\n  if (auto const value = try_get_string(object, std::forward<Path>(path)...)) {\n    return *value;\n  } else {\n    return {};\n  }\n}\n\n// empty if not found\ntemplate <typename... Path> std::string_view get_zstring_view(nlohmann::json const &object, Path &&... path)\n{\n  if (auto const value = try_get_string(object, std::forward<Path>(path)...)) {\n    return {value->c_str(), value->size()};\n  } else {\n    return {};\n  }\n}\n\n// std::nullopt if not found\ntemplate <typename T = std::int64_t, typename... Path>\nstd::optional<T> try_get_int(nlohmann::json const &object, Path &&... path)\n{\n  static_assert(std::is_integral_v<T>);\n\n  auto value = follow_path(object, std::forward<Path>(path)...);\n\n  if (!value || !value->is_number_integer()) {\n    return std::nullopt;\n  }\n\n  return static_cast<T>(value->template get<std::int64_t>());\n}\n\n// converts string value to integer\n// std::nullopt if not found or if value doesn't represent an integer\ntemplate <typename... Path> std::optional<std::int64_t> try_get_as_int(nlohmann::json const &object, Path &&... path)\n{\n  auto const value = try_get_string(object, std::forward<Path>(path)...);\n\n  if (!value) {\n    return std::nullopt;\n  }\n\n  auto const begin = value->c_str();\n  char *end = nullptr;\n  auto const result = std::strtoll(begin, &end, 10);\n  if (!end || begin == end) {\n    return std::nullopt;\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "util/json_converter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/buffer.h>\n#include <util/expected.h>\n#include <util/file_ops.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n#include <util/meta.h>\n#include <util/raw_json.h>\n\n#include <iomanip>\n#include <iostream>\n#include <string_view>\n#include <system_error>\n\nnamespace json_converter {\n\ntemplate <typename App> class WireToJsonConverter {\n  template <typename Message> static void convert_message(void *ctx, std::uint64_t timestamp, char *data)\n  {\n    auto context = reinterpret_cast<WireToJsonConverter *>(ctx);\n\n    if (context->first_) {\n      context->first_ = false;\n    } else {\n      context->out_ << ',';\n    }\n\n    context->out_ << \"{\\\"name\\\":\\\"\" << Message::name << \"\\\"\"\n                  << \",\\\"rpc_id\\\":\" << Message::rpc_id << \",\\\"timestamp\\\":\" << timestamp;\n\n    auto const message = reinterpret_cast<typename Message::parsed_message const *>(data);\n\n    if constexpr (Message::has_reference) {\n      print_json_value<typename Message::reference::type>(context->out_ << \",\\\"ref\\\":\", Message::reference::get(message));\n    }\n\n    context->out_ << \",\\\"data\\\":{\";\n\n    meta::foreach<typename Message::fields>([&](auto tag) {\n      using field = decltype(meta::tag_type(tag));\n\n      if constexpr (field::index) {\n        context->out_ << ',';\n      }\n      context->out_ << '\"' << field::name << \"\\\":\";\n\n      print_json_value<typename field::type>(context->out_, field::get(message));\n    });\n    context->out_ << \"}}\";\n  }\n\npublic:\n  template <typename... Args>\n  WireToJsonConverter(std::ostream &out, Args &&...args)\n      : out_(out), transform_builder_(std::forward<Args>(args)...), protocol_(transform_builder_)\n  {\n    meta::foreach<typename App::messages>([this](auto tag) {\n      using message = decltype(meta::tag_type(tag));\n      protocol_.add_handler(message::rpc_id, this, &convert_message<message>);\n    });\n\n    protocol_.insert_no_auth_identity_transforms();\n    protocol_.insert_need_auth_identity_transforms();\n  }\n\n  Expected<std::size_t, std::error_code> process(char const *data, std::size_t size)\n  {\n    auto const result = protocol_.handle_multiple(data, size).result;\n    if (result < 0) {\n      return {unexpected, -result, std::generic_category()};\n    }\n    return static_cast<std::size_t>(result);\n  }\n\nprivate:\n  std::ostream &out_;\n  bool first_ = true;\n  typename App::transform_builder transform_builder_;\n  typename App::protocol protocol_;\n};\n\n/**\n * Converts all wire messages from `in` for the render application metadata\n * `App` to JSON format and prints output to `out`.\n *\n * Example:\n *\n *  auto in = FileDescriptor::std_in();\n *  llvm::LLVMContext llvm;\n *  json_converter::print_from_wire_format<ebpf_net::flowtune_metadata>(in, std::cout, llvm);\n */\ntemplate <typename App, std::size_t BufferSize = 4096, typename... Args>\nint print_from_wire_format(FileDescriptor &in, std::ostream &out, Args &&...args)\n{\n  json_converter::WireToJsonConverter<App> converter(out, std::forward<Args>(args)...);\n  buffer::Buffer<BufferSize> buffer;\n\n  out << '[';\n  for (bool eof = false; !eof;) {\n    auto writable = buffer.compact().writable();\n\n    if (auto const read = in.read_all(writable.data(), writable.size()).try_raise().value()) {\n      buffer.commit(read);\n    } else {\n      eof = true;\n    }\n\n    auto const available = buffer.available();\n    if (auto const handled = converter.process(available.data(), available.size()); !handled) {\n      if (handled.error().value() == EAGAIN) {\n        if (buffer.consumed().empty()) {\n          LOG::error(\"buffer too small with {} bytes\", buffer.size());\n          return ENOBUFS;\n        }\n\n        continue;\n      }\n      LOG::error(\"error while handling message: {}\", handled.error());\n      return handled.error().value();\n    } else if (*handled > 0) {\n      buffer.consume(*handled);\n    }\n  }\n\n  out << ']';\n  out.flush();\n  return 0;\n}\n\n} // namespace json_converter\n"
  },
  {
    "path": "util/json_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/json.h>\n\n#include <string>\n#include <vector>\n\n#include <cstdint>\n\nusing nlohmann::json;\n\n// some of these tests are meant as a quick reference on how to use the JSON API\n\nTEST(json, example_type)\n{\n  EXPECT_EQ(json::value_t::null, json::parse(R\"json(null)json\").type());\n  EXPECT_EQ(json::value_t::boolean, json::parse(R\"json(true)json\").type());\n  EXPECT_EQ(json::value_t::string, json::parse(R\"json(\"foobar\")json\").type());\n  EXPECT_EQ(json::value_t::number_integer, json::parse(R\"json(-10)json\").type());\n  EXPECT_EQ(json::value_t::number_unsigned, json::parse(R\"json(10)json\").type());\n  EXPECT_EQ(json::value_t::number_float, json::parse(R\"json(5.6)json\").type());\n  EXPECT_EQ(json::value_t::object, json::parse(R\"json({})json\").type());\n  EXPECT_EQ(json::value_t::array, json::parse(R\"json([])json\").type());\n}\n\nTEST(json, example_dictionary)\n{\n  std::vector<std::pair<std::string, std::int64_t>> const expected{{\"a\", 10}, {\"b\", 20}, {\"c\", 30}, {\"d\", 40}, {\"e\", 50}};\n\n  auto const data = json::parse(R\"json(\n    {\n      \"a\": 10,\n      \"b\": 20,\n      \"c\": 30,\n      \"d\": 40,\n      \"e\": 50\n    }\n  )json\");\n\n  EXPECT_EQ(json::value_t::object, data.type());\n  EXPECT_EQ(expected.size(), data.size());\n\n  { // iterate on values\n    auto i = expected.begin();\n    for (auto const &value : data) {\n      ASSERT_NE(i, expected.end());\n\n      EXPECT_EQ(json::value_t::number_unsigned, value.type());\n      EXPECT_EQ(i->second, value);\n\n      ++i;\n    }\n    EXPECT_EQ(i, expected.end());\n  }\n\n  { // iterate on key/value pairs\n    auto i = expected.begin();\n    for (auto const &item : data.items()) {\n      ASSERT_NE(i, expected.end());\n\n      EXPECT_EQ(i->first, item.key());\n\n      auto const &value = item.value();\n      EXPECT_EQ(json::value_t::number_unsigned, value.type());\n      EXPECT_EQ(i->second, value);\n\n      ++i;\n    }\n    EXPECT_EQ(i, expected.end());\n  }\n}\n"
  },
  {
    "path": "util/k8s_metadata.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/k8s_metadata.h>\n\n#include <util/json.h>\n\nnamespace {\nstatic std::string const CONTAINER_PORT_NAME = \"name\";\nstatic std::string const CONTAINER_PORT_NUMBER = \"containerPort\";\nstatic std::string const CONTAINER_PORT_PROTOCOL = \"protocol\";\n} // namespace\n\nK8sMetadata::K8sMetadata(nlohmann::json const &labels)\n    : container_name_(get_string_view(labels, CONTAINER_NAME)),\n      pod_name_(get_string_view(labels, POD_NAME)),\n      pod_ns_(get_string_view(labels, POD_NS)),\n      pod_uid_(get_string_view(labels, POD_UID)),\n      sandbox_uid_(get_string_view(labels, SANDBOX_ID))\n{\n  if (auto const ports_string = try_get_string(labels, CONTAINER_PORTS)) {\n    auto const ports = nlohmann::json::parse(*ports_string);\n    if (ports.type() == nlohmann::json::value_t::array) {\n      for (auto const &port : ports) {\n        if (port.type() != nlohmann::json::value_t::object) {\n          continue;\n        }\n\n        auto const name = try_get_string(port, CONTAINER_PORT_NAME);\n        auto const number = try_get_int(port, CONTAINER_PORT_NUMBER);\n        auto const protocol = try_get_string(port, CONTAINER_PORT_PROTOCOL);\n\n        if (name && number && protocol) {\n          auto const port_number = static_cast<std::uint16_t>(*number);\n          ports_[port_number] = PortInfo{\n              .port = port_number,\n              .protocol = try_enum_from_string(*protocol, PortProtocol::unknown),\n              .name = *name,\n          };\n        }\n      }\n    }\n  }\n}\n\nK8sMetadata::operator bool() const\n{\n  return !container_name_.empty() || !pod_name_.empty() || !pod_ns_.empty() || !pod_uid_.empty() || !sandbox_uid_.empty();\n}\n"
  },
  {
    "path": "util/k8s_metadata.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <common/port_protocol.h>\n\n#include <nlohmann/json.hpp>\n\n#include <map>\n#include <optional>\n#include <string>\n#include <string_view>\n\n#include <cstdint>\n\n/**\n * Reads metadata posted by Kubernetes to the Docker engine.\n */\nclass K8sMetadata {\npublic:\n  static constexpr std::string_view CONTAINER_PORTS = \"annotation.io.kubernetes.container.ports\";\n  static constexpr std::string_view CONTAINER_NAME = \"io.kubernetes.container.name\";\n  static constexpr std::string_view POD_NAME = \"io.kubernetes.pod.name\";\n  static constexpr std::string_view POD_NS = \"io.kubernetes.pod.namespace\";\n  static constexpr std::string_view POD_UID = \"io.kubernetes.pod.uid\";\n  static constexpr std::string_view SANDBOX_ID = \"io.kubernetes.sandbox.id\";\n\n  static constexpr std::string_view POD_CONTAINER_NAME_VALUE = \"POD\";\n\n  struct PortInfo {\n    std::uint16_t port;\n    PortProtocol protocol;\n    std::string name;\n  };\n\n  // `labels` is the `.Config.Labels` section from the equivalent of `docker inspect`\n  explicit K8sMetadata(nlohmann::json const &labels);\n\n  std::string_view container_name() const { return container_name_; }\n  std::string_view pod_name() const { return pod_name_; }\n  std::string_view pod_ns() const { return pod_ns_; }\n  std::string_view pod_uid() const { return pod_uid_; }\n  std::string_view sandbox_uid() const { return sandbox_uid_; }\n  std::map<std::uint16_t, PortInfo> ports() const { return ports_; }\n\n  explicit operator bool() const;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, K8sMetadata const &what);\n\nprivate:\n  std::string container_name_;\n  std::string pod_name_;\n  std::string pod_ns_;\n  std::string pod_uid_;\n  std::string sandbox_uid_;\n  std::map<std::uint16_t, PortInfo> ports_;\n};\n\n#include <util/k8s_metadata.inl>\n"
  },
  {
    "path": "util/k8s_metadata.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\ntemplate <typename Out> Out &&operator<<(Out &&out, K8sMetadata const &what)\n{\n  out << \"\\\"container_name\\\":\\\"\" << what.container_name_ << \"\\\",\\\"pod_name\\\":\\\"\" << what.pod_name_ << \"\\\",\\\"pod_ns\\\":\\\"\"\n      << what.pod_ns_ << \"\\\",\\\"pod_uid\\\":\\\"\" << what.pod_uid_ << \"\\\",\\\"sandbox_uid\\\":\\\"\" << what.sandbox_uid_\n      << \"\\\",\\\"ports\\\":[\";\n  bool first = true;\n  for (auto const &port : what.ports_) {\n    if (first) {\n      first = false;\n    } else {\n      out << ',';\n    }\n    out << '\"' << port.second.port << \"\\\":{\\\"name\\\":\\\"\" << port.second.name << \"\\\",\\\"protocol\\\":\\\"\" << port.second.protocol\n        << \"\\\"}\";\n  }\n  out << ']';\n\n  return std::forward<Out>(out);\n}\n"
  },
  {
    "path": "util/lazy_array.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/iterable_bitmap.h>\n\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n\n// Simple array-like container in which elements are constructed only when\n// they are first accessed.\n//\ntemplate <typename T, std::size_t SIZE, class Allocator = std::allocator<T>> class LazyArray {\npublic:\n  static_assert(std::is_default_constructible<T>::value);\n\n  using value_type = T;\n  using size_type = std::size_t;\n\n  static constexpr size_type size = SIZE;\n\n  LazyArray(LazyArray const &) = delete;\n  LazyArray(LazyArray &&) = delete;\n  LazyArray &operator=(LazyArray const &) = delete;\n  LazyArray &operator=(LazyArray &&) = delete;\n\nprivate:\n  /* use allocator for T directly; allocate returns uninitialized storage */\n  using allocator_traits = std::allocator_traits<Allocator>;\n\npublic:\n  LazyArray() { array_ = allocator_traits::allocate(allocator_, size); }\n\n  ~LazyArray()\n  {\n    for (auto i : constructed_) {\n      allocator_traits::destroy(allocator_, &array_[i]);\n    }\n\n    allocator_traits::deallocate(allocator_, array_, size);\n  }\n\n  value_type &operator[](size_type i)\n  {\n    if (!constructed_.get(i)) {\n      allocator_traits::construct(allocator_, &array_[i]);\n      constructed_.set(i);\n    }\n\n    return array_[i];\n  }\n\n  value_type const &operator[](size_type i) const\n  {\n    if (!constructed_.get(i)) {\n      allocator_traits::construct(allocator_, &array_[i]);\n      constructed_.set(i);\n    }\n\n    return array_[i];\n  }\n\nprivate:\n  using allocator_type = typename allocator_traits::allocator_type;\n  using bitmap_type = IterableBitmap<SIZE>;\n\n  allocator_type allocator_;\n\n  mutable bitmap_type constructed_;\n\n  value_type *array_;\n};\n"
  },
  {
    "path": "util/log.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/types.h>\n#include <util/environment_variables.h>\n#include <util/log.h>\n\n// clang-format off\n// The order of following includes is important for the spdlog library.\n// Disable clang-format so that it won't reorder them.\n#include <spdlog/spdlog.h>\n#include <spdlog/async.h>\n#include <spdlog/sinks/null_sink.h>\n#include <spdlog/sinks/rotating_file_sink.h>\n#include <spdlog/sinks/stdout_color_sinks.h>\n#include <spdlog/sinks/stdout_sinks.h>\n// clang-format on\n#include <chrono>\n#include <memory>\n#include <cstdlib>\n\nnamespace {\nconstexpr unsigned int MAX_FILE_SIZE = 1024 * 1024;\nconstexpr unsigned int MAX_NUM_FILES = 5;\nconstexpr auto FLUSH_INTERVAL = 5s;\n\nconstexpr std::string_view LOG_FILE_VAR = \"EBPF_NET_LOG_FILE_PATH\";\nconstexpr std::string_view LOG_FILE_DEFAULT = \"/var/log/ebpf_net.log\";\n} // namespace\n\n// TODO: move the rate limit mechanism into a spdlog sink (inside its lock),\n//       just before the line is written out. This way, there is no need\n//       to use atomic operation.\nstd::atomic<int64_t> LOG::rate_limit_budget(0);\nbool LOG::rate_limit_enabled = false;\n\nvoid LOG::init(bool console, std::string const *filename)\n{\n  std::vector<spdlog::sink_ptr> sinks;\n\n  if (filename) {\n    sinks.emplace_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(*filename, MAX_FILE_SIZE, MAX_NUM_FILES));\n  }\n\n  if (console) {\n    if (auto const fd = fileno(stdout); fd != -1 && isatty(fd)) {\n      // colored output if stdout is a tty\n      sinks.emplace_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());\n    } else {\n      sinks.emplace_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());\n    }\n  }\n\n  if (sinks.empty()) {\n    sinks.emplace_back(std::make_shared<spdlog::sinks::null_sink_mt>());\n  }\n\n  // thread pool values borrowed from spdlog's defaults\n  spdlog::init_thread_pool(8192, 1);\n  auto logger = std::make_shared<spdlog::async_logger>(\n      \"main_logger\", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);\n\n  spdlog::set_default_logger(std::move(logger));\n\n  spdlog::set_pattern(\"%Y-%m-%d %T.%f%z %^%l%$ [p:%P t:%t] %v\");\n\n  spdlog::flush_on(spdlog::level::err);\n\n  spdlog::flush_every(FLUSH_INTERVAL);\n\n#ifndef NDEBUG\n  // Spdlog doesn't throw exceptions while logging.  See https://spdlog.docsforge.com/v1.x/error-handling.\n  // In debug builds, use a custom error handler to catch incorrectly formatted (typically due to argument mismatches) logs.\n  spdlog::set_error_handler([](const std::string &msg) { throw std::runtime_error(\"spdlog error: \" + msg); });\n#endif\n}\n\nstd::string_view LOG::log_file_path()\n{\n  return try_get_env_var(LOG_FILE_VAR.data(), LOG_FILE_DEFAULT);\n}\n\nvoid LOG::enable_rate_limit(int64_t initial_budget)\n{\n  assert(initial_budget > 0);\n\n  LOG::rate_limit_budget = initial_budget;\n  LOG::rate_limit_enabled = true;\n}\n\nbool LOG::rate_limited()\n{\n  if (!LOG::rate_limit_enabled) {\n    return false;\n  }\n\n  if (LOG::rate_limit_budget.fetch_sub(1) <= 0) {\n    return true;\n  }\n\n  return false;\n}\n\nvoid LOG::refill_rate_limit_budget(int64_t amount)\n{\n  if (!LOG::rate_limit_enabled) {\n    return;\n  }\n\n  assert(amount > 0);\n\n  // Try a few times to update the budget atomically\n  for (int i = 0; i < 2; i++) {\n    int64_t current = LOG::rate_limit_budget.load();\n    if (LOG::rate_limit_budget.compare_exchange_strong(current, std::max(current, amount))) {\n      return;\n    }\n  }\n\n  // Gives up, just update it to |amount|.\n  // We might leak |amount| messages budget.\n  LOG::rate_limit_budget.store(amount);\n}\n"
  },
  {
    "path": "util/log.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <spdlog/cfg/env.h>\n#include <spdlog/fmt/fmt.h>\n#include <spdlog/fmt/ostr.h>\n#include <spdlog/spdlog.h>\n\n#include <atomic>\n#include <config.h>\n#include <string>\n#include <string_view>\n\n#include <common/client_type.h>\n#include <util/debug.h>\n#include <util/log_modifiers.h>\n#include <util/log_whitelist.h>\n#include <util/fmt_extensions.h>\n\nclass LOG {\npublic:\n  /**\n   * Initialize loggers.\n   *\n   * If `console` is true, intializes console log sink.\n   *\n   * If `filename` is not null, initializes file log sink.\n   */\n  static void init(bool console, std::string const *filename = nullptr);\n\n  /**\n   * Resolve log file path from environment variable.\n   */\n  static std::string_view log_file_path();\n\n  // Enables global rate limit\n  // It is expected that this function is called once during global\n  // initialization (e.g., in main()).\n  //\n  // |initial_budget|: number of messages allowed initiallly\n  static void enable_rate_limit(int64_t initial_budget);\n\n  // Bumps up the max number of messages allowed by |amount|.\n  static void refill_rate_limit_budget(int64_t amount);\n\n  template <typename Format, typename... Args> static void inline trace(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::trace(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n\n  }\n\n  /**\n   * Calls trace() if the given filter is in the log whitelist defined by\n   * `Whitelist`. See `util/log_whitelist.h` for details.\n   *\n   * Example:\n   *\n   * LOG::trace_in(client_type_, \"my log string\");\n   */\n  template <typename Whitelist, typename Format, typename... Args, typename = std::enable_if_t<std::is_enum_v<Whitelist>>>\n  static void inline trace_in(Whitelist filter, Format &&format, Args &&... args)\n  {\n    if (!is_log_whitelisted(filter)) {\n      return;\n    }\n    trace(std::forward<Format>(format), std::forward<Args>(args)...);\n  }\n\n  /**\n   * Calls trace() if all given filters are in the log whitelist defined by\n   * `Whitelist`. See `util/log_whitelist.h` for details.\n   *\n   * Example:\n   *\n   * LOG::trace_in(client_type_, NodeResolutionType::AWS, \"my log string\");\n   */\n  template <typename Format, typename... Whitelist, typename... Args>\n  static void inline trace_in(std::tuple<Whitelist...> const &filter, Format &&format, Args &&... args)\n  {\n    if (!is_log_whitelisted(filter)) {\n      return;\n    }\n    trace(std::forward<Format>(format), std::forward<Args>(args)...);\n  }\n\n  template <typename Format, typename... Args> static void inline debug(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::debug(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n  }\n\n  /**\n   * Calls debug() if the given filter is in the log whitelist defined by\n   * `Whitelist`. See `util/log_whitelist.h` for details.\n   *\n   * Example:\n   *\n   * LOG::debug_in(client_type_, \"my log string\");\n   *\n   * LOG::debug_in(client_type_, NodeResolutionType::AWS, \"my log string\");\n   */\n  template <typename Whitelist, typename Format, typename... Args, typename = std::enable_if_t<std::is_enum_v<Whitelist>>>\n  static void inline debug_in(Whitelist filter, Format &&format, Args &&... args)\n  {\n    if (!is_log_whitelisted(filter)) {\n      return;\n    }\n    debug(std::forward<Format>(format), std::forward<Args>(args)...);\n  }\n\n  /**\n   * Calls debug() if all given filters are in the log whitelist defined by\n   * `Whitelist`. See `util/log_whitelist.h` for details.\n   *\n   * Example:\n   *\n   * LOG::debug_in(client_type_, NodeResolutionType::AWS, \"my log string\");\n   */\n  template <typename Format, typename... Whitelist, typename... Args>\n  static void inline debug_in(std::tuple<Whitelist...> const &filter, Format &&format, Args &&... args)\n  {\n    if (!is_log_whitelisted(filter)) {\n      return;\n    }\n    debug(std::forward<Format>(format), std::forward<Args>(args)...);\n  }\n\n  template <typename Format, typename... Args> static void inline info(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::info(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n  }\n\n  template <typename Format, typename... Args> static void inline warn(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::warn(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n  }\n\n  template <typename Format, typename... Args> static void inline error(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::error(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n  }\n\n  template <typename Format, typename... Args> static void inline critical(Format &&format, Args &&... args)\n  {\n    logger::check_logging_overhead<Args &&...>();\n    if (rate_limited()) {\n      return;\n    }\n    spdlog::critical(fmt::runtime(std::forward<Format>(format)), std::forward<Args>(args)...);\n  }\n\nprivate:\n  // Returns true if rate limit is hit, and we should not log for now.\n  static bool rate_limited();\n\n  static bool rate_limit_enabled;\n  static std::atomic<int64_t> rate_limit_budget;\n};\n"
  },
  {
    "path": "util/log_formatters.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <chrono>\n#include <spdlog/fmt/fmt.h>\n#include <exception>\n#include <system_error>\n#include <variant>\n\n#include <cstring>\n\n/////////////////\n// error codes //\n/////////////////\n\nnamespace std {\n\ntemplate <typename Out> Out &operator<<(Out &&out, std::errc error_code)\n{\n  out << std::strerror(static_cast<int>(error_code));\n  return out;\n}\n\ntemplate <typename Out> Out &operator<<(Out &&out, std::error_code error)\n{\n  out << '[' << error.value() << \": \" << error.message() << ']';\n  return out;\n}\n\n} // namespace std\n\nnamespace fmt {\n\ntemplate <> struct formatter<std::error_code> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n  template <typename FormatContext> auto format(std::error_code const &error, FormatContext &ctx) const\n  {\n    return fmt::format_to(ctx.out(), \"[{}: {}]\", error.value(), error.message());\n  }\n};\n\ntemplate <> struct formatter<std::errc> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n  template <typename FormatContext> auto format(std::errc error, FormatContext &ctx) const\n  {\n    return fmt::format_to(ctx.out(), \"[{}: {}]\", static_cast<int>(error), std::strerror(static_cast<int>(error)));\n  }\n};\n\n} // namespace fmt\n\n////////////////\n// exceptions //\n////////////////\n\nnamespace std {\n\ntemplate <typename Out> Out &operator<<(Out &&out, std::exception const &e)\n{\n  out << e.what();\n  return out;\n}\n\n} // namespace std\n\n////////////\n// chrono //\n////////////\n\nnamespace std {\n\ntemplate <typename Out> Out &operator<<(Out &&out, std::chrono::hours value)\n{\n  out << value.count() << \"h\";\n  return out;\n}\ntemplate <typename Out> Out &operator<<(Out &&out, std::chrono::minutes value)\n{\n  out << value.count() << \"min\";\n  return out;\n}\n\ntemplate <typename Out> Out &operator<<(Out &&out, std::chrono::seconds value)\n{\n  out << value.count() << \"s\";\n  return out;\n}\n\n\n} // namespace std\n\n////////////////\n// containers //\n////////////////\n\nnamespace std {\n\ntemplate <std::size_t Offset = 0, typename Out, typename T, typename... Args>\nOut &operator<<(Out &&out, std::variant<T, Args...> const &value)\n{\n  if constexpr (Offset + 4 <= (1 + sizeof...(Args))) {\n    switch (value.index()) {\n    case Offset + 0: {\n      out << std::get<Offset + 0>(value);\n      break;\n    }\n    case Offset + 1: {\n      out << std::get<Offset + 1>(value);\n      break;\n    }\n    case Offset + 2: {\n      out << std::get<Offset + 2>(value);\n      break;\n    }\n    case Offset + 3: {\n      out << std::get<Offset + 3>(value);\n      break;\n    }\n    default: {\n      operator<<<Offset + 4>(out, value);\n      break;\n    }\n    }\n  } else if constexpr (Offset + 2 <= (1 + sizeof...(Args))) {\n    switch (value.index()) {\n    case Offset + 0: {\n      out << std::get<Offset + 0>(value);\n      break;\n    }\n    case Offset + 1: {\n      out << std::get<Offset + 1>(value);\n      break;\n    }\n    default: {\n      operator<<<Offset + 2>(out, value);\n      break;\n    }\n    }\n  } else if constexpr (Offset + 1 <= (1 + sizeof...(Args))) {\n    if (value.index() == Offset + 0) {\n      out << std::get<Offset + 0>(value);\n    }\n  }\n  return out;\n}\n\n} // namespace std\n"
  },
  {
    "path": "util/log_modifiers.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <optional>\n#include <sstream>\n\n#include <cassert>\n\nnamespace logger {\n\ntemplate <typename... Args>\nconstexpr inline bool is_logging_overhead =\n    ((std::is_same_v<std::string, std::decay_t<Args>> && std::is_rvalue_reference_v<Args>) || ... || false);\n\ntemplate <typename... Args> static constexpr inline void check_logging_overhead()\n{\n  static_assert(\n      !is_logging_overhead<Args...>,\n      \"\\n\"\n      \"  ====================\\n\"\n      \"  = LOGGING OVERHEAD =\\n\"\n      \"  ===================================================================================\\n\"\n      \"  Detected: temporary dynamic allocation of string for the sole purpose of logging.\\n\"\n      \"\\n\"\n      \"  Use `std::string_view` instead or see `util/log_modifiers.h` for low cost alternatives.\\n\"\n      \"\\n\"\n      \"  For protobuf messages, avoid `DebugString()`. Instead, include `util/protobuf_log.h`\\n\"\n      \"  and pass a message reference directly to the logger.\\n\"\n      \"  ===================================================================================\\n\");\n}\n\n} // namespace logger\n\n////////////\n// waived //\n////////////\n\nnamespace logger::impl {\n\ntemplate <typename T> struct waived_t {\n  constexpr explicit waived_t(T const &what) : what_(what) {}\n\n  // make sure this is not used as a non-temporary\n  waived_t(waived_t const &) = delete;\n  waived_t(waived_t &&) = delete;\n  waived_t &operator=(waived_t const &) = delete;\n  waived_t &operator=(waived_t &&) = delete;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, waived_t const &what)\n  {\n    out << what.what_;\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  T const &what_;\n};\n\n// Specialization for std::optional to handle logging within waived_t\ntemplate <typename Out, typename T>\nOut&& operator<<(Out&& out, const std::optional<T>& opt) {\n    if (opt) {\n        out << *opt;\n    } else {\n        out << \"(empty)\";\n    }\n    return std::forward<Out>(out);\n}\n\n} // namespace logger::impl\n\n/**\n * This is an unfortunate but necessary compatibility escape hatch for broken libraries\n * over which we have no control of, or special cases where temporaries are legitimate.\n *\n * Example:\n *\n *  some_class instance;\n *\n *  LOG::info(\"value: {}\", log_waive(instance.getter()));\n *\n * Example context:\n *\n *  struct some_class {\n *    //...\n *\n *    std::string getter() { return value_; }\n *\n *  private:\n *    std::string value_;\n *  };\n */\ntemplate <typename T> constexpr auto log_waive(T &&what)\n{\n  // waive the logging overhead check\n  return logger::impl::waived_t<T>(std::forward<T>(what));\n}\n\n////////////\n// either //\n////////////\n\nnamespace logger::impl {\n\ntemplate <typename WhenTrue, typename WhenFalse> struct either_t {\n  constexpr explicit either_t(bool condition, WhenTrue const &when_true, WhenFalse const &when_false)\n      : condition_(condition), when_true_(when_true), when_false_(when_false)\n  {}\n\n  // make sure this is not used as a non-temporary\n  either_t(either_t const &) = delete;\n  either_t(either_t &&) = delete;\n  either_t &operator=(either_t const &) = delete;\n  either_t &operator=(either_t &&) = delete;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, either_t const &what)\n  {\n    if (what.condition_) {\n      out << what.when_true_;\n    } else {\n      out << what.when_false_;\n    }\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  bool const condition_;\n  WhenTrue const &when_true_;\n  WhenFalse const &when_false_;\n};\n\n} // namespace logger::impl\n\n/**\n * A ternary conditional replacement for logging statements.\n *\n * Motivation:\n * -----------\n *\n * It's not uncommon for logs to have statements like this:\n *\n *  LOG::info(\"name: {}\", condition ? get_name() : \"<unknown>\")\n *\n * where `get_name` is a run-of-the-mill getter that returns a string reference.\n *\n * This is expected to be a very cheap call, but there's a hidden cost:\n * The ternary operator performs type promotion and comes up with a common type\n * between both `when_true` and `when_false` operands.\n *\n * In this particular example, the common type between `std::string const &` and\n * `char const []` is `std::string const &`. This means that the string literal\n * will, on every log call, be converted to a `std::string` temporary, with dynamic\n * allocation and all, using the cast constructor of `std::string`. Being a temporary,\n * such object will be destroyed and its dynamically allocated memory freed once the\n * expression is fully evaluated.\n *\n * There are not only costs for allocating, deallocating and copying what otherwise\n * should be a compile-time available string, but also (implementation defined)\n * contention on the allocator.\n *\n * With `log_either`, no promotion or allocation is performed and operands are\n * heterogeneously handled by the logger according to their respective types.\n *\n * Example:\n *\n *  // either logs the result of `get_name()` when `condition` is `true`\n *  // or `<unknown>` otherwise\n *  LOG::info(\"name: {}\", log_either(condition, get_name(), \"<unknown>\"))\n *\n *  // either logs the value pointed to by `ptr` or the string `<null>`\n *  // note that LOG_LAZY prevents the pointer from being dereferenced when it is null\n *  LOG::info(\"value: {}\", log_either(ptr, LOG_LAZY(*ptr), \"<null>\"));\n */\ntemplate <typename WhenTrue, typename WhenFalse> auto log_either(bool condition, WhenTrue &&when_true, WhenFalse &&when_false)\n{\n  logger::check_logging_overhead<WhenTrue &&, WhenFalse &&>();\n  return logger::impl::either_t<WhenTrue, WhenFalse>(\n      condition, std::forward<WhenTrue>(when_true), std::forward<WhenFalse>(when_false));\n}\n\n////////////////\n// surrounded //\n////////////////\n\nnamespace logger::impl {\n\ntemplate <typename T, char Open, char Close = Open> struct surrounded_t {\n  constexpr explicit surrounded_t(T const &what) : what_(what) {}\n\n  // make sure this is not used as a non-temporary\n  surrounded_t(surrounded_t const &) = delete;\n  surrounded_t(surrounded_t &&) = delete;\n  surrounded_t &operator=(surrounded_t const &) = delete;\n  surrounded_t &operator=(surrounded_t &&) = delete;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, surrounded_t const &what)\n  {\n    out << Open << what.what_ << Close;\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  T const &what_;\n};\n\n} // namespace logger::impl\n\n/**\n * A logging helper that surrounds values with an open/close character.\n *\n * Example:\n *\n *  // either logs the string `name: \"NAME\"` or `name: <unknown>`\n *  LOG::info(\"name: {}\", log_either(condition, log_quoted(get_name()), \"<unknown>\"));\n *\n *  // logs the string `name: 'NAME'`\n *  LOG::info(\"name: {}\", log_single_quoted(get_name()));\n *\n *  // logs the string `name: *NAME*`\n *  LOG::info(\"name: {}\", log_surround<'*'>(get_name()));\n *\n *  // logs the string `name: <NAME>`\n *  LOG::info(\"name: {}\", log_surround<'<', '>'>(get_name()));\n *\n * Motivation:\n *\n * It helps avoid two problems:\n * - create temporaries when formatting data\n * - (non-goal) cluttering log formats with minor style information\n *\n * The most common scenario where this problem manifests itself is this:\n *\n *  LOG::info(\"name: {}\", condition ? \"'\" + get_name() + \"'\" : \"<unknown>\");\n *\n * where `get_name` is a run-of-the-mill getter that returns a string reference.\n *\n * Two different problems manifest here. One of them is solved by `log_either`.\n * The remaining one is the unnecessary allocation of a temporary string to hold\n * the concatenation resulting from surrounding the name with quotes.\n *\n * `log_surround` solves this problem without any temporaries being allocated by\n * surrounding the value within given `Open` and `Close` characters.\n *\n * There are also convenience alternatives for common formatting:\n * - log_quoted: encloses the value between double quotes;\n * - log_single_quoted: encloses the value between double quotes;\n * - log_parens: encloses the value between parenthesis;\n * - log_backet: encloses the value between square brackets;\n * - log_brace: encloses the value between curly braces.\n */\ntemplate <char Open, char Close = Open, typename T> constexpr auto log_surround(T &&what)\n{\n  logger::check_logging_overhead<T &&>();\n  return logger::impl::surrounded_t<T, Open, Close>(std::forward<T>(what));\n}\n\ntemplate <typename T> constexpr auto log_quoted(T &&what)\n{\n  return log_surround<'\"'>(std::forward<T>(what));\n}\n\ntemplate <typename T> constexpr auto log_single_quoted(T &&what)\n{\n  return log_surround<'\\''>(std::forward<T>(what));\n}\n\ntemplate <typename T> constexpr auto log_parens(T &&what)\n{\n  return log_surround<'(', ')'>(std::forward<T>(what));\n}\n\ntemplate <typename T> constexpr auto log_bracket(T &&what)\n{\n  return log_surround<'[', ']'>(std::forward<T>(what));\n}\n\ntemplate <typename T> constexpr auto log_brace(T &&what)\n{\n  return log_surround<'{', '}'>(std::forward<T>(what));\n}\n\n/////////////\n// kv_pair //\n/////////////\n\nnamespace logger::impl {\n\ntemplate <typename Key, typename Value, char Separator, char Open, char Close = Open> struct kv_pair_t {\n  static constexpr std::string_view invalid_key_characters = \"\\\"='<>{}[]()?/$#@!\";\n  using key_t = std::decay_t<Key>;\n\n  constexpr explicit kv_pair_t(Key const &key, Value const &value) : key_(key), value_(value)\n  {\n    if constexpr (std::is_same_v<key_t, std::string> || std::is_same_v<key_t, std::string_view>) {\n      assert(key_.find_first_of(invalid_key_characters) == key_t::npos);\n    } else if constexpr (std::is_same_v<key_t, char const *> || std::is_same_v<key_t, char *>) {\n      std::string_view const key_view(key_);\n      assert(key_view.find_first_of(invalid_key_characters) == std::string_view::npos);\n    }\n  }\n\n  // make sure this is not used as a non-temporary\n  kv_pair_t(kv_pair_t const &) = delete;\n  kv_pair_t(kv_pair_t &&) = delete;\n  kv_pair_t &operator=(kv_pair_t const &) = delete;\n  kv_pair_t &operator=(kv_pair_t &&) = delete;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, kv_pair_t const &what)\n  {\n    out << what.key_ << Separator << Open << what.value_ << Close;\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  Key const &key_;\n  Value const &value_;\n};\n\n} // namespace logger::impl\n\n/**\n * An output stream helper for quoted key/value pairs.\n *\n * Example:\n *\n *  // logs the string `pair: key=\"value\"\n *  LOG::info(\"pair: {}\", log_kv_pair(get_key(), get_value()));\n *\n *  // logs the string `pair: KEY/<VALUE>`\n *  LOG::info(\"pair: {}\", log_kv_pair<'/', '<', '>'>(get_key(), get_value()));\n *\n * Motivation:\n *\n * It helps avoid these problems:\n * - create temporaries when formatting data\n * - leverages compile-time type system checks to find human errors in formatting\n * - (non-goal) cluttering log formats with minor style information\n *\n * The most common scenario where this problem manifests itself is this:\n *\n *  ss << \"metric {key=\\\"\" << key << \"\\\"} \" << value << ' ' << timestamp << '\\n';\n *\n * All the quoting and string formatting becomes pretty cryptic and hard to check\n * with the human eye. Using the helpers there's no formatting necessary.\n */\ntemplate <char Separator = '=', char Open = '\"', char Close = Open, typename Key, typename Value>\nconstexpr auto log_kv_pair(Key &&key, Value &&value)\n{\n  logger::check_logging_overhead<Key &&, Value &&>();\n  return logger::impl::kv_pair_t<Key, Value, Separator, Open, Close>(std::forward<Key>(key), std::forward<Value>(value));\n}\n\n//////////////\n// callable //\n//////////////\n\nnamespace logger::impl {\n\ntemplate <typename Fn> struct callable_t {\n  template <typename UFn> constexpr explicit callable_t(UFn &&fn) : fn_(fn) {}\n\n  // make sure this is not used as a non-temporary\n  callable_t(callable_t const &) = delete;\n  callable_t(callable_t &&) = delete;\n  callable_t &operator=(callable_t const &) = delete;\n  callable_t &operator=(callable_t &&) = delete;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, callable_t const &what)\n  {\n    out << what.fn_();\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  Fn const &fn_;\n};\n\n} // namespace logger::impl\n\n/**\n * A logging helper to allow logging the result of a callable object.\n *\n * The callable is lazily called at the time of logging, and might\n * not be called at all if used in conjunction with conditionals like\n * log_either.\n *\n * Example:\n *\n *  // logs the result of calling the given lambda\n *  LOG::info(\"value: {}\", log_call([] { return std::pow(2, 8); }));\n */\ntemplate <typename Fn> constexpr auto log_call(Fn &&callable)\n{\n  return logger::impl::callable_t<Fn>(callable);\n}\n\n/**\n * A logging helper to allow lazy evaluation of an expression.\n *\n * Example:\n *\n *  // either logs the value pointed to by `ptr` or the string `<null>`\n *  LOG::info(\"value: {}\", log_either(ptr, LOG_LAZY(*ptr), \"<null>\"));\n */\n#define LOG_LAZY(...)                                                                                                          \\\n  ::log_call(                                                                                                                  \\\n      [&]() -> ::std::conditional_t<                                                                                           \\\n                ::std::is_reference_v<decltype(__VA_ARGS__)>,                                                                  \\\n                decltype(__VA_ARGS__) const &,                                                                                 \\\n                decltype(__VA_ARGS__)> { return __VA_ARGS__; })\n\n// Global operator for std::optional to handle ADL-based lookup\nnamespace std {\ntemplate <typename Out, typename T>\nOut&& operator<<(Out&& out, const std::optional<T>& opt) {\n    if (opt) {\n        out << *opt;\n    } else {\n        out << \"(empty)\";\n    }\n    return std::forward<Out>(out);\n}\n}\n\n// fmt v10+ ADL customization points for logging wrappers\n// These provide formatting when util/fmt_extensions.h isn't included.\n// When it is included, its formatter specializations take precedence.\nnamespace logger::impl {\n\ntemplate <typename T>\ninline std::string format_as(waived_t<T> const &w)\n{\n  std::ostringstream os;\n  os << w;\n  return os.str();\n}\n\ntemplate <typename WhenTrue, typename WhenFalse>\ninline std::string format_as(either_t<WhenTrue, WhenFalse> const &v)\n{\n  std::ostringstream os;\n  os << v;\n  return os.str();\n}\n\ntemplate <typename T, char Open, char Close>\ninline std::string format_as(surrounded_t<T, Open, Close> const &v)\n{\n  std::ostringstream os;\n  os << v;\n  return os.str();\n}\n\ntemplate <typename Key, typename Value, char Separator, char Open, char Close>\ninline std::string format_as(kv_pair_t<Key, Value, Separator, Open, Close> const &v)\n{\n  std::ostringstream os;\n  os << v;\n  return os.str();\n}\n\ntemplate <typename Fn>\ninline std::string format_as(callable_t<Fn> const &v)\n{\n  std::ostringstream os;\n  os << v;\n  return os.str();\n}\n\n} // namespace logger::impl\n"
  },
  {
    "path": "util/log_modifiers_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/log_modifiers.h>\n\n#include <gtest/gtest.h>\n#include <spdlog/fmt/fmt.h>\n#include <spdlog/fmt/ostr.h>\n\n#include <string>\n#include <string_view>\n#include <utility>\n#include <vector>\n\nnamespace {\n\nTEST(log_modifiers, logging_overhead)\n{\n  EXPECT_FALSE(logger::is_logging_overhead<bool &>);\n  EXPECT_FALSE(logger::is_logging_overhead<bool &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<bool const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<bool const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<char &>);\n  EXPECT_FALSE(logger::is_logging_overhead<char &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<char const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<char const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<char const *&>);\n  EXPECT_FALSE(logger::is_logging_overhead<char const *&&>);\n  EXPECT_FALSE(logger::is_logging_overhead<char const *const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<char const *const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<int &>);\n  EXPECT_FALSE(logger::is_logging_overhead<int &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<int const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<int const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<long &>);\n  EXPECT_FALSE(logger::is_logging_overhead<long &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<long const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<long const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<float &>);\n  EXPECT_FALSE(logger::is_logging_overhead<float &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<float const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<float const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<double &>);\n  EXPECT_FALSE(logger::is_logging_overhead<double &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<double const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<double const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<std::string &>);\n  EXPECT_TRUE(logger::is_logging_overhead<std::string &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::string const &>);\n  EXPECT_TRUE(logger::is_logging_overhead<std::string const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<std::string_view &>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::string_view &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::string_view const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::string_view const &&>);\n\n  EXPECT_FALSE(logger::is_logging_overhead<std::vector<int> &>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::vector<int> &&>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::vector<int> const &>);\n  EXPECT_FALSE(logger::is_logging_overhead<std::vector<int> const &&>);\n}\n\n#define LOG_STR(...) fmt::format(\"{}\", __VA_ARGS__)\n\nTEST(log_modifiers, log_waive)\n{\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(10)));\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(std::string(\"10\"))));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(cref)));\n\n  EXPECT_EQ(\"10\", LOG_STR(log_waive(std::move(s))));\n\n  EXPECT_EQ(\"10\", LOG_STR(log_waive([] { return std::string(\"10\"); }())));\n}\n\nTEST(log_modifiers, log_either)\n{\n  EXPECT_EQ(\"111\", LOG_STR(log_either(true, 111, 222)));\n  EXPECT_EQ(\"222\", LOG_STR(log_either(false, 111, 222)));\n\n  EXPECT_EQ(\"TTT\", LOG_STR(log_either(true, \"TTT\", \"FFF\")));\n  EXPECT_EQ(\"FFF\", LOG_STR(log_either(false, \"TTT\", \"FFF\")));\n\n  EXPECT_EQ(\"111\", LOG_STR(log_either(true, 111, \"FFF\")));\n  EXPECT_EQ(\"222\", LOG_STR(log_either(false, \"TTT\", 222)));\n}\n\nTEST(log_modifiers, log_surround)\n{\n  EXPECT_EQ(\"<10>\", LOG_STR(log_surround<'<', '>'>(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"<10>\", LOG_STR(log_surround<'<', '>'>(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"<10>\", LOG_STR(log_surround<'<', '>'>(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"<10>\", LOG_STR(log_surround<'<', '>'>(cref)));\n}\n\nTEST(log_modifiers, log_quoted)\n{\n  EXPECT_EQ(\"\\\"10\\\"\", LOG_STR(log_quoted(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"\\\"10\\\"\", LOG_STR(log_quoted(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"\\\"10\\\"\", LOG_STR(log_quoted(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"\\\"10\\\"\", LOG_STR(log_quoted(cref)));\n}\n\nTEST(log_modifiers, log_single_quoted)\n{\n  EXPECT_EQ(\"'10'\", LOG_STR(log_single_quoted(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"'10'\", LOG_STR(log_single_quoted(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"'10'\", LOG_STR(log_single_quoted(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"'10'\", LOG_STR(log_single_quoted(cref)));\n}\n\nTEST(log_modifiers, log_parens)\n{\n  EXPECT_EQ(\"(10)\", LOG_STR(log_parens(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"(10)\", LOG_STR(log_parens(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"(10)\", LOG_STR(log_parens(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"(10)\", LOG_STR(log_parens(cref)));\n}\n\nTEST(log_modifiers, log_bracket)\n{\n  EXPECT_EQ(\"[10]\", LOG_STR(log_bracket(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"[10]\", LOG_STR(log_bracket(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"[10]\", LOG_STR(log_bracket(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"[10]\", LOG_STR(log_bracket(cref)));\n}\n\nTEST(log_modifiers, log_brace)\n{\n  EXPECT_EQ(\"{10}\", LOG_STR(log_brace(10)));\n\n  std::string s(\"10\");\n  EXPECT_EQ(\"{10}\", LOG_STR(log_brace(s)));\n\n  std::string &ref = s;\n  EXPECT_EQ(\"{10}\", LOG_STR(log_brace(ref)));\n\n  std::string const &cref = s;\n  EXPECT_EQ(\"{10}\", LOG_STR(log_brace(cref)));\n}\n\nTEST(log_modifiers, log_kv_pair)\n{\n  EXPECT_EQ(R\"(key=\"value\")\", LOG_STR(log_kv_pair(\"key\", \"value\")));\n  EXPECT_EQ(R\"(key=\"10\")\", LOG_STR(log_kv_pair(\"key\", 10)));\n  EXPECT_EQ(R\"(v0|\"v1\")\", LOG_STR(log_kv_pair<'|'>(\"v0\", \"v1\")));\n  EXPECT_EQ(R\"(10+(20))\", LOG_STR(log_kv_pair<'+', '(', ')'>(10, 20)));\n\n  {\n    std::string_view s(\"10\");\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, s)));\n\n    std::string_view &ref = s;\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(ref, ref)));\n\n    std::string_view const &cref = s;\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(cref, cref)));\n\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, ref)));\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, cref)));\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(ref, cref)));\n  }\n\n  {\n    std::string s(\"10\");\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, s)));\n\n    std::string &ref = s;\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(ref, ref)));\n\n    std::string const &cref = s;\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(cref, cref)));\n\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, ref)));\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(s, cref)));\n    EXPECT_EQ(R\"(10=\"10\")\", LOG_STR(log_kv_pair(ref, cref)));\n  }\n}\n\nTEST(log_modifiers, log_call)\n{\n  int when_true_count = 0;\n  std::string const when_true_value = \"TTT\";\n  auto const when_true = [&] {\n    ++when_true_count;\n    return when_true_value;\n  };\n\n  int when_false_count = 0;\n  std::string const when_false_value = \"FFF\";\n  [[maybe_unused]] auto const when_false = [&] {\n    ++when_false_count;\n    return when_false_value;\n  };\n\n  EXPECT_EQ(\"TTT\", LOG_STR(log_call(when_true)));\n  EXPECT_EQ(1, when_true_count);\n  EXPECT_EQ(0, when_false_count);\n\n  EXPECT_EQ(\"FFF\", LOG_STR(log_call(when_false)));\n  EXPECT_EQ(1, when_true_count);\n  EXPECT_EQ(1, when_false_count);\n\n  EXPECT_EQ(\"TTT\", LOG_STR(log_either(true, log_call(when_true), log_call(when_false))));\n  EXPECT_EQ(2, when_true_count);\n  EXPECT_EQ(1, when_false_count);\n\n  EXPECT_EQ(\"FFF\", LOG_STR(log_either(false, log_call(when_true), log_call(when_false))));\n  EXPECT_EQ(2, when_true_count);\n  EXPECT_EQ(2, when_false_count);\n}\n\nTEST(log_modifiers, log_lazy)\n{\n  int when_true_count = 0;\n  std::string_view const when_true_value = \"TTT\";\n  auto const when_true = [&] {\n    ++when_true_count;\n    return when_true_value;\n  };\n\n  int when_false_count = 0;\n  std::string_view const when_false_value = \"FFF\";\n  [[maybe_unused]] auto const when_false = [&] {\n    ++when_false_count;\n    return when_false_value;\n  };\n\n  EXPECT_EQ(\"TTT\", LOG_STR(LOG_LAZY(when_true())));\n  EXPECT_EQ(1, when_true_count);\n  EXPECT_EQ(0, when_false_count);\n\n  EXPECT_EQ(\"FFF\", LOG_STR(LOG_LAZY(when_false())));\n  EXPECT_EQ(1, when_true_count);\n  EXPECT_EQ(1, when_false_count);\n\n  EXPECT_EQ(\"TTT\", LOG_STR(log_either(true, LOG_LAZY(when_true()), LOG_LAZY(when_false()))));\n  EXPECT_EQ(2, when_true_count);\n  EXPECT_EQ(1, when_false_count);\n\n  EXPECT_EQ(\"FFF\", LOG_STR(log_either(false, LOG_LAZY(when_true()), LOG_LAZY(when_false()))));\n  EXPECT_EQ(2, when_true_count);\n  EXPECT_EQ(2, when_false_count);\n}\n\n} // namespace\n"
  },
  {
    "path": "util/log_whitelist.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/log_whitelist.h>\n\nLogWhitelistRegistry::Proxy::Proxy(void (*whitelist_all)()) : whitelist_all(whitelist_all) {}\n\nLogWhitelistRegistry::Proxy *LogWhitelistRegistry::head_ = nullptr;\n\nvoid LogWhitelistRegistry::register_log_whitelist(Proxy &proxy)\n{\n  proxy.next = head_;\n  head_ = &proxy;\n}\n\nvoid LogWhitelistRegistry::whitelist_all()\n{\n  for (auto proxy = head_; proxy; proxy = proxy->next) {\n    proxy->whitelist_all();\n  }\n}\n\nvoid log_whitelist_all_globally()\n{\n  LogWhitelistRegistry::whitelist_all();\n}\n"
  },
  {
    "path": "util/log_whitelist.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/args_parser.h>\n#include <util/enum.h>\n\n#include <spdlog/fmt/fmt.h>\n\n#include <initializer_list>\n#include <list>\n#include <tuple>\n\n/**\n * Implements a log whitelist.\n *\n * All entries are whitelisted by default.\n *\n * Separate whitelists are maintained for each `Which` enum class given.\n *\n * To keep things simple and the implementation small, bitfields are used.\n * Therefore `Which` enum class must have an arithmetic underlying type\n * convertible to `std::uint8_t` assuming integer values smaller than 64. This\n * limitation may be lifted in the future but this class is guaranteed to be\n * backwards compatible.\n */\n\n// these function are not thread-safe for performance reasons\n// call them only in main before any threads are created\ntemplate <typename Which> void log_whitelist_all();\ntemplate <typename Which> void log_whitelist_clear();\ntemplate <typename Which> void set_log_whitelist(std::list<Which> whitelist);\ntemplate <typename Which> void set_log_whitelist(std::initializer_list<Which> whitelist);\nvoid log_whitelist_all_globally();\n\n// this function is thread-safe\ntemplate <typename Whitelist, typename = std::enable_if_t<std::is_enum_v<Whitelist>>> bool is_log_whitelisted(Whitelist which);\n\n// this function is thread-safe\ntemplate <typename... Whitelist> bool is_log_whitelisted(std::tuple<Whitelist...> const &which);\n\n// args parser handler for whitelists\ntemplate <typename Which> class LogWhitelistHandler : public cli::ArgsParser::Handler {\npublic:\n  LogWhitelistHandler(cli::ArgsParser &parser, std::string const &option_name);\n\n  void handle() override;\n\nprivate:\n  std::string option_name_;\n  cli::ArgsParser::ArgProxy<std::string> log_whitelist_;\n};\n\n#include <util/log_whitelist.inl>\n"
  },
  {
    "path": "util/log_whitelist.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <type_traits>\n#include <utility>\n\n#include <iostream>\n\n#include <cassert>\n#include <cstdint>\n\nclass LogWhitelistRegistry {\npublic:\n  class Proxy {\n  public:\n    Proxy(void (*whitelist_all)());\n\n    void (*whitelist_all)();\n    Proxy *next = nullptr;\n  };\n\n  static void register_log_whitelist(Proxy &proxy);\n\n  static void whitelist_all();\n\nprivate:\n  static Proxy *head_;\n};\n\ntemplate <typename Which> class LogWhitelist {\n  static_assert(std::is_enum_v<Which>, \"Which must be an enum class\");\n\npublic:\n  // these functions are not thread-safe for performance reasons\n  // call them only in main before any threads are created\n  static void whitelist_all();\n  static void set_whitelist(std::initializer_list<Which> whitelist = {});\n  static void set_whitelist(std::list<Which> whitelist);\n\n  static bool is_whitelisted(Which which);\n\n  static LogWhitelistRegistry::Proxy proxy;\n\nprivate:\n  static std::uint64_t whitelist_;\n  static constexpr auto const max_filter_ = 64;\n};\n\ntemplate <typename Which> std::uint64_t LogWhitelist<Which>::whitelist_ = 0;\n\ntemplate <typename Which> LogWhitelistRegistry::Proxy LogWhitelist<Which>::proxy{whitelist_all};\n\ntemplate <typename Which> void LogWhitelist<Which>::whitelist_all()\n{\n  whitelist_ = static_cast<std::uint64_t>(~static_cast<std::uint64_t>(0));\n}\n\ntemplate <typename Which> void LogWhitelist<Which>::set_whitelist(std::initializer_list<Which> whitelist)\n{\n  whitelist_ = 0;\n\n  for (auto const which : whitelist) {\n    auto const filter = static_cast<std::underlying_type_t<Which>>(which);\n    assert(filter < max_filter_);\n    whitelist_ |= static_cast<std::uint64_t>(static_cast<std::uint64_t>(1) << static_cast<std::uint64_t>(filter));\n  }\n}\n\ntemplate <typename Which> void LogWhitelist<Which>::set_whitelist(std::list<Which> whitelist)\n{\n  whitelist_ = 0;\n\n  for (auto const which : whitelist) {\n    auto const filter = static_cast<std::underlying_type_t<Which>>(which);\n    assert(filter < max_filter_);\n    whitelist_ |= static_cast<std::uint64_t>(static_cast<std::uint64_t>(1) << static_cast<std::uint64_t>(filter));\n  }\n}\n\ntemplate <typename Which> bool LogWhitelist<Which>::is_whitelisted(Which which)\n{\n  return (whitelist_ & static_cast<std::uint64_t>(static_cast<std::uint64_t>(1) << static_cast<std::uint64_t>(which))) != 0;\n}\n\ntemplate <typename Which> void log_whitelist_all()\n{\n  LogWhitelist<Which>::whitelist_all();\n}\n\ntemplate <typename Which> void log_whitelist_clear()\n{\n  LogWhitelist<Which>::set_whitelist({});\n}\n\ntemplate <typename Which> void set_log_whitelist(std::initializer_list<Which> whitelist)\n{\n  LogWhitelist<Which>::set_whitelist(std::move(whitelist));\n}\n\ntemplate <typename Which> void set_log_whitelist(std::list<Which> whitelist)\n{\n  LogWhitelist<Which>::set_whitelist(std::move(whitelist));\n}\n\ntemplate <typename Whitelist, typename> bool is_log_whitelisted(Whitelist which)\n{\n  return LogWhitelist<Whitelist>::is_whitelisted(which);\n}\n\ntemplate <typename... Whitelist, std::size_t... Index>\nbool is_log_whitelisted_impl(std::tuple<Whitelist...> const &which, std::index_sequence<Index...>)\n{\n  return (true && ... && (is_log_whitelisted(std::get<Index>(which))));\n}\n\ntemplate <typename... Whitelist> bool is_log_whitelisted(std::tuple<Whitelist...> const &which)\n{\n  return is_log_whitelisted_impl(which, std::make_index_sequence<sizeof...(Whitelist)>{});\n}\n\n// LogWhitelistHandler\n\ntemplate <typename Which>\nLogWhitelistHandler<Which>::LogWhitelistHandler(cli::ArgsParser &parser, std::string const &option_name)\n    : option_name_(option_name), log_whitelist_(parser.add_arg<std::string>(fmt::format(\"log-whitelist-{}\", option_name), [&] {\n        auto message = fmt::format(\"Comma separated list of logs to whitelist from {{\", option_name);\n        auto const &values = enum_traits<Which>::values;\n        for (std::size_t i = 0; i < values.size(); ++i) {\n          if (i) {\n            message.append(\", \");\n          }\n          message.append(to_string(values[i]));\n        }\n        message.push_back('}');\n        return message;\n      }()))\n{\n  LogWhitelistRegistry::register_log_whitelist(LogWhitelist<Which>::proxy);\n}\n\ntemplate <typename Which> void LogWhitelistHandler<Which>::handle()\n{\n  std::list<std::string> to_parse;\n  cli::ArgsParser::split_arguments(*log_whitelist_, to_parse);\n\n  if (to_parse.size() == 1 && to_parse.front() == \"*\") {\n    log_whitelist_all<Which>();\n    return;\n  }\n\n  std::list<std::string> left_overs;\n\n  std::list<Which> enum_list;\n  for (auto const &token : to_parse) {\n    Which v;\n    if (enum_from_string(token, v)) {\n      enum_list.push_back(v);\n    } else {\n      left_overs.push_back(token);\n    }\n  }\n\n  if (enum_list.size() > 0) {\n    set_log_whitelist<Which>(enum_list);\n  }\n\n  if (left_overs.size() > 0) {\n    std::string err;\n    for (auto const &s : left_overs) {\n      if (err.size() > 0) {\n        err += \",\";\n      }\n      err += s;\n    }\n    throw std::runtime_error(fmt::format(\"Unable to enable requested logs for whitelist '{}': {}\", option_name_, err));\n  }\n}\n"
  },
  {
    "path": "util/logger.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <generated/ebpf_net/ingest/writer.h>\n#include <util/log.h>\n\n#include <spdlog/fmt/fmt.h>\n#include <spdlog/fmt/ostr.h>\n\n#include <utility>\n\nnamespace logging {\n\nclass Logger {\npublic:\n  explicit Logger(::ebpf_net::ingest::Writer &writer) : writer_(writer) {}\n\n  Logger(const Logger &) = delete;\n  Logger &operator=(const Logger &) = delete;\n\n  template <typename Format, typename... Args> inline void info(Format &&format, Args &&... args)\n  {\n    LOG::info(log_message<spdlog::level::info>(std::forward<Format>(format), std::forward<Args>(args)...));\n  }\n\n  template <typename Format, typename... Args> inline void warn(Format &&format, Args &&... args)\n  {\n    LOG::warn(log_message<spdlog::level::warn>(std::forward<Format>(format), std::forward<Args>(args)...));\n  }\n\n  template <typename Format, typename... Args> inline void error(Format &&format, Args &&... args)\n  {\n    LOG::error(log_message<spdlog::level::err>(std::forward<Format>(format), std::forward<Args>(args)...));\n  }\n\n  template <typename Format, typename... Args> inline void critical(Format &&format, Args &&... args)\n  {\n    LOG::critical(log_message<spdlog::level::critical>(std::forward<Format>(format), std::forward<Args>(args)...));\n  }\n\nprivate:\n  template <spdlog::level::level_enum Level, typename Format, typename... Args>\n  inline std::string log_message(Format &&format, Args &&... args)\n  {\n    auto message = fmt::vformat(std::forward<Format>(format), fmt::make_format_args(args...));\n\n    static_assert(static_cast<std::underlying_type_t<spdlog::level::level_enum>>(Level) == static_cast<u8>(Level));\n\n    writer_.log_message(static_cast<u8>(Level), jb_blob{message});\n\n    return message;\n  }\n\n  ::ebpf_net::ingest::Writer &writer_;\n};\n\n} // namespace logging\n"
  },
  {
    "path": "util/lookup3.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n-------------------------------------------------------------------------------\nlookup3.c, by Bob Jenkins, May 2006, Public Domain.\n\nThese are functions for producing 32-bit hashes for hash table lookup.\nhashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()\nare externally useful functions.  Routines to test the hash are included\nif SELF_TEST is defined.  You can use this free for any purpose.  It's in\nthe public domain.  It has no warranty.\n\nYou probably want to use hashlittle().  hashlittle() and hashbig()\nhash byte arrays.  hashlittle() is is faster than hashbig() on\nlittle-endian machines.  Intel and AMD are little-endian machines.\nOn second thought, you probably want hashlittle2(), which is identical to\nhashlittle() except it returns two 32-bit hashes for the price of one.\nYou could implement hashbig2() if you wanted but I haven't bothered here.\n\nIf you want to find a hash of, say, exactly 7 integers, do\n  a = i1;  b = i2;  c = i3;\n  mix(a,b,c);\n  a += i4; b += i5; c += i6;\n  mix(a,b,c);\n  a += i7;\n  final(a,b,c);\nthen use c as the hash value.  If you have a variable length array of\n4-byte integers to hash, use hashword().  If you have a byte array (like\na character string), use hashlittle().  If you have several byte arrays, or\na mix of things, see the comments above hashlittle().\n\nWhy is this so big?  I read 12 bytes at a time into 3 4-byte integers,\nthen mix those integers.  This is fast (you can do a lot more thorough\nmixing with 12*3 instructions on 3 integers than you can with 3 instructions\non 1 byte), but shoehorning those bytes into integers efficiently is messy.\n-------------------------------------------------------------------------------\n*/\n\n#include <stdint.h>    /* defines uint32_t etc */\n#include <stdio.h>     /* defines printf for tests */\n#include <sys/param.h> /* attempt to define endianness */\n#include <time.h>      /* defines time_t for timings in the test */\n#include <util/lookup3.h>\n#ifdef linux\n#include <endian.h> /* attempt to define endianness */\n#endif\n\n/*\n * My best guess at if you are big-endian or little-endian.  This may\n * need adjustment.\n */\n#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) ||                                  \\\n    (defined(i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(vax) ||      \\\n     defined(MIPSEL))\n#define HASH_LITTLE_ENDIAN 1\n#define HASH_BIG_ENDIAN 0\n#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) ||                                      \\\n    (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))\n#define HASH_LITTLE_ENDIAN 0\n#define HASH_BIG_ENDIAN 1\n#else\n#define HASH_LITTLE_ENDIAN 0\n#define HASH_BIG_ENDIAN 0\n#endif\n\n#define hashsize(n) ((uint32_t)1 << (n))\n#define hashmask(n) (hashsize(n) - 1)\n#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))\n\n/*\n-------------------------------------------------------------------------------\nmix -- mix 3 32-bit values reversibly.\n\nThis is reversible, so any information in (a,b,c) before mix() is\nstill in (a,b,c) after mix().\n\nIf four pairs of (a,b,c) inputs are run through mix(), or through\nmix() in reverse, there are at least 32 bits of the output that\nare sometimes the same for one pair and different for another pair.\nThis was tested for:\n* pairs that differed by one bit, by two bits, in any combination\n  of top bits of (a,b,c), or in any combination of bottom bits of\n  (a,b,c).\n* \"differ\" is defined as +, -, ^, or ~^.  For + and -, I transformed\n  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as\n  is commonly produced by subtraction) look like a single 1-bit\n  difference.\n* the base values were pseudorandom, all zero but one bit set, or\n  all zero plus a counter that starts at zero.\n\nSome k values for my \"a-=c; a^=rot(c,k); c+=b;\" arrangement that\nsatisfy this are\n    4  6  8 16 19  4\n    9 15  3 18 27 15\n   14  9  3  7 17  3\nWell, \"9 15 3 18 27 15\" didn't quite get 32 bits diffing\nfor \"differ\" defined as + with a one-bit base and a two-bit delta.  I\nused http://burtleburtle.net/bob/hash/avalanche.html to choose\nthe operations, constants, and arrangements of the variables.\n\nThis does not achieve avalanche.  There are input bits of (a,b,c)\nthat fail to affect some output bits of (a,b,c), especially of a.  The\nmost thoroughly mixed value is c, but it doesn't really even achieve\navalanche in c.\n\nThis allows some parallelism.  Read-after-writes are good at doubling\nthe number of bits affected, so the goal of mixing pulls in the opposite\ndirection as the goal of parallelism.  I did what I could.  Rotates\nseem to cost as much as shifts on every machine I could lay my hands\non, and rotates are much kinder to the top and bottom bits, so I used\nrotates.\n-------------------------------------------------------------------------------\n*/\n#define mix(a, b, c)                                                                                                           \\\n  {                                                                                                                            \\\n    a -= c;                                                                                                                    \\\n    a ^= rot(c, 4);                                                                                                            \\\n    c += b;                                                                                                                    \\\n    b -= a;                                                                                                                    \\\n    b ^= rot(a, 6);                                                                                                            \\\n    a += c;                                                                                                                    \\\n    c -= b;                                                                                                                    \\\n    c ^= rot(b, 8);                                                                                                            \\\n    b += a;                                                                                                                    \\\n    a -= c;                                                                                                                    \\\n    a ^= rot(c, 16);                                                                                                           \\\n    c += b;                                                                                                                    \\\n    b -= a;                                                                                                                    \\\n    b ^= rot(a, 19);                                                                                                           \\\n    a += c;                                                                                                                    \\\n    c -= b;                                                                                                                    \\\n    c ^= rot(b, 4);                                                                                                            \\\n    b += a;                                                                                                                    \\\n  }\n\n/*\n-------------------------------------------------------------------------------\nfinal -- final mixing of 3 32-bit values (a,b,c) into c\n\nPairs of (a,b,c) values differing in only a few bits will usually\nproduce values of c that look totally different.  This was tested for\n* pairs that differed by one bit, by two bits, in any combination\n  of top bits of (a,b,c), or in any combination of bottom bits of\n  (a,b,c).\n* \"differ\" is defined as +, -, ^, or ~^.  For + and -, I transformed\n  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as\n  is commonly produced by subtraction) look like a single 1-bit\n  difference.\n* the base values were pseudorandom, all zero but one bit set, or\n  all zero plus a counter that starts at zero.\n\nThese constants passed:\n 14 11 25 16 4 14 24\n 12 14 25 16 4 14 24\nand these came close:\n  4  8 15 26 3 22 24\n 10  8 15 26 3 22 24\n 11  8 15 26 3 22 24\n-------------------------------------------------------------------------------\n*/\n#define final(a, b, c)                                                                                                         \\\n  {                                                                                                                            \\\n    c ^= b;                                                                                                                    \\\n    c -= rot(b, 14);                                                                                                           \\\n    a ^= c;                                                                                                                    \\\n    a -= rot(c, 11);                                                                                                           \\\n    b ^= a;                                                                                                                    \\\n    b -= rot(a, 25);                                                                                                           \\\n    c ^= b;                                                                                                                    \\\n    c -= rot(b, 16);                                                                                                           \\\n    a ^= c;                                                                                                                    \\\n    a -= rot(c, 4);                                                                                                            \\\n    b ^= a;                                                                                                                    \\\n    b -= rot(a, 14);                                                                                                           \\\n    c ^= b;                                                                                                                    \\\n    c -= rot(b, 24);                                                                                                           \\\n  }\n\n/*\n--------------------------------------------------------------------\n This works on all machines.  To be useful, it requires\n -- that the key be an array of uint32_t's, and\n -- that the length be the number of uint32_t's in the key\n\n The function hashword() is identical to hashlittle() on little-endian\n machines, and identical to hashbig() on big-endian machines,\n except that the length has to be measured in uint32_ts rather than in\n bytes.  hashlittle() is more complicated than hashword() only because\n hashlittle() has to dance around fitting the key bytes into registers.\n--------------------------------------------------------------------\n*/\nuint32_t lookup3_hashword(\n    const uint32_t *k, /* the key, an array of uint32_t values */\n    size_t length,     /* the length of the key, in uint32_ts */\n    uint32_t initval)  /* the previous hash, or an arbitrary value */\n{\n  uint32_t a, b, c;\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + (((uint32_t)length) << 2) + initval;\n\n  /*------------------------------------------------- handle most of the key */\n  while (length > 3) {\n    a += k[0];\n    b += k[1];\n    c += k[2];\n    mix(a, b, c);\n    length -= 3;\n    k += 3;\n  }\n\n  /*------------------------------------------- handle the last 3 uint32_t's */\n  switch (length) /* all the case statements fall through */\n  {\n  case 3:\n    c += k[2];\n  case 2:\n    b += k[1];\n  case 1:\n    a += k[0];\n    final(a, b, c);\n  case 0: /* case 0: nothing left to add */\n    break;\n  }\n  /*------------------------------------------------------ report the result */\n  return c;\n}\n\n/*\n--------------------------------------------------------------------\nhashword2() -- same as hashword(), but take two seeds and return two\n32-bit values.  pc and pb must both be nonnull, and *pc and *pb must\nboth be initialized with seeds.  If you pass in (*pb)==0, the output\n(*pc) will be the same as the return value from hashword().\n--------------------------------------------------------------------\n*/\nvoid lookup3_hashword2(\n    const uint32_t *k, /* the key, an array of uint32_t values */\n    size_t length,     /* the length of the key, in uint32_ts */\n    uint32_t *pc,      /* IN: seed OUT: primary hash value */\n    uint32_t *pb)      /* IN: more seed OUT: secondary hash value */\n{\n  uint32_t a, b, c;\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)(length << 2)) + *pc;\n  c += *pb;\n\n  /*------------------------------------------------- handle most of the key */\n  while (length > 3) {\n    a += k[0];\n    b += k[1];\n    c += k[2];\n    mix(a, b, c);\n    length -= 3;\n    k += 3;\n  }\n\n  /*------------------------------------------- handle the last 3 uint32_t's */\n  switch (length) /* all the case statements fall through */\n  {\n  case 3:\n    c += k[2];\n  case 2:\n    b += k[1];\n  case 1:\n    a += k[0];\n    final(a, b, c);\n  case 0: /* case 0: nothing left to add */\n    break;\n  }\n  /*------------------------------------------------------ report the result */\n  *pc = c;\n  *pb = b;\n}\n\n/*\n-------------------------------------------------------------------------------\nhashlittle() -- hash a variable-length key into a 32-bit value\n  k       : the key (the unaligned variable-length array of bytes)\n  length  : the length of the key, counting by bytes\n  initval : can be any 4-byte value\nReturns a 32-bit value.  Every bit of the key affects every bit of\nthe return value.  Two keys differing by one or two bits will have\ntotally different hash values.\n\nThe best hash table sizes are powers of 2.  There is no need to do\nmod a prime (mod is sooo slow!).  If you need less than 32 bits,\nuse a bitmask.  For example, if you need only 10 bits, do\n  h = (h & hashmask(10));\nIn which case, the hash table should have hashsize(10) elements.\n\nIf you are hashing n strings (uint8_t **)k, do it like this:\n  for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);\n\nBy Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this\ncode any way you wish, private, educational, or commercial.  It's free.\n\nUse for hash table lookup, or anything where one collision in 2^^32 is\nacceptable.  Do NOT use for cryptographic purposes.\n-------------------------------------------------------------------------------\n*/\n\nuint32_t lookup3_hashlittle(const void *key, size_t length, uint32_t initval)\n{\n  uint32_t a, b, c; /* internal state */\n  union {\n    const void *ptr;\n    size_t i;\n  } u; /* needed for Mac Powerbook G4 */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;\n\n  u.ptr = key;\n  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {\n    const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a, b, c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /*\n     * \"k[2]&0xffffff\" actually reads beyond the end of the string, but\n     * then masks off the part it's not allowed to read.  Because the\n     * string is aligned, the masked-off tail is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticably faster for short strings (like English words).\n     */\n#ifndef VALGRIND\n\n    switch (length) {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += k[2] & 0xffffff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 10:\n      c += k[2] & 0xffff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 9:\n      c += k[2] & 0xff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += k[1] & 0xffffff;\n      a += k[0];\n      break;\n    case 6:\n      b += k[1] & 0xffff;\n      a += k[0];\n      break;\n    case 5:\n      b += k[1] & 0xff;\n      a += k[0];\n      break;\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += k[0] & 0xffffff;\n      break;\n    case 2:\n      a += k[0] & 0xffff;\n      break;\n    case 1:\n      a += k[0] & 0xff;\n      break;\n    case 0:\n      return c; /* zero length strings require no mixing */\n    }\n\n#else /* make valgrind happy */\n\n    const uint8_t *k8 = (const uint8_t *)k;\n    switch (length) {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += ((uint32_t)k8[10]) << 16; /* fall through */\n    case 10:\n      c += ((uint32_t)k8[9]) << 8; /* fall through */\n    case 9:\n      c += k8[8]; /* fall through */\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += ((uint32_t)k8[6]) << 16; /* fall through */\n    case 6:\n      b += ((uint32_t)k8[5]) << 8; /* fall through */\n    case 5:\n      b += k8[4]; /* fall through */\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += ((uint32_t)k8[2]) << 16; /* fall through */\n    case 2:\n      a += ((uint32_t)k8[1]) << 8; /* fall through */\n    case 1:\n      a += k8[0];\n      break;\n    case 0:\n      return c;\n    }\n\n#endif /* !valgrind */\n\n  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {\n    const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */\n    const uint8_t *k8;\n\n    /*--------------- all but last block: aligned reads and different mixing */\n    while (length > 12) {\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      c += k[4] + (((uint32_t)k[5]) << 16);\n      mix(a, b, c);\n      length -= 12;\n      k += 6;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    k8 = (const uint8_t *)k;\n    switch (length) {\n    case 12:\n      c += k[4] + (((uint32_t)k[5]) << 16);\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 11:\n      c += ((uint32_t)k8[10]) << 16; /* fall through */\n    case 10:\n      c += k[4];\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 9:\n      c += k8[8]; /* fall through */\n    case 8:\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 7:\n      b += ((uint32_t)k8[6]) << 16; /* fall through */\n    case 6:\n      b += k[2];\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 5:\n      b += k8[4]; /* fall through */\n    case 4:\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 3:\n      a += ((uint32_t)k8[2]) << 16; /* fall through */\n    case 2:\n      a += k[0];\n      break;\n    case 1:\n      a += k8[0];\n      break;\n    case 0:\n      return c; /* zero length requires no mixing */\n    }\n\n  } else { /* need to read the key one byte at a time */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += k[0];\n      a += ((uint32_t)k[1]) << 8;\n      a += ((uint32_t)k[2]) << 16;\n      a += ((uint32_t)k[3]) << 24;\n      b += k[4];\n      b += ((uint32_t)k[5]) << 8;\n      b += ((uint32_t)k[6]) << 16;\n      b += ((uint32_t)k[7]) << 24;\n      c += k[8];\n      c += ((uint32_t)k[9]) << 8;\n      c += ((uint32_t)k[10]) << 16;\n      c += ((uint32_t)k[11]) << 24;\n      mix(a, b, c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch (length) /* all the case statements fall through */\n    {\n    case 12:\n      c += ((uint32_t)k[11]) << 24;\n    case 11:\n      c += ((uint32_t)k[10]) << 16;\n    case 10:\n      c += ((uint32_t)k[9]) << 8;\n    case 9:\n      c += k[8];\n    case 8:\n      b += ((uint32_t)k[7]) << 24;\n    case 7:\n      b += ((uint32_t)k[6]) << 16;\n    case 6:\n      b += ((uint32_t)k[5]) << 8;\n    case 5:\n      b += k[4];\n    case 4:\n      a += ((uint32_t)k[3]) << 24;\n    case 3:\n      a += ((uint32_t)k[2]) << 16;\n    case 2:\n      a += ((uint32_t)k[1]) << 8;\n    case 1:\n      a += k[0];\n      break;\n    case 0:\n      return c;\n    }\n  }\n\n  final(a, b, c);\n  return c;\n}\n\n/*\n * hashlittle2: return 2 32-bit hash values\n *\n * This is identical to hashlittle(), except it returns two 32-bit hash\n * values instead of just one.  This is good enough for hash table\n * lookup with 2^^64 buckets, or if you want a second hash if you're not\n * happy with the first, or if you want a probably-unique 64-bit ID for\n * the key.  *pc is better mixed than *pb, so use *pc first.  If you want\n * a 64-bit value do something like \"*pc + (((uint64_t)*pb)<<32)\".\n */\nvoid lookup3_hashlittle2(\n    const void *key, /* the key to hash */\n    size_t length,   /* length of the key */\n    uint32_t *pc,    /* IN: primary initval, OUT: primary hash */\n    uint32_t *pb)    /* IN: secondary initval, OUT: secondary hash */\n{\n  uint32_t a, b, c; /* internal state */\n  union {\n    const void *ptr;\n    size_t i;\n  } u; /* needed for Mac Powerbook G4 */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;\n  c += *pb;\n\n  u.ptr = key;\n  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {\n    const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a, b, c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /*\n     * \"k[2]&0xffffff\" actually reads beyond the end of the string, but\n     * then masks off the part it's not allowed to read.  Because the\n     * string is aligned, the masked-off tail is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticably faster for short strings (like English words).\n     */\n#ifndef VALGRIND\n\n    switch (length) {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += k[2] & 0xffffff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 10:\n      c += k[2] & 0xffff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 9:\n      c += k[2] & 0xff;\n      b += k[1];\n      a += k[0];\n      break;\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += k[1] & 0xffffff;\n      a += k[0];\n      break;\n    case 6:\n      b += k[1] & 0xffff;\n      a += k[0];\n      break;\n    case 5:\n      b += k[1] & 0xff;\n      a += k[0];\n      break;\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += k[0] & 0xffffff;\n      break;\n    case 2:\n      a += k[0] & 0xffff;\n      break;\n    case 1:\n      a += k[0] & 0xff;\n      break;\n    case 0:\n      *pc = c;\n      *pb = b;\n      return; /* zero length strings require no mixing */\n    }\n\n#else /* make valgrind happy */\n\n    const uint8_t *k8 = (const uint8_t *)k;\n    switch (length) {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += ((uint32_t)k8[10]) << 16; /* fall through */\n    case 10:\n      c += ((uint32_t)k8[9]) << 8; /* fall through */\n    case 9:\n      c += k8[8]; /* fall through */\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += ((uint32_t)k8[6]) << 16; /* fall through */\n    case 6:\n      b += ((uint32_t)k8[5]) << 8; /* fall through */\n    case 5:\n      b += k8[4]; /* fall through */\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += ((uint32_t)k8[2]) << 16; /* fall through */\n    case 2:\n      a += ((uint32_t)k8[1]) << 8; /* fall through */\n    case 1:\n      a += k8[0];\n      break;\n    case 0:\n      *pc = c;\n      *pb = b;\n      return; /* zero length strings require no mixing */\n    }\n\n#endif /* !valgrind */\n\n  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {\n    const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */\n    const uint8_t *k8;\n\n    /*--------------- all but last block: aligned reads and different mixing */\n    while (length > 12) {\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      c += k[4] + (((uint32_t)k[5]) << 16);\n      mix(a, b, c);\n      length -= 12;\n      k += 6;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    k8 = (const uint8_t *)k;\n    switch (length) {\n    case 12:\n      c += k[4] + (((uint32_t)k[5]) << 16);\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 11:\n      c += ((uint32_t)k8[10]) << 16; /* fall through */\n    case 10:\n      c += k[4];\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 9:\n      c += k8[8]; /* fall through */\n    case 8:\n      b += k[2] + (((uint32_t)k[3]) << 16);\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 7:\n      b += ((uint32_t)k8[6]) << 16; /* fall through */\n    case 6:\n      b += k[2];\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 5:\n      b += k8[4]; /* fall through */\n    case 4:\n      a += k[0] + (((uint32_t)k[1]) << 16);\n      break;\n    case 3:\n      a += ((uint32_t)k8[2]) << 16; /* fall through */\n    case 2:\n      a += k[0];\n      break;\n    case 1:\n      a += k8[0];\n      break;\n    case 0:\n      *pc = c;\n      *pb = b;\n      return; /* zero length strings require no mixing */\n    }\n\n  } else { /* need to read the key one byte at a time */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += k[0];\n      a += ((uint32_t)k[1]) << 8;\n      a += ((uint32_t)k[2]) << 16;\n      a += ((uint32_t)k[3]) << 24;\n      b += k[4];\n      b += ((uint32_t)k[5]) << 8;\n      b += ((uint32_t)k[6]) << 16;\n      b += ((uint32_t)k[7]) << 24;\n      c += k[8];\n      c += ((uint32_t)k[9]) << 8;\n      c += ((uint32_t)k[10]) << 16;\n      c += ((uint32_t)k[11]) << 24;\n      mix(a, b, c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch (length) /* all the case statements fall through */\n    {\n    case 12:\n      c += ((uint32_t)k[11]) << 24;\n    case 11:\n      c += ((uint32_t)k[10]) << 16;\n    case 10:\n      c += ((uint32_t)k[9]) << 8;\n    case 9:\n      c += k[8];\n    case 8:\n      b += ((uint32_t)k[7]) << 24;\n    case 7:\n      b += ((uint32_t)k[6]) << 16;\n    case 6:\n      b += ((uint32_t)k[5]) << 8;\n    case 5:\n      b += k[4];\n    case 4:\n      a += ((uint32_t)k[3]) << 24;\n    case 3:\n      a += ((uint32_t)k[2]) << 16;\n    case 2:\n      a += ((uint32_t)k[1]) << 8;\n    case 1:\n      a += k[0];\n      break;\n    case 0:\n      *pc = c;\n      *pb = b;\n      return; /* zero length strings require no mixing */\n    }\n  }\n\n  final(a, b, c);\n  *pc = c;\n  *pb = b;\n}\n\n/*\n * hashbig():\n * This is the same as hashword() on big-endian machines.  It is different\n * from hashlittle() on all machines.  hashbig() takes advantage of\n * big-endian byte ordering.\n */\nuint32_t lookup3_hashbig(const void *key, size_t length, uint32_t initval)\n{\n  uint32_t a, b, c;\n  union {\n    const void *ptr;\n    size_t i;\n  } u; /* to cast key to (size_t) happily */\n\n  /* Set up the internal state */\n  a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;\n\n  u.ptr = key;\n  if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {\n    const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */\n\n    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += k[0];\n      b += k[1];\n      c += k[2];\n      mix(a, b, c);\n      length -= 12;\n      k += 3;\n    }\n\n    /*----------------------------- handle the last (probably partial) block */\n    /*\n     * \"k[2]<<8\" actually reads beyond the end of the string, but\n     * then shifts out the part it's not allowed to read.  Because the\n     * string is aligned, the illegal read is in the same word as the\n     * rest of the string.  Every machine with memory protection I've seen\n     * does it on word boundaries, so is OK with this.  But VALGRIND will\n     * still catch it and complain.  The masking trick does make the hash\n     * noticably faster for short strings (like English words).\n     */\n#ifndef VALGRIND\n\n    switch (length) {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += k[2] & 0xffffff00;\n      b += k[1];\n      a += k[0];\n      break;\n    case 10:\n      c += k[2] & 0xffff0000;\n      b += k[1];\n      a += k[0];\n      break;\n    case 9:\n      c += k[2] & 0xff000000;\n      b += k[1];\n      a += k[0];\n      break;\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += k[1] & 0xffffff00;\n      a += k[0];\n      break;\n    case 6:\n      b += k[1] & 0xffff0000;\n      a += k[0];\n      break;\n    case 5:\n      b += k[1] & 0xff000000;\n      a += k[0];\n      break;\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += k[0] & 0xffffff00;\n      break;\n    case 2:\n      a += k[0] & 0xffff0000;\n      break;\n    case 1:\n      a += k[0] & 0xff000000;\n      break;\n    case 0:\n      return c; /* zero length strings require no mixing */\n    }\n\n#else /* make valgrind happy */\n\n    const uint8_t *k8 = (const uint8_t *)k;\n    switch (length) /* all the case statements fall through */\n    {\n    case 12:\n      c += k[2];\n      b += k[1];\n      a += k[0];\n      break;\n    case 11:\n      c += ((uint32_t)k8[10]) << 8; /* fall through */\n    case 10:\n      c += ((uint32_t)k8[9]) << 16; /* fall through */\n    case 9:\n      c += ((uint32_t)k8[8]) << 24; /* fall through */\n    case 8:\n      b += k[1];\n      a += k[0];\n      break;\n    case 7:\n      b += ((uint32_t)k8[6]) << 8; /* fall through */\n    case 6:\n      b += ((uint32_t)k8[5]) << 16; /* fall through */\n    case 5:\n      b += ((uint32_t)k8[4]) << 24; /* fall through */\n    case 4:\n      a += k[0];\n      break;\n    case 3:\n      a += ((uint32_t)k8[2]) << 8; /* fall through */\n    case 2:\n      a += ((uint32_t)k8[1]) << 16; /* fall through */\n    case 1:\n      a += ((uint32_t)k8[0]) << 24;\n      break;\n    case 0:\n      return c;\n    }\n\n#endif /* !VALGRIND */\n\n  } else { /* need to read the key one byte at a time */\n    const uint8_t *k = (const uint8_t *)key;\n\n    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */\n    while (length > 12) {\n      a += ((uint32_t)k[0]) << 24;\n      a += ((uint32_t)k[1]) << 16;\n      a += ((uint32_t)k[2]) << 8;\n      a += ((uint32_t)k[3]);\n      b += ((uint32_t)k[4]) << 24;\n      b += ((uint32_t)k[5]) << 16;\n      b += ((uint32_t)k[6]) << 8;\n      b += ((uint32_t)k[7]);\n      c += ((uint32_t)k[8]) << 24;\n      c += ((uint32_t)k[9]) << 16;\n      c += ((uint32_t)k[10]) << 8;\n      c += ((uint32_t)k[11]);\n      mix(a, b, c);\n      length -= 12;\n      k += 12;\n    }\n\n    /*-------------------------------- last block: affect all 32 bits of (c) */\n    switch (length) /* all the case statements fall through */\n    {\n    case 12:\n      c += k[11];\n    case 11:\n      c += ((uint32_t)k[10]) << 8;\n    case 10:\n      c += ((uint32_t)k[9]) << 16;\n    case 9:\n      c += ((uint32_t)k[8]) << 24;\n    case 8:\n      b += k[7];\n    case 7:\n      b += ((uint32_t)k[6]) << 8;\n    case 6:\n      b += ((uint32_t)k[5]) << 16;\n    case 5:\n      b += ((uint32_t)k[4]) << 24;\n    case 4:\n      a += k[3];\n    case 3:\n      a += ((uint32_t)k[2]) << 8;\n    case 2:\n      a += ((uint32_t)k[1]) << 16;\n    case 1:\n      a += ((uint32_t)k[0]) << 24;\n      break;\n    case 0:\n      return c;\n    }\n  }\n\n  final(a, b, c);\n  return c;\n}\n\n#ifdef SELF_TEST\n\n/* used for timings */\nvoid driver1()\n{\n  uint8_t buf[256];\n  uint32_t i;\n  uint32_t h = 0;\n  time_t a, z;\n\n  time(&a);\n  for (i = 0; i < 256; ++i)\n    buf[i] = 'x';\n  for (i = 0; i < 1; ++i) {\n    h = hashlittle(&buf[0], 1, h);\n  }\n  time(&z);\n  if (z - a > 0)\n    printf(\"time %d %.8x\\n\", z - a, h);\n}\n\n/* check that every input bit changes every output bit half the time */\n#define HASHSTATE 1\n#define HASHLEN 1\n#define MAXPAIR 60\n#define MAXLEN 70\nvoid driver2()\n{\n  uint8_t qa[MAXLEN + 1], qb[MAXLEN + 2], *a = &qa[0], *b = &qb[1];\n  uint32_t c[HASHSTATE], d[HASHSTATE], i = 0, j = 0, k, l, m = 0, z;\n  uint32_t e[HASHSTATE], f[HASHSTATE], g[HASHSTATE], h[HASHSTATE];\n  uint32_t x[HASHSTATE], y[HASHSTATE];\n  uint32_t hlen;\n\n  printf(\"No more than %d trials should ever be needed \\n\", MAXPAIR / 2);\n  for (hlen = 0; hlen < MAXLEN; ++hlen) {\n    z = 0;\n    for (i = 0; i < hlen; ++i) /*----------------------- for each input byte, */\n    {\n      for (j = 0; j < 8; ++j) /*------------------------ for each input bit, */\n      {\n        for (m = 1; m < 8; ++m) /*------------ for serveral possible initvals, */\n        {\n          for (l = 0; l < HASHSTATE; ++l)\n            e[l] = f[l] = g[l] = h[l] = x[l] = y[l] = ~((uint32_t)0);\n\n          /*---- check that every output bit is affected by that input bit */\n          for (k = 0; k < MAXPAIR; k += 2) {\n            uint32_t finished = 1;\n            /* keys have one bit different */\n            for (l = 0; l < hlen + 1; ++l) {\n              a[l] = b[l] = (uint8_t)0;\n            }\n            /* have a and b be two keys differing in only one bit */\n            a[i] ^= (k << j);\n            a[i] ^= (k >> (8 - j));\n            c[0] = hashlittle(a, hlen, m);\n            b[i] ^= ((k + 1) << j);\n            b[i] ^= ((k + 1) >> (8 - j));\n            d[0] = hashlittle(b, hlen, m);\n            /* check every bit is 1, 0, set, and not set at least once */\n            for (l = 0; l < HASHSTATE; ++l) {\n              e[l] &= (c[l] ^ d[l]);\n              f[l] &= ~(c[l] ^ d[l]);\n              g[l] &= c[l];\n              h[l] &= ~c[l];\n              x[l] &= d[l];\n              y[l] &= ~d[l];\n              if (e[l] | f[l] | g[l] | h[l] | x[l] | y[l])\n                finished = 0;\n            }\n            if (finished)\n              break;\n          }\n          if (k > z)\n            z = k;\n          if (k == MAXPAIR) {\n            printf(\"Some bit didn't change: \");\n            printf(\"%.8x %.8x %.8x %.8x %.8x %.8x  \", e[0], f[0], g[0], h[0], x[0], y[0]);\n            printf(\"i %d j %d m %d len %d\\n\", i, j, m, hlen);\n          }\n          if (z == MAXPAIR)\n            goto done;\n        }\n      }\n    }\n  done:\n    if (z < MAXPAIR) {\n      printf(\"Mix success  %2d bytes  %2d initvals  \", i, m);\n      printf(\"required  %d  trials\\n\", z / 2);\n    }\n  }\n  printf(\"\\n\");\n}\n\n/* Check for reading beyond the end of the buffer and alignment problems */\nvoid driver3()\n{\n  uint8_t buf[MAXLEN + 20], *b;\n  uint32_t len;\n  uint8_t q[] = \"This is the time for all good men to come to the aid of their country...\";\n  uint32_t h;\n  uint8_t qq[] = \"xThis is the time for all good men to come to the aid of their country...\";\n  uint32_t i;\n  uint8_t qqq[] = \"xxThis is the time for all good men to come to the aid of their country...\";\n  uint32_t j;\n  uint8_t qqqq[] = \"xxxThis is the time for all good men to come to the aid of their country...\";\n  uint32_t ref, x, y;\n  uint8_t *p;\n\n  printf(\"Endianness.  These lines should all be the same (for values filled in):\\n\");\n  printf(\n      \"%.8x                            %.8x                            %.8x\\n\",\n      hashword((const uint32_t *)q, (sizeof(q) - 1) / 4, 13),\n      hashword((const uint32_t *)q, (sizeof(q) - 5) / 4, 13),\n      hashword((const uint32_t *)q, (sizeof(q) - 9) / 4, 13));\n  p = q;\n  printf(\n      \"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n      hashlittle(p, sizeof(q) - 1, 13),\n      hashlittle(p, sizeof(q) - 2, 13),\n      hashlittle(p, sizeof(q) - 3, 13),\n      hashlittle(p, sizeof(q) - 4, 13),\n      hashlittle(p, sizeof(q) - 5, 13),\n      hashlittle(p, sizeof(q) - 6, 13),\n      hashlittle(p, sizeof(q) - 7, 13),\n      hashlittle(p, sizeof(q) - 8, 13),\n      hashlittle(p, sizeof(q) - 9, 13),\n      hashlittle(p, sizeof(q) - 10, 13),\n      hashlittle(p, sizeof(q) - 11, 13),\n      hashlittle(p, sizeof(q) - 12, 13));\n  p = &qq[1];\n  printf(\n      \"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n      hashlittle(p, sizeof(q) - 1, 13),\n      hashlittle(p, sizeof(q) - 2, 13),\n      hashlittle(p, sizeof(q) - 3, 13),\n      hashlittle(p, sizeof(q) - 4, 13),\n      hashlittle(p, sizeof(q) - 5, 13),\n      hashlittle(p, sizeof(q) - 6, 13),\n      hashlittle(p, sizeof(q) - 7, 13),\n      hashlittle(p, sizeof(q) - 8, 13),\n      hashlittle(p, sizeof(q) - 9, 13),\n      hashlittle(p, sizeof(q) - 10, 13),\n      hashlittle(p, sizeof(q) - 11, 13),\n      hashlittle(p, sizeof(q) - 12, 13));\n  p = &qqq[2];\n  printf(\n      \"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n      hashlittle(p, sizeof(q) - 1, 13),\n      hashlittle(p, sizeof(q) - 2, 13),\n      hashlittle(p, sizeof(q) - 3, 13),\n      hashlittle(p, sizeof(q) - 4, 13),\n      hashlittle(p, sizeof(q) - 5, 13),\n      hashlittle(p, sizeof(q) - 6, 13),\n      hashlittle(p, sizeof(q) - 7, 13),\n      hashlittle(p, sizeof(q) - 8, 13),\n      hashlittle(p, sizeof(q) - 9, 13),\n      hashlittle(p, sizeof(q) - 10, 13),\n      hashlittle(p, sizeof(q) - 11, 13),\n      hashlittle(p, sizeof(q) - 12, 13));\n  p = &qqqq[3];\n  printf(\n      \"%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\\n\",\n      hashlittle(p, sizeof(q) - 1, 13),\n      hashlittle(p, sizeof(q) - 2, 13),\n      hashlittle(p, sizeof(q) - 3, 13),\n      hashlittle(p, sizeof(q) - 4, 13),\n      hashlittle(p, sizeof(q) - 5, 13),\n      hashlittle(p, sizeof(q) - 6, 13),\n      hashlittle(p, sizeof(q) - 7, 13),\n      hashlittle(p, sizeof(q) - 8, 13),\n      hashlittle(p, sizeof(q) - 9, 13),\n      hashlittle(p, sizeof(q) - 10, 13),\n      hashlittle(p, sizeof(q) - 11, 13),\n      hashlittle(p, sizeof(q) - 12, 13));\n  printf(\"\\n\");\n\n  /* check that hashlittle2 and hashlittle produce the same results */\n  i = 47;\n  j = 0;\n  hashlittle2(q, sizeof(q), &i, &j);\n  if (hashlittle(q, sizeof(q), 47) != i)\n    printf(\"hashlittle2 and hashlittle mismatch\\n\");\n\n  /* check that hashword2 and hashword produce the same results */\n  len = 0xdeadbeef;\n  i = 47, j = 0;\n  hashword2(&len, 1, &i, &j);\n  if (hashword(&len, 1, 47) != i)\n    printf(\"hashword2 and hashword mismatch %x %x\\n\", i, hashword(&len, 1, 47));\n\n  /* check hashlittle doesn't read before or after the ends of the string */\n  for (h = 0, b = buf + 1; h < 8; ++h, ++b) {\n    for (i = 0; i < MAXLEN; ++i) {\n      len = i;\n      for (j = 0; j < i; ++j)\n        *(b + j) = 0;\n\n      /* these should all be equal */\n      ref = hashlittle(b, len, (uint32_t)1);\n      *(b + i) = (uint8_t)~0;\n      *(b - 1) = (uint8_t)~0;\n      x = hashlittle(b, len, (uint32_t)1);\n      y = hashlittle(b, len, (uint32_t)1);\n      if ((ref != x) || (ref != y)) {\n        printf(\"alignment error: %.8x %.8x %.8x %d %d\\n\", ref, x, y, h, i);\n      }\n    }\n  }\n}\n\n/* check for problems with nulls */\nvoid driver4()\n{\n  uint8_t buf[1];\n  uint32_t h, i, state[HASHSTATE];\n\n  buf[0] = ~0;\n  for (i = 0; i < HASHSTATE; ++i)\n    state[i] = 1;\n  printf(\"These should all be different\\n\");\n  for (i = 0, h = 0; i < 8; ++i) {\n    h = hashlittle(buf, 0, h);\n    printf(\"%2ld  0-byte strings, hash is  %.8x\\n\", i, h);\n  }\n}\n\nvoid driver5()\n{\n  uint32_t b, c;\n  b = 0, c = 0, hashlittle2(\"\", 0, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* deadbeef deadbeef */\n  b = 0xdeadbeef, c = 0, hashlittle2(\"\", 0, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* bd5b7dde deadbeef */\n  b = 0xdeadbeef, c = 0xdeadbeef, hashlittle2(\"\", 0, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* 9c093ccd bd5b7dde */\n  b = 0, c = 0, hashlittle2(\"Four score and seven years ago\", 30, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* 17770551 ce7226e6 */\n  b = 1, c = 0, hashlittle2(\"Four score and seven years ago\", 30, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* e3607cae bd371de4 */\n  b = 0, c = 1, hashlittle2(\"Four score and seven years ago\", 30, &c, &b);\n  printf(\"hash is %.8lx %.8lx\\n\", c, b); /* cd628161 6cbea4b3 */\n  c = hashlittle(\"Four score and seven years ago\", 30, 0);\n  printf(\"hash is %.8lx\\n\", c); /* 17770551 */\n  c = hashlittle(\"Four score and seven years ago\", 30, 1);\n  printf(\"hash is %.8lx\\n\", c); /* cd628161 */\n}\n\nint main()\n{\n  driver1(); /* test that the key is hashed: used for timings */\n  driver2(); /* test that whole key is hashed thoroughly */\n  driver3(); /* test that nothing but the key is hashed */\n  driver4(); /* test hashing multiple buffers (all buffers are null) */\n  driver5(); /* test the hash against known vectors */\n  return 1;\n}\n\n#endif /* SELF_TEST */\n"
  },
  {
    "path": "util/lookup3.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n/*\n-------------------------------------------------------------------------------\nlookup3.c, by Bob Jenkins, May 2006, Public Domain.\n\nThese are functions for producing 32-bit hashes for hash table lookup.\nhashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()\nare externally useful functions.  Routines to test the hash are included\nif SELF_TEST is defined.  You can use this free for any purpose.  It's in\nthe public domain.  It has no warranty.\n\nYou probably want to use hashlittle().  hashlittle() and hashbig()\nhash byte arrays.  hashlittle() is is faster than hashbig() on\nlittle-endian machines.  Intel and AMD are little-endian machines.\nOn second thought, you probably want hashlittle2(), which is identical to\nhashlittle() except it returns two 32-bit hashes for the price of one.\nYou could implement hashbig2() if you wanted but I haven't bothered here.\n\nIf you want to find a hash of, say, exactly 7 integers, do\n  a = i1;  b = i2;  c = i3;\n  mix(a,b,c);\n  a += i4; b += i5; c += i6;\n  mix(a,b,c);\n  a += i7;\n  final(a,b,c);\nthen use c as the hash value.  If you have a variable length array of\n4-byte integers to hash, use hashword().  If you have a byte array (like\na character string), use hashlittle().  If you have several byte arrays, or\na mix of things, see the comments above hashlittle().\n\nWhy is this so big?  I read 12 bytes at a time into 3 4-byte integers,\nthen mix those integers.  This is fast (you can do a lot more thorough\nmixing with 12*3 instructions on 3 integers than you can with 3 instructions\non 1 byte), but shoehorning those bytes into integers efficiently is messy.\n-------------------------------------------------------------------------------\n*/\n\n#include <stddef.h>\n#include <stdint.h> /* defines uint32_t etc */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n/*\n--------------------------------------------------------------------\n This works on all machines.  To be useful, it requires\n -- that the key be an array of uint32_t's, and\n -- that the length be the number of uint32_t's in the key\n\n The function hashword() is identical to hashlittle() on little-endian\n machines, and identical to hashbig() on big-endian machines,\n except that the length has to be measured in uint32_ts rather than in\n bytes.  hashlittle() is more complicated than hashword() only because\n hashlittle() has to dance around fitting the key bytes into registers.\n--------------------------------------------------------------------\n*/\nuint32_t lookup3_hashword(\n    const uint32_t *k, /* the key, an array of uint32_t values */\n    size_t length,     /* the length of the key, in uint32_ts */\n    uint32_t initval); /* the previous hash, or an arbitrary value */\n\n/*\n--------------------------------------------------------------------\nhashword2() -- same as hashword(), but take two seeds and return two\n32-bit values.  pc and pb must both be nonnull, and *pc and *pb must\nboth be initialized with seeds.  If you pass in (*pb)==0, the output\n(*pc) will be the same as the return value from hashword().\n--------------------------------------------------------------------\n*/\nvoid lookup3_hashword2(\n    const uint32_t *k, /* the key, an array of uint32_t values */\n    size_t length,     /* the length of the key, in uint32_ts */\n    uint32_t *pc,      /* IN: seed OUT: primary hash value */\n    uint32_t *pb);     /* IN: more seed OUT: secondary hash value */\n\n/*\n-------------------------------------------------------------------------------\nhashlittle() -- hash a variable-length key into a 32-bit value\n  k       : the key (the unaligned variable-length array of bytes)\n  length  : the length of the key, counting by bytes\n  initval : can be any 4-byte value\nReturns a 32-bit value.  Every bit of the key affects every bit of\nthe return value.  Two keys differing by one or two bits will have\ntotally different hash values.\n\nThe best hash table sizes are powers of 2.  There is no need to do\nmod a prime (mod is sooo slow!).  If you need less than 32 bits,\nuse a bitmask.  For example, if you need only 10 bits, do\n  h = (h & hashmask(10));\nIn which case, the hash table should have hashsize(10) elements.\n\nIf you are hashing n strings (uint8_t **)k, do it like this:\n  for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);\n\nBy Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this\ncode any way you wish, private, educational, or commercial.  It's free.\n\nUse for hash table lookup, or anything where one collision in 2^^32 is\nacceptable.  Do NOT use for cryptographic purposes.\n-------------------------------------------------------------------------------\n*/\nuint32_t lookup3_hashlittle(const void *key, size_t length, uint32_t initval);\n\n/*\n * hashlittle2: return 2 32-bit hash values\n *\n * This is identical to hashlittle(), except it returns two 32-bit hash\n * values instead of just one.  This is good enough for hash table\n * lookup with 2^^64 buckets, or if you want a second hash if you're not\n * happy with the first, or if you want a probably-unique 64-bit ID for\n * the key.  *pc is better mixed than *pb, so use *pc first.  If you want\n * a 64-bit value do something like \"*pc + (((uint64_t)*pb)<<32)\".\n */\nvoid lookup3_hashlittle2(\n    const void *key, /* the key to hash */\n    size_t length,   /* length of the key */\n    uint32_t *pc,    /* IN: primary initval, OUT: primary hash */\n    uint32_t *pb);   /* IN: secondary initval, OUT: secondary hash */\n\n/*\n * hashbig():\n * This is the same as hashword() on big-endian machines.  It is different\n * from hashlittle() on all machines.  hashbig() takes advantage of\n * big-endian byte ordering.\n */\nuint32_t lookup3_hashbig(const void *key, size_t length, uint32_t initval);\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n"
  },
  {
    "path": "util/lookup3_hasher.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <functional>\n#include <string>\n#include <type_traits>\n\n#include \"lookup3.h\"\n#include \"platform/platform.h\"\n\n// Hasher functor that is compatible with std::hash, but uses lookup3 hashing\n// function instead.\n\nnamespace util {\n\ntemplate <class T, class T2 = void> struct Lookup3Hasher {\n  // Default implementation, fall-back to std::hash\n  std::size_t operator()(T const &t) const noexcept { return std::hash<T>{}(t); }\n};\n\n// Integer cases\n// TODO: use \"if constexpr\" to simplify the code once switch to C++17.\ntemplate <class T> struct Lookup3Hasher<T, typename std::enable_if<std::is_integral<T>::value>::type> {\n  std::size_t operator()(T const &t) const noexcept\n  {\n    u64 val = static_cast<u64>(t);\n    u32 pc = 0;\n    u32 pb = 0;\n    lookup3_hashword2((const uint32_t *)(&val), 2, &pc, &pb);\n\n    return (static_cast<std::size_t>(pb) << 32) | static_cast<std::size_t>(pc);\n  }\n};\n\ntemplate <> struct Lookup3Hasher<std::string> {\n  std::size_t operator()(std::string const &t) const noexcept\n  {\n    u32 pc = 0;\n    u32 pb = 0;\n    lookup3_hashlittle2((const void *)(t.c_str()), t.size(), &pc, &pb);\n\n    return (static_cast<std::size_t>(pb) << 32) | static_cast<std::size_t>(pc);\n  }\n};\n\n} // namespace util\n"
  },
  {
    "path": "util/lookup3_hasher_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <string>\n#include <unordered_map>\n\n#include \"gtest/gtest.h\"\n\n#include \"util/lookup3_hasher.h\"\n\nnamespace util {\nnamespace {\n\nTEST(Lookup3HasherTest, Integer)\n{\n  Lookup3Hasher<u64> h;\n  std::hash<u64> s;\n\n  EXPECT_NE(h(0), 0u);\n  EXPECT_NE(h(1), 0u);\n\n  EXPECT_NE(h(123), s(123));\n\n  Lookup3Hasher<u32> h32;\n  EXPECT_EQ(h32(20), h(20));\n}\n\nTEST(Lookup3HasherTest, StringHash)\n{\n  Lookup3Hasher<std::string> h;\n  std::hash<std::string> s;\n\n  EXPECT_NE(h(\"\"), 0u);\n  EXPECT_NE(h(\"hello\"), s(\"hello\"));\n}\n\nTEST(Lookup3HasherTest, UseInMap)\n{\n  std::unordered_map<int, int, ::util::Lookup3Hasher<int>> m;\n  m[10] = 20;\n  m[20] = 30;\n\n  EXPECT_EQ(m[10], 20);\n  EXPECT_EQ(m[20], 30);\n}\n\n} // namespace\n} // namespace util\n"
  },
  {
    "path": "util/lz4_decompressor.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"lz4_decompressor.h\"\n\n#include <cassert>\n#include <cstring>\n#include <stdexcept>\n\nLz4Decompressor::Lz4Decompressor(size_t capacity) : output_buf_capacity_(capacity), tail_loc_(0)\n{\n  output_buf_ = (u8 *)malloc(capacity * sizeof(u8));\n  if (output_buf_ == NULL) {\n    throw std::runtime_error(\"Lz4Decompressor: failed to allocate memory.\");\n  }\n\n  LZ4F_errorCode_t r = LZ4F_createDecompressionContext(&ctx_, LZ4F_VERSION);\n  if (LZ4F_isError(r)) {\n    throw std::runtime_error(\"Lz4Decompressor: failed to create a CTX object.\");\n  }\n}\n\nLz4Decompressor::~Lz4Decompressor()\n{\n  free(output_buf_);\n  LZ4F_freeDecompressionContext(ctx_);\n}\n\nsize_t Lz4Decompressor::process(const u8 *data, size_t data_len, size_t *consumed_len)\n{\n  size_t res = 0;\n  size_t src_size = 0;\n  *consumed_len = 0;\n\n  do {\n    src_size = data_len;\n    size_t dst_size = output_buf_capacity_ - tail_loc_;\n\n    res = LZ4F_decompress(ctx_, (void *)(output_buf_ + tail_loc_), &dst_size, (void *)data, &src_size, NULL);\n\n    *consumed_len += src_size;\n    tail_loc_ += dst_size;\n    data += src_size;\n    data_len -= src_size;\n\n    // continue to decompress while LZ4 is making progress\n  } while ((!LZ4F_isError(res)) && (src_size > 0));\n\n  return LZ4F_isError(res) ? res : 0;\n}\n\nconst u8 *Lz4Decompressor::output_buf() const\n{\n  return output_buf_;\n}\n\nsize_t Lz4Decompressor::output_buf_size() const\n{\n  return tail_loc_;\n}\n\nvoid Lz4Decompressor::discard(size_t len)\n{\n  assert(tail_loc_ >= len);\n\n  if (tail_loc_ == len) {\n    tail_loc_ = 0;\n    return;\n  }\n\n  memmove((void *)output_buf_, (const void *)(output_buf_ + len), tail_loc_ - len);\n  tail_loc_ -= len;\n}\n"
  },
  {
    "path": "util/lz4_decompressor.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n\n#include <lz4frame.h>\n\n#include <cstdint>\n#include <tuple>\n\nclass Lz4Decompressor {\npublic:\n  explicit Lz4Decompressor(size_t buf_capacity);\n  ~Lz4Decompressor();\n\n  const u8 *output_buf() const;\n  size_t output_buf_size() const;\n\n  // Decompresses |data| of size |data_len|.\n  // The # of bytes that have been decompressed is stored in |consumed_len|.\n  //\n  // Returns LZ4 error, if an error happened, 0 otherwise.\n  size_t process(const u8 *data, size_t data_len, size_t *consumed_len);\n\n  // Discards |len| bytes of data in output_buf.\n  void discard(size_t len);\n\nprivate:\n  const size_t output_buf_capacity_;\n  size_t tail_loc_;\n\n  LZ4F_dctx *ctx_;\n  u8 *output_buf_;\n};\n"
  },
  {
    "path": "util/meta.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <cstdint>\n\nnamespace meta {\n\n/**\n * A type tag that can be cheaply instantiated and passed by value.\n *\n * It allows passing type information by value when it is unknown if the type\n * itself is cheaply constructed or passed by value.\n *\n * This is a helper for meta-function implementations like `meta::foreach`.\n *\n * For a real-world example of usage, check `meta::foreach`.\n */\ntemplate <typename T> struct tag {\n  using type = T;\n};\n\n/**\n * A helper to retrieve the type of a type tag.\n *\n * This greatly helps combining lambdas with meta-functions.\n *\n * For a real-world example of usage, check `meta::foreach`.\n */\ntemplate <typename T> typename T::type tag_type(T);\n\n/**\n * A minimalistic type-list used to represent a list of types at compile time.\n */\ntemplate <typename... T> struct list {\n  /**\n   * The size of the list.\n   */\n  static constexpr std::size_t size = sizeof...(T);\n};\n\n} // namespace meta\n\n#include <util/meta.inl>\n\nnamespace meta {\n\n/**\n * Iterates over the elements of a type list, calling the given function for each one.\n *\n * The given function must have the following signature:\n *\n *  template <typename T>\n *  void fn(meta::tag<T>, Args &...args);\n *\n * All arguments given to foreach will be passed to the given function on\n * every call so beware of double-moves.\n *\n * Example:\n *\n *  using list = meta::list<std::string, int, double>;\n *\n *  meta::foreach<list>([](auto tag) {\n *    using type = decltype(meta::tag_type(tag));\n *    std::cout << \"enter a value for a \" << typeid(type).name() << \": \";\n *    type value;\n *    std::cin >> value;\n *    std::cout << \"\\nvalue given: \" << value << std::endl;\n *  });\n */\ntemplate <typename List, typename Fn, typename... Args> void foreach (Fn &&fn, Args && ... args);\n\n} // namespace meta\n"
  },
  {
    "path": "util/meta.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\nnamespace meta {\n\n// implementation for meta::foreach\n\ntemplate <template <typename...> typename List, typename... T, typename Fn, typename... Args>\nstd::size_t foreach_impl(meta::tag<List<T...>>, Fn &fn, Args &... args)\n{\n  // uses the fold expression for operator+ to expand the elements of the type\n  // list and guarantee that `fn` will be called in the correct order for each\n  // element of the type list\n  return ((fn(meta::tag<T>{}, args...), true) && ... && true);\n}\n\ntemplate <typename List, typename Fn, typename... Args> void foreach (Fn &&fn, Args && ... args)\n{\n  meta::foreach_impl(tag<List>{}, fn, args...);\n}\n\n} // namespace meta\n"
  },
  {
    "path": "util/meta_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/meta.h>\n\n#include <gtest/gtest.h>\n\n#include <generated/ebpf_net/ingest/meta.h>\n\n#include <jitbuf/jb.h>\n#include <spdlog/fmt/fmt.h>\n\n#include <type_traits>\n\n#include <cstring>\n\nnamespace ebpf_net::ingest {\n\nTEST(metadata, message_metadata_rpc_id)\n{\n  EXPECT_EQ(301, pid_info_message_metadata::rpc_id);\n  EXPECT_EQ(396, container_metadata_message_metadata::rpc_id);\n}\n\nTEST(metadata, field_type)\n{\n  EXPECT_TRUE((std::is_same_v<std::uint32_t, pid_info_message_metadata::field_pid::type>));\n  EXPECT_TRUE((std::is_same_v<std::uint8_t[16], pid_info_message_metadata::field_comm::type>));\n}\n\nTEST(metadata, field_name)\n{\n  EXPECT_EQ(\"pid\", pid_info_message_metadata::field_pid::name);\n  EXPECT_EQ(\"comm\", pid_info_message_metadata::field_comm::name);\n}\n\nTEST(metadata, field_get)\n{\n  jsrv_ingest__pid_info message;\n  message.pid = 12345;\n  std::strncpy(reinterpret_cast<char *>(message.comm), \"abcdef123456789\", 16);\n\n  EXPECT_EQ(12345u, pid_info_message_metadata::field_pid::get(&message));\n  EXPECT_EQ(\n      0,\n      std::strncmp(\n          \"abcdef123456789\", reinterpret_cast<char const *>(pid_info_message_metadata::field_comm::get(&message)), 16));\n}\n\nTEST(metadata, message_metadata_for)\n{\n  EXPECT_TRUE((std::is_same_v<pid_info_message_metadata, ingest_metadata::message_metadata_for<jb_ingest__pid_info>>));\n  EXPECT_TRUE((std::is_same_v<pid_info_message_metadata, ingest_metadata::message_metadata_for<jsrv_ingest__pid_info>>));\n  EXPECT_TRUE((std::is_same_v<\n               container_metadata_message_metadata,\n               ingest_metadata::message_metadata_for<jb_ingest__container_metadata>>));\n  EXPECT_TRUE((std::is_same_v<\n               container_metadata_message_metadata,\n               ingest_metadata::message_metadata_for<jsrv_ingest__container_metadata>>));\n}\n\n} // namespace ebpf_net::ingest\n\ntemplate <typename... T> void test_foreach()\n{\n  using list = meta::list<T...>;\n\n  std::vector<std::string> const expected{std::initializer_list<std::string>{typeid(T).name()...}};\n\n  std::vector<std::string> actual;\n  meta::foreach<list>([&](auto tag) {\n    using type = decltype(meta::tag_type(tag));\n    actual.emplace_back(typeid(type).name());\n  });\n\n  EXPECT_EQ(sizeof...(T), actual.size());\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(meta, foreach)\n{\n  test_foreach<>();\n  test_foreach<int>();\n  test_foreach<std::string, int>();\n  test_foreach<std::string, int, double>();\n}\n"
  },
  {
    "path": "util/metric_store.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/fast_div.h>\n#include <util/histogram.h>\n#include <util/lazy_array.h>\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n\ntemplate <class Metric, std::size_t SIZE, std::size_t N_EPOCHS, class Allocator = std::allocator<Metric>> class MetricStore {\npublic:\n  using metric_type = Metric;\n  static constexpr std::size_t size = SIZE;\n  static constexpr std::size_t n_epochs = N_EPOCHS;\n  using index_type = typename std::conditional<(SIZE >= (1 << 16) - 2), u32, u16>::type;\n  static constexpr index_type invalid = std::numeric_limits<index_type>::max();\n  static constexpr index_type list_end = std::numeric_limits<index_type>::max() - 1;\n  using epoch_type = u32;\n\n  class queue_type {\n  public:\n    bool empty() { return head_ == list_end; }\n\n    index_type peek()\n    {\n      assert(!empty());\n      return head_;\n    }\n\n    void pop()\n    {\n      assert(!empty());\n\n      /* save the dequeued entry */\n      index_type dequeued = head_;\n      /* find dequeued->next */\n      index_type next = store_->arr_[dequeued].next[queue_index_];\n      /* mark the dequeued entry as unqueued */\n      store_->arr_[dequeued].next[queue_index_] = invalid;\n      /* pop: set the head to the next entry */\n      head_ = next;\n    }\n\n  private:\n    friend class MetricStore;\n    MetricStore *store_;\n    std::size_t queue_index_;\n    index_type head_;\n  };\n\n  MetricStore(const fast_div &t_to_timeslot) : t_to_timeslot_(t_to_timeslot)\n  {\n    static_assert((((N_EPOCHS) & ((N_EPOCHS)-1)) == 0), \"N_EPOCHS must be a power of 2\");\n\n    for (u32 i = 0; i < n_epochs; i++) {\n      auto &queue = queue_[i];\n      queue.store_ = this;\n      queue.queue_index_ = i;\n      queue.head_ = list_end;\n    }\n\n    slot_duration_ = t_to_timeslot_.estimated_reciprocal();\n  }\n\n  /**\n   * Looks up the statistics entry for the given index at time t, and enqueues\n   *   the change if enqueue==true.\n   *\n   * @returns: a pair. first is true if metric was queued before the lookup.\n   */\n  std::pair<bool, metric_type &> lookup(u32 index, u64 t, bool enqueue)\n  {\n    epoch_type bin = histogram_bin(n_epochs, relative_timeslot(t));\n    return lookup_relative(index, bin, enqueue);\n  }\n\n  /**\n   * Looks up the statistics entry for socket @index at @bin slot ahead of\n   *   the current queue. Enqueues the change if enqueue==true\n   *\n   * @assumes timeslot < N_EPOCHS.\n   */\n  std::pair<bool, metric_type &> lookup_relative(u32 index, epoch_type bin, bool enqueue)\n  {\n    assert(index < size);\n    assert(bin < n_epochs);\n\n    epoch_type epoch = (bin + current_queue_) & (n_epochs - 1);\n    element_type &elem = arr_[index];\n    bool was_queued = (elem.next[epoch] != invalid);\n    if (enqueue && !was_queued) {\n      elem.next[epoch] = queue_[epoch].head_;\n      queue_[epoch].head_ = index;\n    }\n    return {was_queued, elem.m[epoch]};\n  }\n\n  /**\n   * Gets the current slot's queue\n   */\n  queue_type &current_queue() { return queue_[current_queue_]; }\n\n  /**\n   * Advances the window of stat collection by one timeslot\n   */\n  void advance()\n  {\n    current_queue_ = (current_queue_ + 1) & (n_epochs - 1);\n\n    if (current_timeslot_) {\n      (*current_timeslot_)++;\n    }\n  }\n\n  /**\n   * returns the timeslot of @t relative to the current timeslot\n   */\n  s16 relative_timeslot(u64 t)\n  {\n    u16 timeslot = t / t_to_timeslot_;\n\n    if (!current_timeslot_) {\n      current_timeslot_ = timeslot;\n    }\n\n    return (s16)timeslot - *current_timeslot_;\n  }\n\n  /**\n   * returns the number of time units that one slot takes up\n   */\n  double slot_duration() const { return slot_duration_; }\n\nprivate:\n  friend class queue_type;\n\n  struct element_type {\n    element_type()\n    {\n      for (u32 i = 0; i < n_epochs; i++)\n        next[i] = invalid;\n    }\n    std::array<metric_type, n_epochs> m;\n    std::array<index_type, n_epochs> next;\n  };\n\n  using element_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<element_type>;\n\n  /* array of statistics */\n  LazyArray<element_type, size, element_allocator_type> arr_;\n\n  /* circular queues for changed entries */\n  std::array<queue_type, n_epochs> queue_;\n\n  /* converting t to timeslot */\n  fast_div t_to_timeslot_;\n\n  /* Timeslot assigned to the current queue, or nullopt if time-to-epoch\n   * relationship is not yet established. Used to calculate the relative\n   * timeslot (relative_timeslot() function).\n   */\n  std::optional<u16> current_timeslot_;\n\n  /* index of the current queue */\n  epoch_type current_queue_{0};\n\n  /* slot duration, in time units */\n  double slot_duration_;\n};\n"
  },
  {
    "path": "util/nomad_metadata.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/nomad_metadata.h>\n\n#include <collector/agent_log.h>\n#include <util/environment_variables.h>\n#include <util/json.h>\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <absl/strings/match.h>\n\nconst std::string NOMAD_NAMESPACE_VAR = \"NOMAD_NAMESPACE\";\nconst std::string NOMAD_GROUP_NAME_VAR = \"NOMAD_GROUP_NAME\";\nconst std::string NOMAD_TASK_NAME_VAR = \"NOMAD_TASK_NAME\";\nconst std::string NOMAD_JOB_NAME_VAR = \"NOMAD_JOB_NAME\";\n\nconst std::string NOMAD_NAMESPACE_VAR_PREFIX = \"NOMAD_NAMESPACE=\";\nconst std::string NOMAD_GROUP_NAME_VAR_PREFIX = \"NOMAD_GROUP_NAME=\";\nconst std::string NOMAD_TASK_NAME_VAR_PREFIX = \"NOMAD_TASK_NAME=\";\nconst std::string NOMAD_JOB_NAME_VAR_PREFIX = \"NOMAD_JOB_NAME=\";\n\nNomadMetadata::NomadMetadata()\n    : ns_(try_get_env_var(NOMAD_NAMESPACE_VAR.c_str())),\n      group_name_(try_get_env_var(NOMAD_GROUP_NAME_VAR.c_str())),\n      task_name_(try_get_env_var(NOMAD_TASK_NAME_VAR.c_str())),\n      job_name_(try_get_env_var(NOMAD_JOB_NAME_VAR.c_str()))\n{}\n\nNomadMetadata::NomadMetadata(nlohmann::json const &environment)\n{\n  const std::string environment_dump = environment.dump();\n  LOG::trace_in(AgentLogKind::NOMAD, \"Container environment: {}\", environment_dump);\n\n  for (auto const &variable : environment) {\n    if (auto string = try_get_string(variable)) {\n      if (absl::StartsWith(*string, NOMAD_NAMESPACE_VAR_PREFIX)) {\n        ns_ = string->substr(NOMAD_NAMESPACE_VAR_PREFIX.size());\n      } else if (absl::StartsWith(*string, NOMAD_GROUP_NAME_VAR_PREFIX)) {\n        group_name_ = string->substr(NOMAD_GROUP_NAME_VAR_PREFIX.size());\n      } else if (absl::StartsWith(*string, NOMAD_TASK_NAME_VAR_PREFIX)) {\n        task_name_ = string->substr(NOMAD_TASK_NAME_VAR_PREFIX.size());\n      } else if (absl::StartsWith(*string, NOMAD_JOB_NAME_VAR_PREFIX)) {\n        job_name_ = string->substr(NOMAD_JOB_NAME_VAR_PREFIX.size());\n      }\n    }\n  }\n\n  LOG::trace_in(\n      AgentLogKind::NOMAD, \"Nomad metadata: ns='{}' group='{}' job='{}' task='{}'\", ns_, group_name_, job_name_, task_name_);\n}\n\nvoid NomadMetadata::print() const\n{\n  LOG::debug(\"Nomad metadata:\");\n  LOG::debug(\"- namespace: {}\", ns_);\n  LOG::debug(\"- group name: {}\", group_name_);\n  LOG::debug(\"- task name: {}\", task_name_);\n  LOG::debug(\"- job name: {}\", job_name_);\n}\n\nNomadMetadata::operator bool() const\n{\n  return !ns_.empty() || !group_name_.empty() || !task_name_.empty() || !job_name_.empty();\n}\n"
  },
  {
    "path": "util/nomad_metadata.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <nlohmann/json.hpp>\n\n#include <string>\n#include <string_view>\n\n/**\n * Reads metadata posted by Nomad (https://www.nomadproject.io/).\n *\n * NOTE: this function reads environment variables so it's advisable to call it\n * before any thread is created, given that reading/writing to environment\n * variables is not thread safe.\n */\nclass NomadMetadata {\npublic:\n  NomadMetadata();\n  NomadMetadata(nlohmann::json const &environment);\n\n  NomadMetadata(NomadMetadata &&) = default;\n\n  std::string_view ns() const { return ns_; }\n  std::string_view group_name() const { return group_name_; }\n  std::string_view task_name() const { return task_name_; }\n  std::string_view job_name() const { return job_name_; }\n\n  void print() const;\n\n  explicit operator bool() const;\n\nprivate:\n  std::string ns_;\n  std::string group_name_;\n  std::string task_name_;\n  std::string job_name_;\n};\n"
  },
  {
    "path": "util/overloaded_visitor.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// This is a helper type for type-matching visitors for std::variants.\n// See https://en.cppreference.com/w/cpp/utility/variant/visit\n\ntemplate <class... Ts> struct overloaded_visitor : Ts... {\n  using Ts::operator()...;\n};\n// explicit deduction guide (not needed as of C++20)\ntemplate <class... Ts> overloaded_visitor(Ts...) -> overloaded_visitor<Ts...>;\n"
  },
  {
    "path": "util/parser_utils.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n#pragma once\n\n#include <cassert>\n\n// This file contains a handful of utilities for parsing a collection of chars.\n// ITERATOR cur is the current position in the collection, while ITERATOR end \n// is the end of the collection.\n//\n// ITERATOR cur needs to be at least a forward iterator.\n// peek() and lookahead() do not modify cur, but the consume(), parse_match() \n// and parse_token() functions do.\n//\n// FUTURE - would it be worth templating the all the 'char' to \n// std::iterator_traits<ITERATOR>::value_type ?\nnamespace parsing {\n\n// get the next character into c, but don't consume it\ntemplate <typename ITERATOR> \ninline bool peek(ITERATOR cur, ITERATOR end, char *c) {\n  if (cur == end) { \n    return false;\n  }\n\n  *c = *cur;\n\n  return true;\n}\n\n// see if the next character matches c\ntemplate <typename ITERATOR> \ninline bool lookahead(ITERATOR cur, ITERATOR end, char c) {\n  char f;\n  if (!peek(cur, end, &f)) return false;\n\n  return f == c;\n}\n\n// see if the next character matches the predicate\ntemplate <typename ITERATOR> \ninline bool lookahead(ITERATOR cur, ITERATOR end, bool (*pred)(char))\n{\n  char f;\n  if (!peek(cur, end, &f)) return false;\n\n  return pred(f);\n}\n\n// look at the next characters to see if they match string s\ntemplate <typename ITERATOR> \ninline bool lookahead(ITERATOR cur, ITERATOR end, std::string_view s)\n{\n  auto dist = std::distance(cur, end);\n  assert(dist >= 0);\n  if ((size_t)dist < s.size()) return false;\n\n  ITERATOR t = cur;\n  for (size_t ii = 0; ii < s.size(); ++ii, ++t)\n  {\n    if (*t != s[ii]) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n// eat the next n characters (default 1)\ntemplate <typename ITERATOR> \ninline void consume(ITERATOR &cur, ITERATOR end, size_t n = 1)\n{\n  for (size_t ii = 0; ii < n && cur != end; ++ii)\n  {\n    ++cur;\n  }\n}\n\n// compare the front character to c and consume if matches,\n// optionally storing it in s\ntemplate <typename ITERATOR>\ninline bool parse_match(ITERATOR &cur, ITERATOR end, char c, std::string *s = nullptr)\n{\n  if (lookahead(cur, end, c))\n  {\n    consume(cur, end);\n    if (s != nullptr)\n    {\n      (*s) += c;\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\n// check, leave untouched if failed, otherwise consume and optionally store\n// at out\ntemplate <typename ITERATOR>\ninline bool parse_match(ITERATOR &cur, ITERATOR end, std::string_view s, std::string *out = nullptr)\n{\n  if (lookahead(cur, end, s))\n  {\n    consume(cur, end, s.size());\n    if (out != nullptr)\n    {\n      (*out) += s;\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\n// compare the front character to a predicate and consume if matches,\n// optionally storing it in s\ntemplate <typename ITERATOR>\ninline bool parse_match(ITERATOR &cur, ITERATOR end, bool (*pred)(char), std::string *s = nullptr)\n{\n  if (lookahead(cur, end, pred))\n  {\n    if (s != nullptr)\n    {\n      (*s) += *cur;\n    }\n    consume(cur, end);\n    return true;\n  }\n\n  return false;\n}\n\n// consume until token is reached, optionally storing consumed in s.\n// NB: it does consume the token, but does NOT store it in s.\ntemplate <typename ITERATOR>\ninline bool parse_token(ITERATOR &cur, ITERATOR end, char token, std::string *s = nullptr) {\n  char p;\n  while (peek(cur, end, &p)) {\n    consume(cur, end);\n    if (p == token) {\n      return true;\n    }\n\n    if (s != nullptr) {\n      (*s) += p;\n    }\n  }\n\n  return false;\n}\n\n} // namespace parsing\n"
  },
  {
    "path": "util/perf_ring.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <util/perf_ring.h>\n\nint pr_init_contig(struct perf_ring *eq, void *data, u32 n_pages, u64 page_size)\n{\n  __u64 buf_len;\n\n  if ((eq == NULL) || (data == NULL))\n    return -EINVAL;\n\n  eq->shared = (struct perf_event_mmap_page *)data;\n  eq->data = (char *)data + page_size;\n\n  buf_len = n_pages * page_size;\n  if ((buf_len & (buf_len - 1)) || (buf_len < 8))\n    return -EINVAL;\n\n  eq->buf_mask = buf_len - 1;\n\n  eq->buf_head = eq->shared->data_tail;\n  eq->buf_tail = eq->shared->data_head;\n\n  return 0;\n}\n\nint pr_write(struct perf_ring *eq, u16 len, u32 type)\n{\n  u32 buf_tail;\n  u32 buf_mask;\n  u32 aligned_len = ((u32)len + sizeof(struct perf_event_header) + 7) & ~7;\n  struct perf_event_header *header;\n\n  /* check input parameters */\n  if (eq == NULL)\n    return -EINVAL;\n  if (aligned_len > eq->buf_mask)\n    return -EINVAL;\n  if (aligned_len >= (1 << 16))\n    return -EINVAL;\n\n  buf_mask = eq->buf_mask;\n  buf_tail = eq->buf_tail;\n\n  /* is there enough space in the eq? */\n  if (buf_tail + aligned_len - eq->buf_head > buf_mask + 1)\n    return -ENOSPC;\n\n  /* okay we're good to go */\n  header = (struct perf_event_header *)&eq->data[buf_tail & buf_mask];\n  header->size = aligned_len;\n  header->type = type;\n  eq->buf_tail = buf_tail + aligned_len;\n  return ((buf_tail + sizeof(struct perf_event_header)) & buf_mask);\n}\n\nint pr_peek_size(const struct perf_ring *eq)\n{\n  struct perf_event_header *header;\n\n  /* is the queue empty? */\n  if (eq->buf_tail == eq->buf_head)\n    return -ENOENT;\n\n  /* sanity check: there should be space for the header */\n  assert((eq->buf_head & 7) == 0);\n\n  header = (struct perf_event_header *)&eq->data[eq->buf_head & eq->buf_mask];\n  return header->size - sizeof(struct perf_event_header);\n}\n\nu32 pr_peek_type(const struct perf_ring *eq)\n{\n  const struct perf_event_header *header;\n\n  assert(eq->buf_tail != eq->buf_head);\n\n  header = (const struct perf_event_header *)&eq->data[eq->buf_head & eq->buf_mask];\n  return header->type;\n}\n\nu64 pr_peek_aligned_u64(const struct perf_ring *eq, u16 offset)\n{\n  u32 total_offset;\n\n  assert((int)eq->buf_tail - (eq->buf_head + offset + 8) >= 0);\n  assert(((eq->buf_head + offset) & 7) == 0);\n\n  total_offset = eq->buf_head + sizeof(struct perf_event_header) + offset;\n\n  return *(u64 *)&eq->data[total_offset & eq->buf_mask];\n}\n\nu32 pr_peek_aligned_u32(const struct perf_ring *eq, u16 offset)\n{\n  u32 total_offset;\n\n  assert((int)eq->buf_tail - (eq->buf_head + offset + 4) >= 0);\n  assert(((eq->buf_head + offset) & 3) == 0);\n\n  total_offset = eq->buf_head + sizeof(struct perf_event_header) + offset;\n\n  return *(u32 *)&eq->data[total_offset & eq->buf_mask];\n}\n\nu16 pr_peek_aligned_u16(const struct perf_ring *eq, u16 offset)\n{\n  u32 total_offset;\n\n  assert((int)eq->buf_tail - (eq->buf_head + offset + 2) >= 0);\n  assert(((eq->buf_head + offset) & 1) == 0);\n\n  total_offset = eq->buf_head + sizeof(struct perf_event_header) + offset;\n\n  return *(u16 *)&eq->data[total_offset & eq->buf_mask];\n}\n\nvoid pr_peek_copy(const struct perf_ring *eq, char *buf, u16 offset, u16 len)\n{\n  u32 begin_head;\n  u32 begin;\n  u32 end;\n  u32 buf_mask = eq->buf_mask;\n\n  if (len == 0)\n    return;\n\n  assert(pr_peek_size(eq) >= (int)offset + len);\n\n  begin_head = eq->buf_head + sizeof(struct perf_event_header) + offset;\n  begin = begin_head & buf_mask;\n  end = (begin_head + len - 1) & buf_mask;\n\n  if (end < begin) {\n    /* wraps around */\n    int len_to_ring_end = buf_mask + 1 - begin;\n    memcpy(buf, &eq->data[begin], len_to_ring_end);\n    memcpy(buf + len_to_ring_end, &eq->data[0], end + 1);\n  } else {\n    /* simple case */\n    memcpy(buf, &eq->data[begin], len);\n  }\n}\n\nstruct pr_data_view pr_peek(const struct perf_ring *eq)\n{\n  struct pr_data_view result = {0};\n\n  const u32 unpadded_len = pr_peek_aligned_u32(eq, sizeof(u32));\n  const u32 offset = sizeof(u64);\n\n  const u32 begin_head = eq->buf_head + sizeof(struct perf_event_header) + offset;\n  const u32 begin = begin_head & eq->buf_mask;\n  const u32 end = (begin_head + unpadded_len - 1) & eq->buf_mask;\n\n  if (unpadded_len == 0) {\n    return result;\n  }\n\n  assert(pr_peek_size(eq) >= (int)offset + unpadded_len);\n\n  if (end < begin) {\n    /* wraps around */\n    int len_to_ring_end = eq->buf_mask + 1 - begin;\n    result.first = &eq->data[begin];\n    result.first_len = len_to_ring_end;\n    result.second = &eq->data[0];\n    result.second_len = end + 1;\n  } else {\n    /* simple case */\n    result.first = &eq->data[begin];\n    result.first_len = unpadded_len;\n  }\n\n  return result;\n}\n\nint pr_read(struct perf_ring *eq, u16 *lenp)\n{\n  struct perf_event_header *header;\n  u32 buf_head;\n  u32 buf_mask;\n\n  /* check input parameters */\n  if ((eq == NULL) || (lenp == NULL))\n    return -EINVAL;\n\n  /* is the queue empty? */\n  if (eq->buf_tail == eq->buf_head)\n    return -EAGAIN;\n\n  /* sanity check: there should be space for the header */\n  assert((eq->buf_head & 7) == 0);\n\n  buf_mask = eq->buf_mask;\n  buf_head = eq->buf_head;\n\n  header = (struct perf_event_header *)&eq->data[buf_head & buf_mask];\n\n  *lenp = header->size - sizeof(struct perf_event_header);\n  eq->buf_head += header->size;\n\n  return ((buf_head + sizeof(struct perf_event_header)) & buf_mask);\n}\n\nu32 pr_bytes_remaining(const struct perf_ring *eq, u32 *total_size)\n{\n  /* check input parameters */\n  if (eq == NULL)\n    return -EINVAL;\n\n  u32 buf_size = eq->buf_mask + 1;\n  if (total_size) {\n    *total_size = buf_size;\n  }\n  u32 begin = eq->buf_head & eq->buf_mask;\n  u32 end = eq->buf_tail & eq->buf_mask;\n\n  if (end < begin) {\n    return (buf_size - begin) + end;\n  }\n\n  return (end - begin);\n}\n"
  },
  {
    "path": "util/perf_ring.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_PERF_RING_H_\n#define INCLUDE_FASTPASS_UTIL_PERF_RING_H_\n\n#include <linux/perf_event.h>\n#include <platform/platform.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n/**\n * A queue structure conveying discrete elements of variable length.\n *\n * Constraints:\n *  - all readers must share the same struct\n *  - all writers must share the same struct\n *  - readers can use a separate struct from writers\n *  - all reads must be surrounded by start_read_batch and finish_read_batch\n *  - all writes must be surrounded by start_write_batch and finish_read_batch\n *\n * @param buf_head: index of the next unread char in the eq\n * @param buf_tail: index following the eq's last written char\n *\n * Format of events in the ring:\n *  BEGIN perf_event_header\n *  - u32: type\n *  - u16: misc\n *  - u16: perf event length (64 bits aligned)\n *  END perf_event_header\n *  BEGIN HEADER\n *  - u32: aligned length (64 bits aligned)\n *  - u32: unpadded length (<= aligned length - sizeof(HEADER))\n *  END HEADER\n *  BEGIN PERF EVENT CONTENTS\n *  - u8[aligned length - sizeof(HEADER)]: opaque perf event payload\n *  END PERF EVENT CONTENTS\n */\nstruct perf_ring {\n  u32 buf_mask;\n\n  u32 buf_head;\n  u32 buf_tail;\n\n  struct perf_event_mmap_page *shared;\n  char *data;\n};\n\n/**\n * Initializes the perf ring using a contiguous memory area\n *\n * @param eq: the element_queue to initialize\n * @param data: shared memory area\n * @param n_pages: number of data pages in the perf ring\n * @param page_size: size of a page\n *\n * Memory layout of eq data:\n *   element_queue_shared\n *   u32 elem[n_elems]\n *   char buf[buf_len]\n *\n * @returns 0 on success,\n *   -EINVAL on NULL pointers, non-power-of 2 sizes.\n */\nint pr_init_contig(struct perf_ring *eq, void *data, u32 n_pages, u64 page_size);\n\n/**\n * Returns the size, in bytes, of a contiguous element queue\n */\nu32 pr_contig_size(u32 n_elems, u32 buf_len);\n\n/**\n * Starts a write batch\n * @param eq: the eq to write to\n *\n * @important: the user must call finish_write after finishing the batch. The\n *   consumer can only see results after a batch is finished\n */\nstatic inline void pr_start_write_batch(struct perf_ring *eq)\n{\n  eq->buf_head = ACCESS_ONCE(eq->shared->data_tail);\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n}\n\n/**\n * Get a buffer where data can be written to the eq\n * @param eq: the eq to write to\n * @param len: number of bytes we want to write\n *\n * @return: offset in eq where data should be written\n *   -EINVAL if trying to write more than the eq size or passed NULL pointer\n *   -ENOSPC if eq too full\n */\nint pr_write(struct perf_ring *eq, u16 len, u32 type);\n\n/**\n * Finishes a batch write to the element_queue\n * @param eq: the element_queue written to\n *\n * @assumes a successful start_write_batch\n */\nstatic inline void pr_finish_write_batch(struct perf_ring *eq)\n{\n  /* make sure items have been committed before writing the tails */\n  smp_wmb();\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n\n  eq->shared->data_head = eq->buf_tail;\n}\n\n/**\n * Starts a read batch\n * @param eq: the element_queue to read from\n *\n * @important: after reading the data, the user must call finish_read_batch\n */\nstatic inline void pr_start_read_batch(struct perf_ring *eq)\n{\n  /* we don't need to read buf_tail because we trust that the sizes in the\n   * element-size array do not overflow the element_queue */\n\n  eq->buf_tail = ACCESS_ONCE(eq->shared->data_head);\n  smp_rmb();\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n}\n\n/**\n * Reads the next element's size, or -ENOENT if no element exists\n */\nint pr_peek_size(const struct perf_ring *eq);\n\n/**\n * Reads the next element's type.\n *\n * Assumes element exists\n */\nu32 pr_peek_type(const struct perf_ring *eq);\n\n/**\n * Reads an aligned u64 from the variable at given offset\n *\n * Assumes element exists\n */\nu64 pr_peek_aligned_u64(const struct perf_ring *eq, u16 offset);\n\n/**\n * Reads an aligned u32 from the variable at given offset\n *\n * Assumes element exists\n */\nu32 pr_peek_aligned_u32(const struct perf_ring *eq, u16 offset);\n\n/**\n * Reads an aligned u16 from the variable at given offset\n *\n * Assumes element exists\n */\nu16 pr_peek_aligned_u16(const struct perf_ring *eq, u16 offset);\n\n/**\n * Copies @len bytes from the ring's head at given @offset to @buf\n */\nvoid pr_peek_copy(const struct perf_ring *eq, char *buf, u16 offset, u16 len);\n\n/**\n * This struct represents a view of a logically contiguous chunk of data in the\n * perf ring.\n *\n * The perf ring is implemented as a ring buffer, so if events are located at\n * the end of the ring and wrap-around to the beginning, then the view will be\n * spread over two contiguous chunks, in the order specified by `first` and\n * `second` members. Otherwise, the view will have exactly one contiguous chunk\n * represented by `first`, and `second` will be empty.\n */\nstruct pr_data_view {\n  char const *first;\n  u16 first_len;\n  char const *second;\n  u16 second_len;\n};\n\n/**\n * Peek data from a element_queue\n * @param eq: the element_queue to read from\n *\n * @return: views into data of element, without perf_event_header.\n */\nstruct pr_data_view pr_peek(const struct perf_ring *eq);\n\n/**\n * Read data from a element_queue\n * @param eq: the element_queue to read from\n * @param lenp: [out] length of the buffer to read\n *\n * @return: on success, offset into data of element, without perf_event_header;\n *   -EINVAL if passed NULL pointers,\n *   -EAGAIN if eq is empty\n */\nint pr_read(struct perf_ring *eq, u16 *lenp);\n\n/**\n * Return the number of bytes left to read in the perf ring\n * @param eq: the eq to read from\n * @param total_size: option parameter to return the total size of the ring\n *\n * @return: number of bytes remaining to read in the perf ring\n *   -EINVAL if a NULL pointer is passed\n */\n\nu32 pr_bytes_remaining(const struct perf_ring *eq, u32 *total_size);\n\n/**\n * Finish the read batch, freeing space for producer\n * @param eq: the element_queue to read from\n */\nstatic inline void pr_finish_read_batch(struct perf_ring *eq)\n{\n  smp_mb();\n\n  eq->shared->data_tail = eq->buf_head;\n\n  assert((int)eq->buf_tail - eq->buf_head >= 0);\n}\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_FASTPASS_UTIL_PERF_RING_H_ */\n"
  },
  {
    "path": "util/perf_ring_cpp.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_PERF_RING_CPP_H_\n#define INCLUDE_FASTPASS_UTIL_PERF_RING_CPP_H_\n\n#include <linux/perf_event.h>\n#include <linux/unistd.h>\n#include <memory>\n#include <platform/platform.h>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <sys/ioctl.h>\n#include <sys/mman.h>\n#include <unistd.h>\n#include <util/perf_ring.h>\n#include <uv.h>\n\nclass PerfRing;\n\n/**\n * All the shared data for a memory-contiguous element queue\n */\nclass PerfRingStorage {\npublic:\n  virtual ~PerfRingStorage() {}\n  char *data() { return data_; }\n  u32 n_data_pages() { return n_data_pages_; }\n  u64 page_size() { return page_size_; }\n\n  typedef void CALLBACK(void *ctx);\n  virtual void set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb) = 0;\n\nprotected:\n  char *data_;\n  u32 n_data_pages_;\n  u64 page_size_;\n};\n\n/**\n * All the shared data for a memory-contiguous element queue\n */\nclass MmapPerfRingStorage : public PerfRingStorage {\npublic:\n  /**\n   * C'tor\n   * @param n_pages: the number of allocated pages for the data queue\n   * @param n_watermark_bytes: the number of bytes to wait to arrive before waking up the fd for reading when events are\n   * available. special case is 0 = never wake up fd (poll only)\n   */\n  MmapPerfRingStorage(int cpu, u32 n_pages, u32 n_watermark_bytes);\n  /**\n   * Move c'tor, for resizing vector holding SinglePerfReader\n   */\n  MmapPerfRingStorage(MmapPerfRingStorage &&other);\n\n  virtual ~MmapPerfRingStorage();\n\n  virtual void set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb);\n\n  /**\n   * Returns the file descriptor of the perf buffer\n   */\n  int fd() { return fd_; }\n\nprivate:\n  /* disallow copy and assignment */\n  MmapPerfRingStorage(const MmapPerfRingStorage &) = delete;\n  void operator=(const MmapPerfRingStorage &) = delete;\n\n  int fd_;\n  size_t mmap_size_;\n  uv_poll_t mmap_poll_;\n  void *callback_ctx_;\n  CALLBACK *callback_;\n  u32 n_watermark_bytes_;\n};\n\nclass PerfRing : public perf_ring {\npublic:\n  PerfRing(std::shared_ptr<PerfRingStorage> storage);\n\n  /* @see pr_start_write_batch */\n  void start_write_batch();\n\n  /**\n   * Writes the given string to the element queue\n   * @throws range_error if queue is full,\n   *    invalid_error if string is empty or trying to write more than eq size\n   *\n   * @see pr_write for more info\n   */\n  void write(std::string_view elem, u32 type);\n\n  /* @see pr_finish_write_batch */\n  void finish_write_batch();\n\n  /* @see pr_start_read_batch */\n  void start_read_batch();\n\n  /* @see pr_peek_size */\n  int peek_size() const;\n\n  /* @see pr_peek_type */\n  int peek_type() const;\n\n  /* @see pr_peek_aligned_u64 */\n  u64 peek_aligned_u64(u16 offset) const;\n\n  /* @see pr_peek_aligned_u32 */\n  u32 peek_aligned_u32(u16 offset) const;\n\n  /* @see pr_peek_aligned_u32 */\n  u16 peek_aligned_u16(u16 offset) const;\n\n  /* @see pr_peek_copy */\n  void peek_copy(char *buf, u16 offset, u16 len) const;\n\n  /* @see pr_bytes_remaining */\n  u32 bytes_remaining(u32 *total_bytes) const;\n\n  std::pair<std::string_view, std::string_view> peek() const;\n\n  /**\n   * Reads an element from the element queue.\n   * @return an element on success. throws otherwise\n   *\n   * @see pr_read for more info\n   */\n  std::string read();\n\n  /**\n   * Discards the next event in the queue\n   */\n  void pop();\n\n  /* @see pr_finish_read_batch */\n  void finish_read_batch();\n\n  /**\n   * Set a callback to execute when events show up in the perf ring\n   * Used if you are not polling for high-speed access\n   * Must be called only after all perf rings are added.\n   */\n  typedef void CALLBACK(void *ctx);\n  void set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb);\n\nprotected:\n  /* the underlying storage backing the element queue */\n  std::shared_ptr<PerfRingStorage> storage_;\n};\n\n/*****************\n * IMPLEMENTATION\n *****************/\n\ninline MmapPerfRingStorage::MmapPerfRingStorage(int cpu, u32 n_bytes, u32 n_watermark_bytes)\n    : callback_ctx_(nullptr), callback_(nullptr), n_watermark_bytes_(n_watermark_bytes)\n{\n  // calculate the number of pages required to store n_bytes\n  page_size_ = getpagesize();\n  u32 n_pages = ((n_bytes + page_size_ - 1) / page_size_);\n\n  // create perf_event_open attributes\n  struct perf_event_attr attr = {};\n  attr.type = PERF_TYPE_SOFTWARE;\n  attr.size = sizeof(struct perf_event_attr);\n  attr.config = PERF_COUNT_SW_BPF_OUTPUT;\n  attr.sample_period = 1;\n  attr.sample_type = PERF_SAMPLE_RAW;\n  attr.watermark = (n_watermark_bytes > 0) ? 1 : 0;\n  attr.wakeup_watermark = n_watermark_bytes;\n\n  fd_ = syscall(__NR_perf_event_open, &attr, -1, cpu, -1, PERF_FLAG_FD_CLOEXEC);\n  if (fd_ == -1) {\n    std::stringstream msg;\n    msg << \"perf_event_open on cpu \" << cpu << \" failed with errno \" << errno << \", error: '\" << strerror(errno) << \"'\";\n    throw std::runtime_error(msg.str());\n  }\n\n  /* unistd.h to get page size */\n  n_data_pages_ = n_pages;\n  mmap_size_ = page_size_ * (1 + n_pages);\n  data_ = (char *)mmap(NULL, mmap_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);\n  if (data_ == MAP_FAILED) {\n    close(fd_);\n\n    std::stringstream msg;\n    msg << \"mmap on cpu \" << cpu << \" failed with errno \" << errno << \", error: '\" << strerror(errno) << \"'\";\n    throw std::runtime_error(msg.str());\n  }\n\n  int res = ioctl(fd_, PERF_EVENT_IOC_ENABLE, 0);\n  if (res != 0) {\n    munmap(data_, mmap_size_);\n    close(fd_);\n\n    std::stringstream msg;\n    msg << \"ioctl PERF_EVENT_IOC_ENABLE on cpu \" << cpu << \" failed with return \" << res << \", errno \" << errno << \", error: '\"\n        << strerror(errno) << \"'\";\n    throw std::runtime_error(msg.str());\n  }\n}\n\ninline MmapPerfRingStorage::MmapPerfRingStorage(MmapPerfRingStorage &&other) : fd_(other.fd_)\n{\n  callback_ = other.callback_;\n  other.callback_ = nullptr;\n  callback_ctx_ = other.callback_ctx_;\n  other.callback_ctx_ = nullptr;\n  data_ = other.data_;\n  other.data_ = NULL;\n  mmap_size_ = other.mmap_size_;\n  other.mmap_size_ = 0;\n  n_data_pages_ = other.n_data_pages_;\n  other.n_data_pages_ = 0;\n  page_size_ = other.page_size_;\n  other.page_size_ = 0;\n}\n\ninline MmapPerfRingStorage::~MmapPerfRingStorage()\n{\n  if (data_ != NULL) {\n    munmap(data_, mmap_size_);\n    close(fd_);\n  }\n  if (callback_) {\n    uv_poll_stop(&mmap_poll_);\n  }\n}\n\ninline void MmapPerfRingStorage::set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb)\n{\n  callback_ = cb;\n  callback_ctx_ = ctx;\n\n  int res = uv_poll_init(&loop, &mmap_poll_, fd_);\n  if (res != 0) {\n    throw std::runtime_error(\"Could not init mmap_poll_\");\n  }\n\n  uv_handle_set_data((uv_handle_t *)&mmap_poll_, this);\n\n  res = uv_poll_start(&mmap_poll_, UV_READABLE, [](uv_poll_t *handle, int status, int events) {\n    MmapPerfRingStorage *obj = (MmapPerfRingStorage *)uv_handle_get_data((uv_handle_t *)handle);\n    (obj->callback_)(obj->callback_ctx_);\n  });\n  if (res != 0) {\n    throw std::runtime_error(\"Could not start watching mmap_poll_\");\n  }\n}\n\ninline PerfRing::PerfRing(std::shared_ptr<PerfRingStorage> storage) : storage_(storage)\n{\n  int res = pr_init_contig(this, storage->data(), storage->n_data_pages(), storage->page_size());\n  if (res != 0) {\n    std::stringstream msg;\n    msg << \"pr_init_contig failed, ret=\" << res << \" storage->data()=\" << (u64)storage->data();\n    throw std::runtime_error(msg.str());\n  }\n}\n\ninline void PerfRing::start_write_batch()\n{\n  pr_start_write_batch(this);\n}\n\ninline void PerfRing::write(std::string_view elem, u32 type)\n{\n  int offset = pr_write(this, elem.length(), type);\n  if (offset == -EINVAL)\n    throw std::invalid_argument(\"pr_write returned -EINVAL\");\n  if (offset == -ENOSPC)\n    throw std::range_error(\"not enough space in element queue\");\n  if (offset < 0)\n    throw std::runtime_error(\"unexpected return value\");\n\n  /* if we reached here, can write the element to offset */\n  if (elem.length() == 0)\n    return;\n\n  int end = (offset + elem.length() - 1) & buf_mask;\n  if ((end - offset) < 0) {\n    /* wraps around */\n    int len_to_ring_end = buf_mask + 1 - offset;\n    memcpy(data + offset, elem.data(), len_to_ring_end);\n    memcpy(data, elem.data() + len_to_ring_end, end + 1);\n  } else {\n    /* simple case */\n    memcpy(data + offset, elem.data(), elem.length());\n  }\n}\n\ninline void PerfRing::finish_write_batch()\n{\n  pr_finish_write_batch(this);\n}\n\ninline void PerfRing::start_read_batch()\n{\n  pr_start_read_batch(this);\n}\n\ninline int PerfRing::peek_size() const\n{\n  return pr_peek_size(this);\n}\n\ninline int PerfRing::peek_type() const\n{\n  return pr_peek_type(this);\n}\n\ninline u32 PerfRing::bytes_remaining(u32 *total_bytes) const\n{\n  return pr_bytes_remaining(this, total_bytes);\n}\n\ninline u64 PerfRing::peek_aligned_u64(u16 offset) const\n{\n  return pr_peek_aligned_u64(this, offset);\n}\n\ninline u32 PerfRing::peek_aligned_u32(u16 offset) const\n{\n  return pr_peek_aligned_u32(this, offset);\n}\n\ninline u16 PerfRing::peek_aligned_u16(u16 offset) const\n{\n  return pr_peek_aligned_u16(this, offset);\n}\n\ninline void PerfRing::peek_copy(char *buf, u16 offset, u16 len) const\n{\n  pr_peek_copy(this, buf, offset, len);\n}\n\ninline std::pair<std::string_view, std::string_view> PerfRing::peek() const\n{\n  auto const view = pr_peek(this);\n  return std::make_pair(std::string_view(view.first, view.first_len), std::string_view(view.second, view.second_len));\n}\n\ninline std::string PerfRing::read()\n{\n  int size = peek_size();\n  if (size == -ENOENT)\n    throw std::out_of_range(\"queue empty\");\n\n  std::string ret(size, 0);\n  peek_copy((char *)ret.data(), 0, size);\n\n  pop();\n\n  return ret;\n}\n\ninline void PerfRing::pop()\n{\n  u16 len;\n  [[maybe_unused]] int offset = pr_read(this, &len);\n  assert(offset >= 0);\n}\n\ninline void PerfRing::finish_read_batch()\n{\n  pr_finish_read_batch(this);\n}\n\ninline void PerfRing::set_callback(uv_loop_t &loop, void *ctx, CALLBACK cb)\n{\n  storage_->set_callback(loop, ctx, cb);\n}\n\n#endif /* INCLUDE_FASTPASS_UTIL_PERF_RING_CPP_H_ */\n"
  },
  {
    "path": "util/pool.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/platform.h>\n#include <util/iterable_bitmap.h>\n#include <util/pool_allocator.h>\n\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <memory>\n#include <type_traits>\n\ntemplate <class T, std::size_t SIZE, class Allocator = std::allocator<T>> class Pool {\npublic:\n  using index_type = typename std::conditional<(SIZE >= (1 << 16) - 1), u32, u16>::type;\n  using element_type = T;\n  using size_type = std::size_t;\n  using bitmap_type = IterableBitmap<SIZE>;\n\n  static constexpr size_type pool_size = SIZE;\n  static constexpr index_type invalid = std::numeric_limits<index_type>::max();\n\nprivate:\n  /* force the used storage to at least hold a u32 */\n  union poolable_type {\n    element_type t;\n    u32 __pool_allocator_bytes_when_destroyed;\n  };\n\n  /* POD type suitable for use as uninitialized storage */\n  using storage_type = typename std::aligned_storage<sizeof(poolable_type), alignof(poolable_type)>::type;\n\n  /* allocator traits rebound to the storage type */\n  using allocator_traits = typename std::allocator_traits<Allocator>::template rebind_traits<storage_type>;\n\npublic:\n  using allocator_type = typename allocator_traits::allocator_type;\n\n  struct position {\n    index_type index;\n    element_type *entry;\n  };\n\n  /**\n   * c'tor\n   */\n  Pool()\n  {\n    /* check pool allocator requirements */\n    static_assert(pool_size > 0, \"pool size must be larger than 0\");\n\n    /* allocate storage */\n    elements_ = allocator_traits::allocate(allocator_, pool_size);\n\n    /* initialize the pool allocator (does not throw) */\n    pool_allocator_init(&pool_alloc_, &elements_[0], sizeof(poolable_type), pool_size);\n  }\n\n  ~Pool()\n  {\n    for (auto i : allocated_) {\n      destroy(i);\n    }\n\n    allocator_traits::deallocate(allocator_, elements_, pool_size);\n  }\n\n  bool empty() const { return size() == 0; }\n\n  bool full() const { return size() == capacity(); }\n\n  size_type size() const { return elem_count_; }\n\n  size_type max_size() const { return max_elem_count_; }\n\n  size_type capacity() const { return pool_size; }\n\n  const bitmap_type &allocated() const { return allocated_; }\n\n  element_type &operator[](index_type index)\n  {\n    assert(index < pool_size);\n    assert(allocated_.get(index));\n    return *(element_type *)&elements_[index];\n  }\n\n  element_type const &operator[](index_type index) const\n  {\n    assert(index < pool_size);\n    assert(allocated_.get(index));\n    return *(element_type *)&elements_[index];\n  }\n\n  /**\n   * Returns the index of the element\n   * @assumes elem is in the pool and was returned from emplace->entry\n   */\n  size_type index_of(element_type *elem)\n  {\n    /* just sanity checks */\n    assert((u64)elem >= (u64)&elements_[0]);\n    assert((u64)elem < (u64)&elements_[0] + sizeof(elements_));\n\n    return (poolable_type *)elem - (poolable_type *)&elements_[0];\n  }\n\n  /**\n   * Emplaces an element into the pool.\n   * @returns position of the new value, or {invalid,nullptr} if the container\n   *   is full.\n   */\n  template <typename... Args> position emplace(Args &&... args)\n  {\n    if (full())\n      return {invalid, nullptr};\n\n    /* allocate an element from the pool */\n    u32 index = pool_allocator_alloc(&pool_alloc_);\n    assert(index != ~0u);\n    bool disarm = false;\n    DisarmGuard pool_guard(disarm, [index, this] { pool_allocator_free(&pool_alloc_, index); });\n\n    /* construct the object, might throw! */\n    allocator_traits::construct(allocator_, (element_type *)&elements_[index], std::forward<Args>(args)...);\n\n    /* okay, we're good! */\n    elem_count_++;\n    allocated_.set(index);\n    disarm = true; /* don't want to deallocate */\n\n    max_elem_count_ = std::max(max_elem_count_, elem_count_);\n\n    return {(index_type)index, (element_type *)&elements_[index]};\n  }\n\n  /**\n   * Erase the value pointed to by key\n   * @return true on success, false if key not found\n   */\n  void remove(index_type index)\n  {\n    assert(index < pool_size);\n    assert(allocated_.get(index));\n\n    /* call destructor */\n    destroy(index);\n    /* reclaim the element into the pool */\n    pool_allocator_free(&pool_alloc_, index);\n\n    elem_count_--;\n    allocated_.clear(index);\n  }\n\nprivate:\n  struct DisarmGuard {\n    template <typename F> DisarmGuard(bool &disarm_b, F fn) : fn_(fn), disarm_(disarm_b) {}\n    ~DisarmGuard()\n    {\n      if (!disarm_)\n        fn_();\n    }\n\n  private:\n    std::function<void(void)> fn_;\n    bool &disarm_;\n  };\n\n  allocator_type allocator_;\n\n  storage_type *elements_;\n\n  bitmap_type allocated_;\n\n  pool_allocator pool_alloc_;\n\n  size_type elem_count_{0};\n  size_type max_elem_count_{0};\n\n  /**\n   * Destroys the element at @index.\n   */\n  void destroy(index_type index) { allocator_traits::destroy(allocator_, (T *)&elements_[(u32)index]); }\n};\n"
  },
  {
    "path": "util/pool_allocator.c",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <util/pool_allocator.h>\n\nint pool_allocator_init(struct pool_allocator *map, void *pool, u32 elem_size, u32 n_elems)\n{\n  if ((map == NULL) || (pool == NULL) || (n_elems == 0) || (elem_size < sizeof(u32)))\n    return -EINVAL;\n\n  map->pool = pool;\n  map->elem_size = elem_size;\n  map->n_elems = n_elems;\n\n  map->free_list = 0;\n  map->alloc_end = 0;\n\n  return 0;\n}\n"
  },
  {
    "path": "util/pool_allocator.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#ifndef INCLUDE_FASTPASS_UTIL_POOL_ALLOCATOR_H_\n#define INCLUDE_FASTPASS_UTIL_POOL_ALLOCATOR_H_\n\n#include <platform/platform.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n#define POOL_ALLOCATOR_NULL_ELEM ((u32)~0U)\n\n/**\n * @pool: The pool of elements. If the element is free, the first u32 is the\n *   index of the next free element (or ~0 if last free element). Otherwise,\n *   contains user data (not owned by the allocator).\n * @free_list: the index of the free list's head element, or ~0 otherwise.\n * @elem_size: the length of each element in bytes (i.e., sizeof an elem)\n */\nstruct pool_allocator {\n  void *pool;\n  u32 free_list;\n  u32 alloc_end;\n  u32 elem_size;\n  u32 n_elems;\n};\n\n/**\n * Internal function to compute a pointer to an elem, cast as u32*\n */\nstatic inline u32 *__pool_allocator_elem(struct pool_allocator *map, u32 index)\n{\n  char *charp = ((char *)map->pool) + index * map->elem_size;\n  return (u32 *)charp;\n}\n\n/**\n * Allocates a new element from the pool.\n *\n * @param map: the pool_allocator to allocate from\n *\n * @returns the index of a new element on success, ~0 if no free entries\n */\nstatic inline u32 pool_allocator_alloc(struct pool_allocator *map)\n{\n  u32 id;\n  u32 next;\n\n  assert(map != NULL);\n  assert(map->free_list <= map->alloc_end);\n\n  if (map->free_list == POOL_ALLOCATOR_NULL_ELEM)\n    return ~0;\n\n  id = map->free_list;\n\n  if (id == map->alloc_end) {\n    // this element was never allocated\n    next = id < (map->n_elems - 1) ? id + 1 : POOL_ALLOCATOR_NULL_ELEM;\n    map->alloc_end = next;\n  } else {\n    // this element was previously returned to the free list\n    next = *__pool_allocator_elem(map, id);\n  }\n\n  map->free_list = next;\n\n  // zero memory before returning\n  memset(__pool_allocator_elem(map, id), 0, map->elem_size);\n\n  return id;\n}\n\n/**\n * Frees the unique ID for further allocation\n */\nstatic inline void pool_allocator_free(struct pool_allocator *map, u32 id)\n{\n  assert((map != NULL) && (id < map->n_elems));\n\n  *__pool_allocator_elem(map, id) = map->free_list;\n  map->free_list = id;\n}\n\n/**\n * Initializes the ID map\n *\n * @param map: the map to initialize\n * @param pool: pointer to the pool of elements allocating from\n * @param elem_size: bytes\n * @param n_elems: the maximum number of elements supported\n *\n * @returns: 0 on success, -EINVAL on (n_elems == 0), NULL map or pool, or\n *   when elem_size is too small to hold a u32.\n */\nint pool_allocator_init(struct pool_allocator *map, void *pool, u32 elem_size, u32 n_elems);\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* INCLUDE_FASTPASS_UTIL_POOL_ALLOCATOR_H_ */\n"
  },
  {
    "path": "util/preprocessor.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// Preprocessor macro to stringize (convert into a string constant) the result of expansion of a macro argument.\n#define PREPROC_STRINGIZE_IMPL(X) #X\n#define PREPROC_STRINGIZE(X) PREPROC_STRINGIZE_IMPL(X)\n\n// Preprocessor macro to concatenate two values, one or both of which is the result of expansion of a macro argument.\n// One common use is to create a uniquely named anonymous local variable.\n// As an example, see DEFER macro.\n#define PREPROC_CONCAT_IMPL(X, Y) X##Y\n#define PREPROC_CONCAT(X, Y) PREPROC_CONCAT_IMPL(X, Y)\n"
  },
  {
    "path": "util/proc_io_view.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/types.h>\n\n// from proc(5) - /proc/[pid]/io\n#define PROC_IO_VIEW_IMPL(X)                                                                                                   \\\n  X(views::NumberView<u64>, rchar, \"rchar\")                                                                                    \\\n  X(views::NumberView<u64>, wchar, \"wchar\")                                                                                    \\\n  X(views::NumberView<u64>, syscr, \"syscr\")                                                                                    \\\n  X(views::NumberView<u64>, syscw, \"syscw\")                                                                                    \\\n  X(views::NumberView<u64>, read_bytes, \"read_bytes\")                                                                          \\\n  X(views::NumberView<u64>, write_bytes, \"write_bytes\")                                                                        \\\n  X(views::NumberView<u64>, cancelled_write_bytes, \"cancelled_write_bytes\")\n"
  },
  {
    "path": "util/proc_ops.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/proc_ops.h>\n\n#include <util/string_view.h>\n\n#include <absl/container/flat_hash_map.h>\n\nnamespace {\n\ntemplate <typename T> void parse_proc_stat_field(views::NumberView<T> &out, std::string_view &data)\n{\n  using namespace views;\n  trim_run(data, WHITESPACE);\n  out = trim_up_to(data, WHITESPACE, SeekBehavior::CONSUME);\n}\n\nvoid parse_proc_stat_field(std::string_view &out, std::string_view &data)\n{\n  using namespace views;\n  trim_up_to(data, '(', SeekBehavior::CONSUME);\n  out = trim_up_to_last(data, ')', SeekBehavior::CONSUME);\n}\n\nvoid parse_proc_stat_field(ProcessState &out, std::string_view &data)\n{\n  using namespace views;\n  trim_run(data, WHITESPACE);\n  if (data.empty()) {\n    out = ProcessState::unknown;\n  } else {\n    out = sanitize_enum(static_cast<ProcessState>(data.front()));\n    data.remove_prefix(1);\n  }\n}\n\nstd::string_view parse_label(std::string_view &data)\n{\n  using namespace views;\n  trim_run(data, WHITESPACE);\n  return trim_up_to(data, ':', SeekBehavior::CONSUME);\n}\n\n} // namespace\n\nstatic bool is_valid_value(ProcessState value)\n{\n  return true;\n}\n\nstatic bool is_valid_value(std::string_view value)\n{\n  return !value.empty();\n}\n\ntemplate <typename T> static bool is_valid_value(views::NumberView<T> value)\n{\n  return !value.empty();\n}\n\nProcStatView::ProcStatView(std::string_view data) : data_(data)\n{\n#define PROC_STAT_VIEW_PARSE_FIELDS(Type, Name, ...) parse_proc_stat_field(Name, data);\n  PROC_STAT_VIEW_IMPL(PROC_STAT_VIEW_PARSE_FIELDS)\n#undef PROC_STAT_VIEW_PARSE_FIELDS\n}\n\nbool ProcStatView::internal_validity_check() const\n{\n  return true\n#define VERIFY_FIELDS(Type, Name, ...) &&is_valid_value(Name)\n      PROC_STAT_VIEW_IMPL(VERIFY_FIELDS)\n#undef VERIFY_FIELDS\n          ;\n}\n\nnamespace {\nauto const proc_status_view_parse_map = [] {\n  // set up parsing map for labels\n  using namespace views;\n\n  using parser_fn = void (*)(ProcStatusView &, std::string_view &);\n  absl::flat_hash_map<std::string_view, parser_fn> map;\n\n#define PROC_STATUS_VIEW_PARSE_FIELDS(Type, Name, Label, ...)                                                                  \\\n  map[Label] = [](ProcStatusView &out, std::string_view &data) {                                                               \\\n    trim_run(data, NON_EOL_WHITESPACE);                                                                                        \\\n    auto const view = trim_up_to(data, EOL, SeekBehavior::CONSUME);                                                            \\\n    out.Name = view;                                                                                                           \\\n  };\n  PROC_STATUS_VIEW_IMPL(PROC_STATUS_VIEW_PARSE_FIELDS)\n#undef PROC_STATUS_VIEW_PARSE_FIELDS\n\n  return map;\n}();\n} // namespace\n\nProcStatusView::ProcStatusView(std::string_view data) : data_(data)\n{\n  using namespace views;\n\n  while (!data.empty()) {\n    auto const label = parse_label(data);\n    auto const parser = proc_status_view_parse_map.find(label);\n    if (parser == proc_status_view_parse_map.end()) {\n      trim_up_to(data, EOL, SeekBehavior::CONSUME);\n    } else {\n      parser->second(*this, data);\n    }\n  }\n}\n\nbool ProcStatusView::internal_validity_check() const\n{\n  return true\n#define VERIFY_FIELDS(Type, Name, ...) &&!Name.empty()\n      PROC_STATUS_VIEW_IMPL(VERIFY_FIELDS)\n#undef VERIFY_FIELDS\n          ;\n}\n\nnamespace {\nauto const proc_io_view_parse_map = [] {\n  // set up parsing map for labels\n  using namespace views;\n\n  using parser_fn = void (*)(ProcIoView &, std::string_view &);\n  absl::flat_hash_map<std::string_view, parser_fn> map;\n\n#define PROC_IO_VIEW_PARSE_FIELDS(Type, Name, Label, ...)                                                                      \\\n  map[Label] = [](ProcIoView &out, std::string_view &data) {                                                                   \\\n    trim_run(data, NON_EOL_WHITESPACE);                                                                                        \\\n    auto const view = trim_up_to(data, EOL, SeekBehavior::CONSUME);                                                            \\\n    out.Name = view;                                                                                                           \\\n  };\n  PROC_IO_VIEW_IMPL(PROC_IO_VIEW_PARSE_FIELDS)\n#undef PROC_IO_VIEW_PARSE_FIELDS\n\n  return map;\n}();\n} // namespace\n\nProcIoView::ProcIoView(std::string_view data) : data_(data)\n{\n  using namespace views;\n\n  while (!data.empty()) {\n    auto const label = parse_label(data);\n    auto const parser = proc_io_view_parse_map.find(label);\n    if (parser == proc_io_view_parse_map.end()) {\n      trim_up_to(data, EOL, SeekBehavior::CONSUME);\n    } else {\n      parser->second(*this, data);\n    }\n  }\n}\n\nbool ProcIoView::internal_validity_check() const\n{\n  return true\n#define VERIFY_FIELDS(Type, Name, ...) &&!Name.empty()\n      PROC_IO_VIEW_IMPL(VERIFY_FIELDS)\n#undef VERIFY_FIELDS\n          ;\n}\n"
  },
  {
    "path": "util/proc_ops.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/expected.h>\n#include <util/process_state.h>\n#include <util/string_view.h>\n\n#include <string_view>\n\n#define PROC_VIEWS_COMMON_CODE(ClassName)                                                                                      \\\npublic:                                                                                                                        \\\n  ClassName() = default;                                                                                                       \\\n  ClassName(std::string_view data);                                                                                            \\\n                                                                                                                               \\\n  explicit operator bool() const { return !data_.empty() && internal_validity_check(); }                                       \\\n  bool operator!() const { return !static_cast<bool>(*this); }                                                                 \\\n  bool valid() const { return static_cast<bool>(*this); }                                                                      \\\n  std::string_view view() const { return data_; }                                                                              \\\n                                                                                                                               \\\nprivate:                                                                                                                       \\\n  bool internal_validity_check() const;                                                                                        \\\n  std::string_view data_;\n\n#include <util/proc_stat_view.inl>\nstruct ProcStatView {\n#define PROC_STAT_VIEW_DECLARE_FIELDS(Type, Name, ...) Type Name;\n  PROC_STAT_VIEW_IMPL(PROC_STAT_VIEW_DECLARE_FIELDS)\n#undef PROC_STAT_VIEW_DECLARE_FIELDS\n\n  PROC_VIEWS_COMMON_CODE(ProcStatView);\n};\n\n#include <util/proc_status_view.inl>\nstruct ProcStatusView {\n#define PROC_STATUS_VIEW_DECLARE_FIELDS(Type, Name, ...) Type Name;\n  PROC_STATUS_VIEW_IMPL(PROC_STATUS_VIEW_DECLARE_FIELDS)\n#undef PROC_STATUS_VIEW_DECLARE_FIELDS\n\n  PROC_VIEWS_COMMON_CODE(ProcStatusView);\n};\n\n#include <util/proc_io_view.inl>\nstruct ProcIoView {\n#define PROC_IO_VIEW_DECLARE_FIELDS(Type, Name, ...) Type Name;\n  PROC_IO_VIEW_IMPL(PROC_IO_VIEW_DECLARE_FIELDS)\n#undef PROC_IO_VIEW_DECLARE_FIELDS\n\n  PROC_VIEWS_COMMON_CODE(ProcIoView);\n};\n"
  },
  {
    "path": "util/proc_ops_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/proc_ops.h>\n\nTEST(proc_stat_view, pid_3062214)\n{\n  constexpr std::string_view data =\n      R\"(3062214 (multi thread) S 2829421 3062214 2829421 34819 3062214 1077936128 138 0 0 0 3 0 0 0 20 0 4 0 183646228 29917184 289 18446744073709551615 94693039165440 94693039850013 140729897079600 0 0 0 0 0 0 0 0 0 17 2 0 0 0 0 0 94693040067224 94693040096472 94693057708032 140729897082593 140729897082611 140729897082611 140729897086950 0)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(3062214, view.pid);\n  EXPECT_EQ(\"multi thread\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(2829421, view.ppid);\n  EXPECT_EQ(3062214, view.pgrp);\n  EXPECT_EQ(2829421, view.session);\n  EXPECT_EQ(34819, view.tty_nr);\n  EXPECT_EQ(3062214, view.tpgid);\n  EXPECT_EQ(1077936128, view.flags);\n  EXPECT_EQ(138, view.minflt);\n  EXPECT_EQ(0, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(3, view.utime);\n  EXPECT_EQ(0, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(0, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(4, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(183646228ull, view.starttime);\n  EXPECT_EQ(29917184, view.vsize);\n  EXPECT_EQ(289, view.rss);\n  EXPECT_EQ(18446744073709551615ul, view.rsslim);\n  EXPECT_EQ(94693039165440ul, view.startcode);\n  EXPECT_EQ(94693039850013, view.endcode);\n  EXPECT_EQ(140729897079600, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(0, view.blocked);\n  EXPECT_EQ(0, view.sigignore);\n  EXPECT_EQ(0, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(2, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(94693040067224, view.start_data);\n  EXPECT_EQ(94693040096472, view.end_data);\n  EXPECT_EQ(94693057708032, view.start_brk);\n  EXPECT_EQ(140729897082593, view.arg_start);\n  EXPECT_EQ(140729897082611, view.arg_end);\n  EXPECT_EQ(140729897082611, view.env_start);\n  EXPECT_EQ(140729897086950, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, pid_3049866)\n{\n  constexpr std::string_view data =\n      R\"(3049866 (multi-thread) S 2829421 3049866 2829421 34819 3049866 1077936128 136 0 0 0 22 26 0 0 20 0 4 0 183619102 29917184 305 18446744073709551615 94892149485568 94892150170141 140724065920688 0 0 0 0 0 0 0 0 0 17 1 0 0 0 0 0 94892150387352 94892150416600 94892153749504 140724065922713 140724065922755 140724065922755 140724065927118 0)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(3049866, view.pid);\n  EXPECT_EQ(\"multi-thread\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(2829421, view.ppid);\n  EXPECT_EQ(3049866, view.pgrp);\n  EXPECT_EQ(2829421, view.session);\n  EXPECT_EQ(34819, view.tty_nr);\n  EXPECT_EQ(3049866, view.tpgid);\n  EXPECT_EQ(1077936128, view.flags);\n  EXPECT_EQ(136, view.minflt);\n  EXPECT_EQ(0, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(22, view.utime);\n  EXPECT_EQ(26, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(0, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(4, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(183619102ull, view.starttime);\n  EXPECT_EQ(29917184, view.vsize);\n  EXPECT_EQ(305, view.rss);\n  EXPECT_EQ(18446744073709551615ul, view.rsslim);\n  EXPECT_EQ(94892149485568ul, view.startcode);\n  EXPECT_EQ(94892150170141ul, view.endcode);\n  EXPECT_EQ(140724065920688ul, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(0, view.blocked);\n  EXPECT_EQ(0, view.sigignore);\n  EXPECT_EQ(0, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(1, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(94892150387352, view.start_data);\n  EXPECT_EQ(94892150416600, view.end_data);\n  EXPECT_EQ(94892153749504, view.start_brk);\n  EXPECT_EQ(140724065922713, view.arg_start);\n  EXPECT_EQ(140724065922755, view.arg_end);\n  EXPECT_EQ(140724065922755, view.env_start);\n  EXPECT_EQ(140724065927118, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, pid_3070715)\n{\n  constexpr std::string_view data =\n      R\"(3070715 (multi (99) thre) S 2829421 3070715 2829421 34819 3070715 1077936128 139 0 0 0 2 1 0 0 20 0 4 0 183650343 29917184 306 18446744073709551615 94786539032576 94786539717149 140728695372256 0 0 0 0 0 0 0 0 0 17 0 0 0 0 0 0 94786539934360 94786539963608 94786569641984 140728695377618 140728695377641 140728695377641 140728695381985 0)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(3070715, view.pid);\n  EXPECT_EQ(\"multi (99) thre\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(2829421, view.ppid);\n  EXPECT_EQ(3070715, view.pgrp);\n  EXPECT_EQ(2829421, view.session);\n  EXPECT_EQ(34819, view.tty_nr);\n  EXPECT_EQ(3070715, view.tpgid);\n  EXPECT_EQ(1077936128, view.flags);\n  EXPECT_EQ(139, view.minflt);\n  EXPECT_EQ(0, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(2, view.utime);\n  EXPECT_EQ(1, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(0, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(4, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(183650343ull, view.starttime);\n  EXPECT_EQ(29917184, view.vsize);\n  EXPECT_EQ(306, view.rss);\n  EXPECT_EQ(18446744073709551615ul, view.rsslim);\n  EXPECT_EQ(94786539032576ul, view.startcode);\n  EXPECT_EQ(94786539717149ul, view.endcode);\n  EXPECT_EQ(140728695372256ul, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(0, view.blocked);\n  EXPECT_EQ(0, view.sigignore);\n  EXPECT_EQ(0, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(0, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(94786539934360, view.start_data);\n  EXPECT_EQ(94786539963608, view.end_data);\n  EXPECT_EQ(94786569641984, view.start_brk);\n  EXPECT_EQ(140728695377618, view.arg_start);\n  EXPECT_EQ(140728695377641, view.arg_end);\n  EXPECT_EQ(140728695377641, view.env_start);\n  EXPECT_EQ(140728695381985, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_io_view, pid_6545)\n{\n  constexpr std::string_view data = R\"(rchar: 783422713330\nwchar: 197059793079\nsyscr: 127142356\nsyscw: 71858467\nread_bytes: 3966263296\nwrite_bytes: 75290411008\ncancelled_write_bytes: 11436556288)\";\n\n  ProcIoView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(783422713330ull, view.rchar);\n  EXPECT_EQ(197059793079ull, view.wchar);\n  EXPECT_EQ(127142356ull, view.syscr);\n  EXPECT_EQ(71858467ull, view.syscw);\n  EXPECT_EQ(3966263296ull, view.read_bytes);\n  EXPECT_EQ(75290411008ull, view.write_bytes);\n  EXPECT_EQ(11436556288ull, view.cancelled_write_bytes);\n}\n\nTEST(proc_io_view, pid_662910)\n{\n  constexpr std::string_view data = R\"(rchar: 9069720\nwchar: 337393\nsyscr: 1131\nsyscw: 76\nread_bytes: 27787264\nwrite_bytes: 2957312\ncancelled_write_bytes: 16384)\";\n\n  ProcIoView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(9069720ull, view.rchar);\n  EXPECT_EQ(337393ull, view.wchar);\n  EXPECT_EQ(1131ull, view.syscr);\n  EXPECT_EQ(76ull, view.syscw);\n  EXPECT_EQ(27787264ull, view.read_bytes);\n  EXPECT_EQ(2957312ull, view.write_bytes);\n  EXPECT_EQ(16384ull, view.cancelled_write_bytes);\n}\n\nTEST(proc_io_view, pid_717621)\n{\n  constexpr std::string_view data = R\"(rchar: 41578\nwchar: 8\nsyscr: 53\nsyscw: 2\nread_bytes: 0\nwrite_bytes: 4096\ncancelled_write_bytes: 0)\";\n\n  ProcIoView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(41578ull, view.rchar);\n  EXPECT_EQ(8ull, view.wchar);\n  EXPECT_EQ(53ull, view.syscr);\n  EXPECT_EQ(2ull, view.syscw);\n  EXPECT_EQ(0ull, view.read_bytes);\n  EXPECT_EQ(4096ull, view.write_bytes);\n  EXPECT_EQ(0ull, view.cancelled_write_bytes);\n}\n\nTEST(proc_io_view, pid_98303)\n{\n  constexpr std::string_view data = R\"(rchar: 394415079\nwchar: 82744660\nsyscr: 554996\nsyscw: 371456\nread_bytes: 912367616\nwrite_bytes: 27467776\ncancelled_write_bytes: 610304)\";\n\n  ProcIoView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(394415079ull, view.rchar);\n  EXPECT_EQ(82744660ull, view.wchar);\n  EXPECT_EQ(554996ull, view.syscr);\n  EXPECT_EQ(371456ull, view.syscw);\n  EXPECT_EQ(912367616ull, view.read_bytes);\n  EXPECT_EQ(27467776ull, view.write_bytes);\n  EXPECT_EQ(610304ull, view.cancelled_write_bytes);\n}\n\nTEST(proc_io_view, pid_996354)\n{\n  constexpr std::string_view data = R\"(rchar: 42616\nwchar: 1236\nsyscr: 2570\nsyscw: 1236\nread_bytes: 0\nwrite_bytes: 0\ncancelled_write_bytes: 0)\";\n\n  ProcIoView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(42616ull, view.rchar);\n  EXPECT_EQ(1236ull, view.wchar);\n  EXPECT_EQ(2570ull, view.syscr);\n  EXPECT_EQ(1236ull, view.syscw);\n  EXPECT_EQ(0ull, view.read_bytes);\n  EXPECT_EQ(0ull, view.write_bytes);\n  EXPECT_EQ(0ull, view.cancelled_write_bytes);\n}\n\nTEST(proc_status_view, pid_6545)\n{\n  constexpr std::string_view data = R\"(Name:\tdocker-containe\nUmask:\t0022\nState:\tS (sleeping)\nTgid:\t6545\nNgid:\t0\nPid:\t6545\nPPid:\t6486\nTracerPid:\t0\nUid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\nFDSize:\t64\nGroups:\t \nNStgid:\t6545\nNSpid:\t6545\nNSpgid:\t6545\nNSsid:\t6545\nVmPeak:\t 1760208 kB\nVmSize:\t 1694672 kB\nVmLck:\t       0 kB\nVmPin:\t       0 kB\nVmHWM:\t   42008 kB\nVmRSS:\t   23800 kB\nRssAnon:\t   12708 kB\nRssFile:\t   11092 kB\nRssShmem:\t       0 kB\nVmData:\t  279540 kB\nVmStk:\t     132 kB\nVmExe:\t   16636 kB\nVmLib:\t       0 kB\nVmPTE:\t     340 kB\nVmSwap:\t    3080 kB\nHugetlbPages:\t       0 kB\nCoreDumping:\t0\nTHP_enabled:\t1\nThreads:\t22\nSigQ:\t0/126119\nSigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\tfffffffe3bfa2800\nSigIgn:\t0000000000000000\nSigCgt:\tffffffffffc1feff\nCapInh:\t0000000000000000\nCapPrm:\t0000003fffffffff\nCapEff:\t0000003fffffffff\nCapBnd:\t0000003fffffffff\nCapAmb:\t0000000000000000\nNoNewPrivs:\t0\nSeccomp:\t0\nSpeculation_Store_Bypass:\tthread vulnerable\nCpus_allowed:\t3f\nCpus_allowed_list:\t0-5\nMems_allowed:\t00000000,00000001\nMems_allowed_list:\t0\nvoluntary_ctxt_switches:\t290\nnonvoluntary_ctxt_switches:\t0)\";\n\n  ProcStatusView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(290, view.voluntary_ctxt_switches);\n  EXPECT_EQ(0, view.nonvoluntary_ctxt_switches);\n}\n\nTEST(proc_status_view, pid_662910)\n{\n  constexpr std::string_view data = R\"(Name:\tmysqld\nUmask:\t0006\nState:\tS (sleeping)\nTgid:\t662910\nNgid:\t0\nPid:\t662910\nPPid:\t1\nTracerPid:\t0\nUid:\t113\t113\t113\t113\nGid:\t120\t120\t120\t120\nFDSize:\t128\nGroups:\t120 \nNStgid:\t662910\nNSpid:\t662910\nNSpgid:\t662910\nNSsid:\t662910\nVmPeak:\t 1972020 kB\nVmSize:\t 1906484 kB\nVmLck:\t       0 kB\nVmPin:\t       0 kB\nVmHWM:\t   81016 kB\nVmRSS:\t   57224 kB\nRssAnon:\t   47328 kB\nRssFile:\t    9896 kB\nRssShmem:\t       0 kB\nVmData:\t  701244 kB\nVmStk:\t     132 kB\nVmExe:\t   13312 kB\nVmLib:\t     424 kB\nVmPTE:\t     432 kB\nVmSwap:\t   19224 kB\nHugetlbPages:\t       0 kB\nCoreDumping:\t0\nTHP_enabled:\t1\nThreads:\t30\nSigQ:\t0/126119\nSigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\t0000000000087007\nSigIgn:\t0000000000001000\nSigCgt:\t00000001800066e9\nCapInh:\t0000000000000000\nCapPrm:\t0000000000000000\nCapEff:\t0000000000000000\nCapBnd:\t0000000000004000\nCapAmb:\t0000000000000000\nNoNewPrivs:\t1\nSeccomp:\t2\nSpeculation_Store_Bypass:\tthread force mitigated\nCpus_allowed:\t3f\nCpus_allowed_list:\t0-5\nMems_allowed:\t00000000,00000001\nMems_allowed_list:\t0\nvoluntary_ctxt_switches:\t233\nnonvoluntary_ctxt_switches:\t18)\";\n\n  ProcStatusView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(233, view.voluntary_ctxt_switches);\n  EXPECT_EQ(18, view.nonvoluntary_ctxt_switches);\n}\n\nTEST(proc_status_view, pid_717621)\n{\n  constexpr std::string_view data = R\"(Name:\tVBoxXPCOMIPCD\nUmask:\t0077\nState:\tS (sleeping)\nTgid:\t717621\nNgid:\t0\nPid:\t717621\nPPid:\t1\nTracerPid:\t0\nUid:\t1000\t1000\t1000\t1000\nGid:\t1000\t1000\t1000\t1000\nFDSize:\t64\nGroups:\t24 25 27 29 30 44 46 109 116 1000 \nNStgid:\t717621\nNSpid:\t717621\nNSpgid:\t717621\nNSsid:\t717620\nVmPeak:\t   67896 kB\nVmSize:\t   67688 kB\nVmLck:\t       0 kB\nVmPin:\t       0 kB\nVmHWM:\t   13744 kB\nVmRSS:\t   13568 kB\nRssAnon:\t    2780 kB\nRssFile:\t   10788 kB\nRssShmem:\t       0 kB\nVmData:\t   10384 kB\nVmStk:\t     136 kB\nVmExe:\t      20 kB\nVmLib:\t   14796 kB\nVmPTE:\t     108 kB\nVmSwap:\t       0 kB\nHugetlbPages:\t       0 kB\nCoreDumping:\t0\nTHP_enabled:\t0\nThreads:\t1\nSigQ:\t2/126119\nSigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\t0000000000002000\nSigIgn:\t0000000000001002\nSigCgt:\t1000000180010000\nCapInh:\t0000000000000000\nCapPrm:\t0000000000000000\nCapEff:\t0000000000000000\nCapBnd:\t0000003fffffffff\nCapAmb:\t0000000000000000\nNoNewPrivs:\t0\nSeccomp:\t0\nSpeculation_Store_Bypass:\tthread vulnerable\nCpus_allowed:\t3f\nCpus_allowed_list:\t0-5\nMems_allowed:\t00000000,00000001\nMems_allowed_list:\t0\nvoluntary_ctxt_switches:\t929318\nnonvoluntary_ctxt_switches:\t6003)\";\n\n  ProcStatusView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(929318, view.voluntary_ctxt_switches);\n  EXPECT_EQ(6003, view.nonvoluntary_ctxt_switches);\n}\n\nTEST(proc_status_view, pid_98303)\n{\n  constexpr std::string_view data = R\"(Name:\tbash\nUmask:\t0022\nState:\tS (sleeping)\nTgid:\t98303\nNgid:\t0\nPid:\t98303\nPPid:\t13384\nTracerPid:\t0\nUid:\t1000\t1000\t1000\t1000\nGid:\t1000\t1000\t1000\t1000\nFDSize:\t256\nGroups:\t24 25 27 29 30 44 46 109 116 1000 \nNStgid:\t98303\nNSpid:\t98303\nNSpgid:\t98303\nNSsid:\t98303\nVmPeak:\t    9600 kB\nVmSize:\t    9564 kB\nVmLck:\t       0 kB\nVmPin:\t       0 kB\nVmHWM:\t    6320 kB\nVmRSS:\t    5524 kB\nRssAnon:\t    2712 kB\nRssFile:\t    2812 kB\nRssShmem:\t       0 kB\nVmData:\t    2780 kB\nVmStk:\t     132 kB\nVmExe:\t     880 kB\nVmLib:\t    1560 kB\nVmPTE:\t      52 kB\nVmSwap:\t       0 kB\nHugetlbPages:\t       0 kB\nCoreDumping:\t0\nTHP_enabled:\t1\nThreads:\t1\nSigQ:\t2/126119\nSigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\t0000000000000000\nSigIgn:\t0000000000380004\nSigCgt:\t00000001cb817efb\nCapInh:\t0000000000000000\nCapPrm:\t0000000000000000\nCapEff:\t0000000000000000\nCapBnd:\t0000003fffffffff\nCapAmb:\t0000000000000000\nNoNewPrivs:\t0\nSeccomp:\t0\nSpeculation_Store_Bypass:\tthread vulnerable\nCpus_allowed:\t3f\nCpus_allowed_list:\t0-5\nMems_allowed:\t00000000,00000001\nMems_allowed_list:\t0\nvoluntary_ctxt_switches:\t4562\nnonvoluntary_ctxt_switches:\t61)\";\n\n  ProcStatusView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(4562, view.voluntary_ctxt_switches);\n  EXPECT_EQ(61, view.nonvoluntary_ctxt_switches);\n}\n\nTEST(proc_status_view, pid_996354)\n{\n  constexpr std::string_view data = R\"(Name:\tchrome\nUmask:\t0022\nState:\tS (sleeping)\nTgid:\t996354\nNgid:\t0\nPid:\t996354\nPPid:\t7077\nTracerPid:\t0\nUid:\t1000\t1000\t1000\t1000\nGid:\t1000\t1000\t1000\t1000\nFDSize:\t128\nGroups:\t24 25 27 29 30 44 46 109 116 1000 \nNStgid:\t996354\t117016\nNSpid:\t996354\t117016\nNSpgid:\t7061\t0\nNSsid:\t7061\t0\nVmPeak:\t 8714700 kB\nVmSize:\t 4712640 kB\nVmLck:\t       0 kB\nVmPin:\t       0 kB\nVmHWM:\t  189412 kB\nVmRSS:\t  161136 kB\nRssAnon:\t   69048 kB\nRssFile:\t   82976 kB\nRssShmem:\t    9112 kB\nVmData:\t  253240 kB\nVmStk:\t     132 kB\nVmExe:\t  144868 kB\nVmLib:\t   18104 kB\nVmPTE:\t    1140 kB\nVmSwap:\t       0 kB\nHugetlbPages:\t       0 kB\nCoreDumping:\t0\nTHP_enabled:\t1\nThreads:\t12\nSigQ:\t2/126119\nSigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\t0000000000000000\nSigIgn:\t0000000000001002\nSigCgt:\t00000001c0010000\nCapInh:\t0000000000000000\nCapPrm:\t0000000000000000\nCapEff:\t0000000000000000\nCapBnd:\t0000003fffffffff\nCapAmb:\t0000000000000000\nNoNewPrivs:\t1\nSeccomp:\t2\nSpeculation_Store_Bypass:\tthread force mitigated\nCpus_allowed:\t3f\nCpus_allowed_list:\t0-5\nMems_allowed:\t00000000,00000001\nMems_allowed_list:\t0\nvoluntary_ctxt_switches:\t2792\nnonvoluntary_ctxt_switches:\t281)\";\n\n  ProcStatusView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(2792, view.voluntary_ctxt_switches);\n  EXPECT_EQ(281, view.nonvoluntary_ctxt_switches);\n}\n\nTEST(proc_stat_view, pid_19929)\n{\n  constexpr std::string_view data =\n      R\"(19929 (docker-containe) S 2075 19929 2075 0 -1 1077936384 2186078 5647 0 0 6716 10474 0 3 20 0 10 0 164204224 7675904 1075 18446744073709551615 4194304 6343957 140724213514144 0 0 0 1006249984 0 2143420159 0 0 0 17 1 0 0 0 0 0 8261632 8367936 36073472 140724213521731 140724213522062 140724213522062 140724213522392 0)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(19929, view.pid);\n  EXPECT_EQ(\"docker-containe\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(2075, view.ppid);\n  EXPECT_EQ(19929, view.pgrp);\n  EXPECT_EQ(2075, view.session);\n  EXPECT_EQ(0, view.tty_nr);\n  EXPECT_EQ(-1, view.tpgid);\n  EXPECT_EQ(1077936384, view.flags);\n  EXPECT_EQ(2186078, view.minflt);\n  EXPECT_EQ(5647, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(6716, view.utime);\n  EXPECT_EQ(10474, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(3, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(10, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(164204224ull, view.starttime);\n  EXPECT_EQ(7675904, view.vsize);\n  EXPECT_EQ(1075, view.rss);\n  EXPECT_EQ(18446744073709551615ul, view.rsslim);\n  EXPECT_EQ(4194304ul, view.startcode);\n  EXPECT_EQ(6343957ul, view.endcode);\n  EXPECT_EQ(140724213514144, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(1006249984, view.blocked);\n  EXPECT_EQ(0, view.sigignore);\n  EXPECT_EQ(2143420159, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(1, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(8261632, view.start_data);\n  EXPECT_EQ(8367936, view.end_data);\n  EXPECT_EQ(36073472, view.start_brk);\n  EXPECT_EQ(140724213521731, view.arg_start);\n  EXPECT_EQ(140724213522062, view.arg_end);\n  EXPECT_EQ(140724213522062, view.env_start);\n  EXPECT_EQ(140724213522392, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, ipv6_addrconf)\n{\n  constexpr std::string_view data =\n      R\"(80 (ipv6_addrconf) I 2 0 0 0 -1 69238880 0 0 0 0 0 0 0 0 0 -20 1 0 87 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0\n)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(80, view.pid);\n  EXPECT_EQ(\"ipv6_addrconf\", view.comm);\n  EXPECT_TRUE(ProcessState::unknown == sanitize_enum(view.state));\n  EXPECT_EQ(2, view.ppid);\n  EXPECT_EQ(0, view.pgrp);\n  EXPECT_EQ(0, view.session);\n  EXPECT_EQ(0, view.tty_nr);\n  EXPECT_EQ(-1, view.tpgid);\n  EXPECT_EQ(69238880, view.flags);\n  EXPECT_EQ(0, view.minflt);\n  EXPECT_EQ(0, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(0, view.utime);\n  EXPECT_EQ(0, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(0, view.cstime);\n  EXPECT_EQ(0, view.priority);\n  EXPECT_EQ(-20, view.nice);\n  EXPECT_EQ(1, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(87ull, view.starttime);\n  EXPECT_EQ(0, view.vsize);\n  EXPECT_EQ(0, view.rss);\n  EXPECT_EQ(18446744073709551615ull, view.rsslim);\n  EXPECT_EQ(0ul, view.startcode);\n  EXPECT_EQ(0ul, view.endcode);\n  EXPECT_EQ(0, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(0, view.blocked);\n  EXPECT_EQ(2147483647, view.sigignore);\n  EXPECT_EQ(0, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(1, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(0, view.start_data);\n  EXPECT_EQ(0, view.end_data);\n  EXPECT_EQ(0, view.start_brk);\n  EXPECT_EQ(0, view.arg_start);\n  EXPECT_EQ(0, view.arg_end);\n  EXPECT_EQ(0, view.env_start);\n  EXPECT_EQ(0, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, kernel_collector)\n{\n  constexpr std::string_view data =\n      R\"(39973 (kernel-collecto) S 1293 39973 1293 34817 39973 4194304 393 27583 0 0 0 0 7 8 20 0 1 0 9864441 6959104 906 18446744073709551615 94780814675968 94780815573037 140724866766880 0 0 0 65536 4 65538 0 0 0 17 0 0 0 0 0 0 94780815803376 94780815850756 94780843311104 140724866770313 140724866770428 140724866770428 140724866772962 0\n)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(39973, view.pid);\n  EXPECT_EQ(\"kernel-collecto\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(1293, view.ppid);\n  EXPECT_EQ(39973, view.pgrp);\n  EXPECT_EQ(1293, view.session);\n  EXPECT_EQ(34817, view.tty_nr);\n  EXPECT_EQ(39973, view.tpgid);\n  EXPECT_EQ(4194304, view.flags);\n  EXPECT_EQ(393, view.minflt);\n  EXPECT_EQ(27583, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(0, view.utime);\n  EXPECT_EQ(0, view.stime);\n  EXPECT_EQ(7, view.cutime);\n  EXPECT_EQ(8, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(1, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(9864441, view.starttime);\n  EXPECT_EQ(6959104, view.vsize);\n  EXPECT_EQ(906, view.rss);\n  EXPECT_EQ(18446744073709551615ull, view.rsslim);\n  EXPECT_EQ(94780814675968, view.startcode);\n  EXPECT_EQ(94780815573037, view.endcode);\n  EXPECT_EQ(140724866766880, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(65536, view.blocked);\n  EXPECT_EQ(4, view.sigignore);\n  EXPECT_EQ(65538, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(0, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(94780815803376, view.start_data);\n  EXPECT_EQ(94780815850756, view.end_data);\n  EXPECT_EQ(94780843311104, view.start_brk);\n  EXPECT_EQ(140724866770313, view.arg_start);\n  EXPECT_EQ(140724866770428, view.arg_end);\n  EXPECT_EQ(140724866770428, view.env_start);\n  EXPECT_EQ(140724866772962, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, collector_entry)\n{\n  constexpr std::string_view data =\n      R\"(40060 (collector-entry) S 40043 40060 40060 34816 40060 4194560 441 81195 0 0 0 0 41 5 20 0 1 0 9864881 3956736 782 18446744073709551615 94886840201216 94886841096077 140732640240592 0 0 0 65536 4 65538 0 0 0 17 0 0 0 0 0 0 94886841324528 94886841371908 94886870679552 140732640246970 140732640247110 140732640247110 140732640247771 0\n)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(40060, view.pid);\n  EXPECT_EQ(\"collector-entry\", view.comm);\n  EXPECT_TRUE(ProcessState::interruptible_sleep == sanitize_enum(view.state));\n  EXPECT_EQ(40043, view.ppid);\n  EXPECT_EQ(40060, view.pgrp);\n  EXPECT_EQ(40060, view.session);\n  EXPECT_EQ(34816, view.tty_nr);\n  EXPECT_EQ(40060, view.tpgid);\n  EXPECT_EQ(4194560, view.flags);\n  EXPECT_EQ(441, view.minflt);\n  EXPECT_EQ(81195, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(0, view.utime);\n  EXPECT_EQ(0, view.stime);\n  EXPECT_EQ(41, view.cutime);\n  EXPECT_EQ(5, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(1, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(9864881, view.starttime);\n  EXPECT_EQ(3956736, view.vsize);\n  EXPECT_EQ(782, view.rss);\n  EXPECT_EQ(18446744073709551615ull, view.rsslim);\n  EXPECT_EQ(94886840201216, view.startcode);\n  EXPECT_EQ(94886841096077, view.endcode);\n  EXPECT_EQ(140732640240592, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(65536, view.blocked);\n  EXPECT_EQ(4, view.sigignore);\n  EXPECT_EQ(65538, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(0, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(94886841324528, view.start_data);\n  EXPECT_EQ(94886841371908, view.end_data);\n  EXPECT_EQ(94886870679552, view.start_brk);\n  EXPECT_EQ(140732640246970, view.arg_start);\n  EXPECT_EQ(140732640247110, view.arg_end);\n  EXPECT_EQ(140732640247110, view.env_start);\n  EXPECT_EQ(140732640247771, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n\nTEST(proc_stat_view, kworker_39842)\n{\n  constexpr std::string_view data =\n      R\"(39842 (kworker/u4:0-events_unbound) I 2 0 0 0 -1 69238880 0 315 0 0 0 0 0 0 20 0 1 0 9863357 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n)\";\n\n  ProcStatView const view(data);\n  EXPECT_TRUE(view.valid());\n\n  EXPECT_EQ(39842, view.pid);\n  EXPECT_EQ(\"kworker/u4:0-events_unbound\", view.comm);\n  EXPECT_TRUE(ProcessState::unknown == sanitize_enum(view.state));\n  EXPECT_EQ(2, view.ppid);\n  EXPECT_EQ(0, view.pgrp);\n  EXPECT_EQ(0, view.session);\n  EXPECT_EQ(0, view.tty_nr);\n  EXPECT_EQ(-1, view.tpgid);\n  EXPECT_EQ(69238880, view.flags);\n  EXPECT_EQ(0, view.minflt);\n  EXPECT_EQ(315, view.cminflt);\n  EXPECT_EQ(0, view.majflt);\n  EXPECT_EQ(0, view.cmajflt);\n  EXPECT_EQ(0, view.utime);\n  EXPECT_EQ(0, view.stime);\n  EXPECT_EQ(0, view.cutime);\n  EXPECT_EQ(0, view.cstime);\n  EXPECT_EQ(20, view.priority);\n  EXPECT_EQ(0, view.nice);\n  EXPECT_EQ(1, view.num_threads);\n  EXPECT_EQ(0, view.itrealvalue);\n  EXPECT_EQ(9863357, view.starttime);\n  EXPECT_EQ(0, view.vsize);\n  EXPECT_EQ(0, view.rss);\n  EXPECT_EQ(18446744073709551615ull, view.rsslim);\n  EXPECT_EQ(0, view.startcode);\n  EXPECT_EQ(0, view.endcode);\n  EXPECT_EQ(0, view.startstack);\n  EXPECT_EQ(0, view.kstkesp);\n  EXPECT_EQ(0, view.kstkeip);\n  EXPECT_EQ(0, view.signal);\n  EXPECT_EQ(0, view.blocked);\n  EXPECT_EQ(2147483647, view.sigignore);\n  EXPECT_EQ(0, view.sigcatch);\n  EXPECT_EQ(0, view.wchan);\n  EXPECT_EQ(0, view.nswap);\n  EXPECT_EQ(0, view.cnswap);\n  EXPECT_EQ(17, view.exit_signal);\n  EXPECT_EQ(0, view.processor);\n  EXPECT_EQ(0, view.rt_priority);\n  EXPECT_EQ(0, view.policy);\n  EXPECT_EQ(0, view.delayacct_blkio_ticks);\n  EXPECT_EQ(0, view.guest_time);\n  EXPECT_EQ(0, view.cguest_time);\n  EXPECT_EQ(0, view.start_data);\n  EXPECT_EQ(0, view.end_data);\n  EXPECT_EQ(0, view.start_brk);\n  EXPECT_EQ(0, view.arg_start);\n  EXPECT_EQ(0, view.arg_end);\n  EXPECT_EQ(0, view.env_start);\n  EXPECT_EQ(0, view.env_end);\n  EXPECT_EQ(0, view.exit_code);\n}\n"
  },
  {
    "path": "util/proc_stat_view.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// from proc(5) - /proc/[pid]/stat\n#define PROC_STAT_VIEW_IMPL(X)                                                                                                 \\\n  X(views::NumberView<int>, pid)                                                                                               \\\n  X(std::string_view, comm)                                                                                                    \\\n  X(ProcessState, state)                                                                                                       \\\n  X(views::NumberView<int>, ppid)                                                                                              \\\n  X(views::NumberView<int>, pgrp)                                                                                              \\\n  X(views::NumberView<int>, session)                                                                                           \\\n  X(views::NumberView<int>, tty_nr)                                                                                            \\\n  X(views::NumberView<int>, tpgid)                                                                                             \\\n  X(views::NumberView<unsigned>, flags)                                                                                        \\\n  X(views::NumberView<unsigned long>, minflt)                                                                                  \\\n  X(views::NumberView<unsigned long>, cminflt)                                                                                 \\\n  X(views::NumberView<unsigned long>, majflt)                                                                                  \\\n  X(views::NumberView<unsigned long>, cmajflt)                                                                                 \\\n  X(views::NumberView<unsigned long>, utime)                                                                                   \\\n  X(views::NumberView<unsigned long>, stime)                                                                                   \\\n  X(views::NumberView<long>, cutime)                                                                                           \\\n  X(views::NumberView<long>, cstime)                                                                                           \\\n  X(views::NumberView<long>, priority)                                                                                         \\\n  X(views::NumberView<long>, nice)                                                                                             \\\n  X(views::NumberView<long>, num_threads)                                                                                      \\\n  X(views::NumberView<long>, itrealvalue)                                                                                      \\\n  X(views::NumberView<unsigned long long>, starttime)                                                                          \\\n  X(views::NumberView<unsigned long>, vsize)                                                                                   \\\n  X(views::NumberView<long>, rss)                                                                                              \\\n  X(views::NumberView<unsigned long>, rsslim)                                                                                  \\\n  X(views::NumberView<unsigned long>, startcode)                                                                               \\\n  X(views::NumberView<unsigned long>, endcode)                                                                                 \\\n  X(views::NumberView<unsigned long>, startstack)                                                                              \\\n  X(views::NumberView<unsigned long>, kstkesp)                                                                                 \\\n  X(views::NumberView<unsigned long>, kstkeip)                                                                                 \\\n  X(views::NumberView<unsigned long>, signal)                                                                                  \\\n  X(views::NumberView<unsigned long>, blocked)                                                                                 \\\n  X(views::NumberView<unsigned long>, sigignore)                                                                               \\\n  X(views::NumberView<unsigned long>, sigcatch)                                                                                \\\n  X(views::NumberView<unsigned long>, wchan)                                                                                   \\\n  X(views::NumberView<unsigned long>, nswap)                                                                                   \\\n  X(views::NumberView<unsigned long>, cnswap)                                                                                  \\\n  X(views::NumberView<int>, exit_signal)                                                                                       \\\n  X(views::NumberView<int>, processor)                                                                                         \\\n  X(views::NumberView<unsigned>, rt_priority)                                                                                  \\\n  X(views::NumberView<unsigned>, policy)                                                                                       \\\n  X(views::NumberView<unsigned long long>, delayacct_blkio_ticks)                                                              \\\n  X(views::NumberView<unsigned long>, guest_time)                                                                              \\\n  X(views::NumberView<long>, cguest_time)                                                                                      \\\n  X(views::NumberView<unsigned long>, start_data)                                                                              \\\n  X(views::NumberView<unsigned long>, end_data)                                                                                \\\n  X(views::NumberView<unsigned long>, start_brk)                                                                               \\\n  X(views::NumberView<unsigned long>, arg_start)                                                                               \\\n  X(views::NumberView<unsigned long>, arg_end)                                                                                 \\\n  X(views::NumberView<unsigned long>, env_start)                                                                               \\\n  X(views::NumberView<unsigned long>, env_end)                                                                                 \\\n  X(views::NumberView<int>, exit_code)\n"
  },
  {
    "path": "util/proc_status_view.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <platform/types.h>\n\n// from proc(5) - /proc/[pid]/status\n#define PROC_STATUS_VIEW_IMPL(X)                                                                                               \\\n  X(views::NumberView<u64>, voluntary_ctxt_switches, \"voluntary_ctxt_switches\")                                                \\\n  X(views::NumberView<u64>, nonvoluntary_ctxt_switches, \"nonvoluntary_ctxt_switches\")\n\n/* Excluding these fields from the view since we don't care about those\n * This speeds up parsing\n\n  X(std::string_view, Umask, \"Umask\") \\\n  X(std::string_view, State, \"State\") \\\n  X(std::string_view, Tgid, \"Tgid\") \\\n  X(std::string_view, Ngid, \"Ngid\") \\\n  X(std::string_view, Pid, \"Pid\") \\\n  X(std::string_view, PPid, \"PPid\") \\\n  X(std::string_view, TracerPid, \"TracerPid\") \\\n  X(std::string_view, Uid, \"Uid\") \\\n  X(std::string_view, Gid, \"Gid\") \\\n  X(std::string_view, FDSize, \"FDSize\") \\\n  X(std::string_view, Groups, \"Groups\") \\\n  X(std::string_view, NStgid, \"NStgid\") \\\n  X(std::string_view, NSpid, \"NSpid\") \\\n  X(std::string_view, NSpgid, \"NSpgid\") \\\n  X(std::string_view, NSsid, \"NSsid\") \\\n  X(std::string_view, VmPeak, \"VmPeak\") \\\n  X(std::string_view, VmSize, \"VmSize\") \\\n  X(std::string_view, VmLck, \"VmLck\") \\\n  X(std::string_view, VmPin, \"VmPin\") \\\n  X(std::string_view, VmHWM, \"VmHWM\") \\\n  X(std::string_view, VmRSS, \"VmRSS\") \\\n  X(std::string_view, RssAnon, \"RssAnon\") \\\n  X(std::string_view, RssFile, \"RssFile\") \\\n  X(std::string_view, RssShmem, \"RssShmem\") \\\n  X(std::string_view, VmData, \"VmData\") \\\n  X(std::string_view, VmStk, \"VmStk\") \\\n  X(std::string_view, VmExe, \"VmExe\") \\\n  X(std::string_view, VmLib, \"VmLib\") \\\n  X(std::string_view, VmPTE, \"VmPTE\") \\\n  X(std::string_view, VmSwap, \"VmSwap\") \\\n  X(std::string_view, HugetlbPages, \"HugetlbPages\") \\\n  X(std::string_view, CoreDumping, \"CoreDumping\") \\\n  X(std::string_view, THP_enabled, \"THP_enabled\") \\\n  X(std::string_view, Threads, \"Threads\") \\\n  X(std::string_view, SigQ, \"SigQ\") \\\n  X(std::string_view, SigPnd, \"SigPnd\") \\\n  X(std::string_view, ShdPnd, \"ShdPnd\") \\\n  X(std::string_view, SigBlk, \"SigBlk\") \\\n  X(std::string_view, SigIgn, \"SigIgn\") \\\n  X(std::string_view, SigCgt, \"SigCgt\") \\\n  X(std::string_view, CapInh, \"CapInh\") \\\n  X(std::string_view, CapPrm, \"CapPrm\") \\\n  X(std::string_view, CapEff, \"CapEff\") \\\n  X(std::string_view, CapBnd, \"CapBnd\") \\\n  X(std::string_view, CapAmb, \"CapAmb\") \\\n  X(std::string_view, NoNewPrivs, \"NoNewPrivs\") \\\n  X(std::string_view, Seccomp, \"Seccomp\") \\\n  X(std::string_view, Speculation_Store_Bypass, \"Speculation_Store_Bypass\") \\\n  X(std::string_view, Cpus_allowed, \"Cpus_allowed\") \\\n  X(std::string_view, Cpus_allowed_list, \"Cpus_allowed_list\") \\\n  X(std::string_view, Mems_allowed, \"Mems_allowed\") \\\n  X(std::string_view, Mems_allowed_list, \"Mems_allowed_list\") \\\n*/\n"
  },
  {
    "path": "util/process_state.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n// from proc(5)\n#define ENUM_NAME ProcessState\n#define ENUM_TYPE char\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(unknown, 0, \"\")                                                                                                            \\\n  X(uninterruptible_sleep, 'D', \"\") /*Waiting in uninterruptible disk sleep*/                                                  \\\n  X(wake_kill, 'K', \"\")             /*Wakekill (Linux 2.6.33 .. 3.13)*/                                                        \\\n  X(parked, 'P', \"\")                /*Parked (Linux 3.9 .. 3.13)*/                                                             \\\n  X(running, 'R', \"\")               /*Running*/                                                                                \\\n  X(interruptible_sleep, 'S', \"\")   /*Sleeping in an interruptible wait*/                                                      \\\n  X(stopped, 'T', \"\")               /*Stopped (on a signal) or (Linux < 2.6.33) trace stopped*/                                \\\n  X(waking_paging, 'W', \"\")         /*Paging (Linux < 2.6.0) / Waking (Linux 2.6.33 .. 3.13)*/                                 \\\n  X(dead, 'X', \"\")                  /*Dead (Linux >= 2.6.0)*/                                                                  \\\n  X(zombie, 'Z', \"\")                /*Zombie*/                                                                                 \\\n  X(tracing_stop, 't', \"\")          /*Tracing stop (Linux >= 2.6.33)*/                                                         \\\n  X(dead_x, 'x', \"\")                /*Dead (Linux 2.6.33 .. 3.13)*/\n#define ENUM_DEFAULT unknown\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "util/protobuf_log.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <google/protobuf/io/zero_copy_stream_impl.h>\n#include <google/protobuf/message.h>\n#include <google/protobuf/text_format.h>\n\nstd::ostream &operator<<(std::ostream &out, google::protobuf::Message const &message)\n{\n  google::protobuf::io::OstreamOutputStream output(&out);\n\n  google::protobuf::TextFormat::Printer printer;\n  printer.SetExpandAny(true);\n\n  printer.Print(message, &output);\n\n  return out;\n}\n\ntemplate <typename T>\nstruct fmt::formatter<T, char, std::enable_if_t<std::is_base_of_v<google::protobuf::Message, T>>> : fmt::formatter<std::string> {\n  template <typename FormatContext>\n  auto format(const T &message, FormatContext &ctx) const\n  {\n    std::ostringstream oss;\n    oss << message;\n    return fmt::formatter<std::string>::format(oss.str(), ctx);\n  }\n};\n"
  },
  {
    "path": "util/raii.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <memory>\n\n/**\n * A `std::unique_ptr` compatible deleter that's statically bound to a free\n * function deleter and is an empty instance.\n */\ntemplate <typename T, typename DeleterResult, DeleterResult (*Deleter)(T *)> struct free_function_deleter {\n  void operator()(T *p) { Deleter(p); }\n};\n\n/**\n * A unique pointer for POD (plain-old-data) that requires deletion using a free function call.\n */\ntemplate <typename T, typename DeleterResult, DeleterResult (*Deleter)(T *)>\nusing pod_unique_ptr = std::unique_ptr<T, free_function_deleter<T, DeleterResult, Deleter>>;\n"
  },
  {
    "path": "util/random.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/random.h>\n\nextern template class RNG<std::uint_fast32_t>;\nextern template class RNG<std::uint_fast64_t>;\n"
  },
  {
    "path": "util/random.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <random>\n#include <type_traits>\n\ntemplate <typename T> class RNG {\npublic:\n  static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>);\n\n  using engine_type = std::conditional_t<(sizeof(T) <= sizeof(std::mt19937::result_type)), std::mt19937, std::mt19937_64>;\n\n  using result_type = typename engine_type::result_type;\n\n  static_assert(sizeof(T) <= sizeof(result_type));\n\n  /**\n   * Generates a random integer of type `Value` in the range [`lower_bound`, `upper_bound`].\n   */\n  template <typename Value = result_type> static Value next(Value lower_bound, Value upper_bound);\n\n  /**\n   * Generates a random integer of type `Value` in the range [0, `upper_bound`].\n   */\n  template <typename Value = result_type> static Value next(Value upper_bound);\n\n  /**\n   * Generates a random integer of type `Value`.\n   */\n  template <typename Value = result_type> static Value next();\n\n  static thread_local engine_type engine;\n};\n\n#include <util/random.inl>\n\nusing rng_32 = RNG<std::uint_fast32_t>;\nusing rng_64 = RNG<std::uint_fast64_t>;\n"
  },
  {
    "path": "util/random.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <limits>\n\n#include <cassert>\n\ntemplate <typename T>\nthread_local typename RNG<T>::engine_type RNG<T>::engine{[] {\n  std::random_device seeder;\n  return seeder();\n}()};\n\ntemplate <typename T> template <typename Value> Value RNG<T>::next(Value lower_bound, Value upper_bound)\n{\n  assert(lower_bound <= upper_bound);\n  return std::uniform_int_distribution<Value>{lower_bound, upper_bound}(engine);\n}\n\ntemplate <typename T> template <typename Value> Value RNG<T>::next(Value upper_bound)\n{\n  return next<Value>(0, upper_bound);\n}\n\ntemplate <typename T> template <typename Value> Value RNG<T>::next()\n{\n  return next<Value>(std::numeric_limits<Value>::min(), std::numeric_limits<Value>::max());\n}\n"
  },
  {
    "path": "util/random_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <util/random.h>\n\nconstexpr std::size_t TIGHT_LOOP_ITERATIONS = 1'000'000;\n\nTEST(rng_32, next)\n{\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_32::next();\n    EXPECT_GE(result, std::numeric_limits<rng_32::result_type>::min());\n    EXPECT_LE(result, std::numeric_limits<rng_32::result_type>::max());\n  }\n}\n\nTEST(rng_32, next_upper_bound)\n{\n  constexpr std::int32_t upper_bound = 100;\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_32::next(upper_bound);\n    EXPECT_GE(result, 0);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n\nTEST(rng_32, next_lower_upper_bound)\n{\n  constexpr std::int32_t lower_bound = -100;\n  constexpr std::int32_t upper_bound = 100;\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_32::next(lower_bound, upper_bound);\n    EXPECT_GE(result, lower_bound);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n\nTEST(rng_64, next)\n{\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_64::next();\n    EXPECT_GE(result, std::numeric_limits<rng_64::result_type>::min());\n    EXPECT_LE(result, std::numeric_limits<rng_64::result_type>::max());\n  }\n}\n\nTEST(rng_64, next_upper_bound)\n{\n  constexpr std::int64_t upper_bound = 100;\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_64::next(upper_bound);\n    EXPECT_GE(result, 0);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n\nTEST(rng_64, next_lower_upper_bound)\n{\n  constexpr std::int64_t lower_bound = -100;\n  constexpr std::int64_t upper_bound = 100;\n  for (auto iterations = TIGHT_LOOP_ITERATIONS; iterations--;) {\n    auto const result = rng_64::next(lower_bound, upper_bound);\n    EXPECT_GE(result, lower_bound);\n    EXPECT_LE(result, upper_bound);\n  }\n}\n"
  },
  {
    "path": "util/raw_json.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <jitbuf/jb.h>\n\n#include <iomanip>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n\n#include <cstdint>\n#include <cstdlib>\n\nstatic constexpr std::string_view json_printable_characters = \" !#$%&'()*+,-./0123456789:;<=>?\"\n                                                              \"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_\"\n                                                              \"`abcdefghijklmnopqrstuvwxyz{|}~\";\n\nstatic constexpr std::string_view json_backslash_escape_characters = \"\\\"\\\\\\b\\f\\n\\r\\t\";\n\nstatic constexpr std::string_view json_non_hex_escape_characters = \" !#$%&'()*+,-./0123456789:;<=>?\"\n                                                                   \"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_\"\n                                                                   \"`abcdefghijklmnopqrstuvwxyz{|}~\"\n                                                                   \"\\\"\\\\\\b\\f\\n\\r\\t\";\n\ntemplate <bool DoubleQuoteWrap = true, typename Out> Out &&print_escaped_json_string(Out &&out, std::string_view value)\n{\n  if constexpr (DoubleQuoteWrap) {\n    out << '\"';\n  }\n\n  while (!value.empty()) {\n    // characters that don't need escaping\n    {\n      auto index = value.find_first_not_of(json_printable_characters);\n      if (index == std::string_view::npos) {\n        index = value.size();\n      }\n\n      out.write(value.data(), index);\n      value.remove_prefix(index);\n    }\n\n    // characters that need backslash escaping\n    {\n      auto index = value.find_first_not_of(json_backslash_escape_characters);\n      if (index == std::string_view::npos) {\n        index = value.size();\n      }\n\n      for (auto const c : value.substr(0, index)) {\n        out << '\\\\';\n        switch (c) {\n        case '\\b':\n          out << 'b';\n          break;\n        case '\\f':\n          out << 'f';\n          break;\n        case '\\n':\n          out << 'n';\n          break;\n        case '\\r':\n          out << 'r';\n          break;\n        case '\\t':\n          out << 't';\n          break;\n        default:\n          out << c;\n          break;\n        }\n      }\n\n      value.remove_prefix(index);\n    }\n\n    // characters that need hex escaping\n    {\n      auto index = value.find_first_of(json_non_hex_escape_characters);\n      if (index == std::string_view::npos) {\n        index = value.size();\n      }\n\n      for (auto const c : value.substr(0, index)) {\n        out << \"\\\\u\" << std::setw(4) << std::setfill('0') << std::hex\n            << static_cast<std::uint16_t>(static_cast<std::uint8_t>(c)) << std::dec << std::setw(0);\n      }\n\n      value.remove_prefix(index);\n    }\n  }\n\n  if constexpr (DoubleQuoteWrap) {\n    out << '\"';\n  }\n\n  return std::forward<Out>(out);\n}\n\ntemplate <bool DoubleQuoteWrap = true, typename Out> Out &&print_escaped_json_string(Out &&out, jb_blob value)\n{\n  print_escaped_json_string<DoubleQuoteWrap>(out, value.string_view());\n  return std::forward<Out>(out);\n}\n\ntemplate <bool DoubleQuoteWrap = true, typename Out, std::size_t Size>\nOut &&print_escaped_json_string(Out &&out, std::uint8_t const (&value)[Size])\n{\n  print_escaped_json_string<DoubleQuoteWrap>(out, std::string_view(reinterpret_cast<char const *>(value), Size));\n  return std::forward<Out>(out);\n}\n\ntemplate <bool DoubleQuoteWrap = true, typename Out, std::size_t Size>\nOut &&print_escaped_json_string(Out &&out, std::array<unsigned char, Size> const &value)\n{\n  print_escaped_json_string<DoubleQuoteWrap>(out, to_jb_blob(value));\n  return std::forward<Out>(out);\n}\n\ntemplate <typename T, typename Out> Out &&print_json_value(Out &&out, T const &value)\n{\n  if constexpr (std::is_same_v<bool, T>) {\n    constexpr char const *literals[] = {\"false\", \"true\"};\n    out << literals[value];\n  } else if constexpr (std::is_same_v<std::int8_t, T> || std::is_same_v<char, T>) {\n    // avoid printing the raw character\n    out << static_cast<std::int16_t>(value);\n  } else if constexpr (std::is_same_v<std::uint8_t, T> || std::is_same_v<unsigned char, T>) {\n    // avoid printing the raw character\n    out << static_cast<std::uint16_t>(value);\n  } else if constexpr (std::is_same_v<__int128, T> || std::is_same_v<__int128 unsigned, T>) {\n    // __int128 types don't have built-in operator<< for std::ostream\n    // Output as hex using two u64 values with leading zeros\n    __int128 unsigned uval = static_cast<__int128 unsigned>(value);\n    std::uint64_t high = static_cast<std::uint64_t>(uval >> 64);\n    std::uint64_t low = static_cast<std::uint64_t>(uval);\n    out << \"\\\"0x\" << std::setfill('0') << std::hex << std::setw(16) << high \n        << std::setw(16) << low << std::dec << \"\\\"\";\n  } else if constexpr (std::is_integral_v<T>) {\n    out << value;\n  } else {\n    print_escaped_json_string(out, value);\n  }\n  return std::forward<Out>(out);\n}\n"
  },
  {
    "path": "util/render.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\ntemplate <typename T> using DispatchProtocolMemberHandlerFunc = void (T::*)(u64, char *);\n\n// wraps a member function of T as a protocol handler\ntemplate <typename T, DispatchProtocolMemberHandlerFunc<T> member_handler_func>\nvoid dispatch_protocol_member_handler(void *context, u64 timestamp, char *buffer)\n{\n  auto const object = reinterpret_cast<T *>(context);\n  (object->*member_handler_func)(timestamp, buffer);\n}\n"
  },
  {
    "path": "util/resource_usage.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <chrono>\n\n#include <cstdint>\n\nstruct ResourceUsage {\n  using duration = std::chrono::microseconds;\n\n  duration user_mode_time;\n  duration kernel_mode_time;\n  std::uint64_t max_resident_set_size;\n  std::uint32_t minor_page_faults;\n  std::uint32_t major_page_faults;\n  std::uint32_t block_input_count;\n  std::uint32_t block_output_count;\n  std::uint32_t voluntary_context_switch_count;\n  std::uint32_t involuntary_context_switch_count;\n  std::uint16_t cpu_usage_by_process; // per-thousand fixed point, lowest 3 digits are fractional\n};\n"
  },
  {
    "path": "util/resource_usage_reporter.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/resource_usage_reporter.h>\n\n#include <util/log.h>\n#include <util/system_ops.h>\n\n#include <sys/resource.h>\n#include <sys/time.h>\n\n#include <chrono>\n#include <utility>\n\nusing namespace std::literals::chrono_literals;\n\nconstexpr auto REPORT_INTERVAL = 1s;\n\nResourceUsageReporter::ResourceUsageReporter(uv_loop_t &loop, ::ebpf_net::ingest::Writer &writer)\n    : last_check_(monotonic_clock::now()), writer_(writer), scheduler_(loop, [this] {\n        this->collect();\n        return scheduling::JobFollowUp::ok;\n      })\n{}\n\nvoid ResourceUsageReporter::start()\n{\n  if (!scheduler_.start(REPORT_INTERVAL, REPORT_INTERVAL)) {\n    LOG::error(\"failed to schedule resource usage reporter\");\n  }\n}\n\nvoid ResourceUsageReporter::stop()\n{\n  scheduler_.stop();\n}\n\nvoid ResourceUsageReporter::collect()\n{\n  if (auto const info = get_resource_usage()) {\n    auto const now = monotonic_clock::now();\n    auto const interval = now - last_check_;\n    auto const cpu_time = ResourceUsageReporter::duration{info->kernel_mode_time + info->user_mode_time};\n    // per-thousand fixed point, lowest 3 digits are fractional\n    auto const cpu_perthousand = (cpu_time * 1000) / interval;\n\n    writer_.agent_resource_usage(\n        integer_time<std::chrono::microseconds>(info->user_mode_time),\n        integer_time<std::chrono::microseconds>(info->kernel_mode_time),\n        info->max_resident_set_size,\n        info->minor_page_faults,\n        info->major_page_faults,\n        info->block_input_count,\n        info->block_output_count,\n        info->voluntary_context_switch_count,\n        info->involuntary_context_switch_count,\n        cpu_perthousand,\n        0 // TODO: properly gather this value\n    );\n\n    last_check_ = now;\n  } else {\n    LOG::error(\"failed to query resource usage: {}\", info.error().what());\n  }\n}\n\nvoid ResourceUsageReporter::report(::ebpf_net::ingest::Writer &writer)\n{\n  if (auto const info = get_resource_usage()) {\n    writer.agent_resource_usage(\n        integer_time<std::chrono::microseconds>(info->user_mode_time),\n        integer_time<std::chrono::microseconds>(info->kernel_mode_time),\n        info->max_resident_set_size,\n        info->minor_page_faults,\n        info->major_page_faults,\n        info->block_input_count,\n        info->block_output_count,\n        info->voluntary_context_switch_count,\n        info->involuntary_context_switch_count,\n        0,\n        0 // TODO: properly gather this value\n    );\n  } else {\n    LOG::error(\"failed to query resource usage: {}\", info.error().what());\n  }\n}\n"
  },
  {
    "path": "util/resource_usage_reporter.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <scheduling/interval_scheduler.h>\n#include <util/resource_usage.h>\n#include <util/time.h>\n\n#include <generated/ebpf_net/ingest/writer.h>\n\nclass ResourceUsageReporter {\npublic:\n  using duration = std::common_type_t<monotonic_clock::duration, ResourceUsage::duration>;\n\n  ResourceUsageReporter(uv_loop_t &loop, ::ebpf_net::ingest::Writer &writer);\n\n  void start();\n  void stop();\n\n  static void report(::ebpf_net::ingest::Writer &writer);\n\nprivate:\n  void collect();\n\n  monotonic_clock::time_point last_check_;\n  ::ebpf_net::ingest::Writer &writer_;\n  scheduling::IntervalScheduler scheduler_;\n};\n"
  },
  {
    "path": "util/restful.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/restful.h>\n\nRestfulFetcher::RestfulFetcher(std::initializer_list<std::string_view> headers)\n{\n  for (auto const header : headers) {\n    headers_.emplace_back(header);\n  }\n}\n\nvoid RestfulFetcher::http_proxy(std::string host, std::uint16_t port)\n{\n  proxy_ = std::move(host);\n  proxy_port_ = port;\n  proxy_type_ = CURLPROXY_HTTP;\n}\n"
  },
  {
    "path": "util/restful.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n#include <util/expected.h>\n\n#include <curl/curl.h>\n\n#include <chrono>\n#include <initializer_list>\n#include <list>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n\nclass RestfulFetcher {\npublic:\n  template <typename T> class CtorDecoder {\n  public:\n    Expected<T, std::runtime_error> operator()(std::string body) const;\n  };\n\n  RestfulFetcher(std::initializer_list<std::string_view> headers = {});\n\n  void http_proxy(std::string host, std::uint16_t port);\n\n  template <typename T, typename Decoder = CtorDecoder<T>>\n  Expected<T, std::runtime_error> sync_fetch(\n      std::string_view description,\n      std::string url,\n      Decoder &&decoder = Decoder(),\n      std::chrono::milliseconds const timeout = 0ms,\n      std::size_t const retries = 0,\n      std::chrono::milliseconds const initial_backoff = 200ms,\n      std::chrono::milliseconds const maximum_backoff = 1min,\n      unsigned const backoff_geometric_ratio = 2);\n\nprivate:\n  std::list<std::string> headers_;\n  std::string proxy_;\n  long proxy_port_;\n  curl_proxytype proxy_type_;\n};\n\n#include <util/restful.inl>\n"
  },
  {
    "path": "util/restful.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <curlpp/Easy.hpp>\n#include <curlpp/Infos.hpp>\n#include <curlpp/Options.hpp>\n#include <curlpp/cURLpp.hpp>\n\ntemplate <typename T> Expected<T, std::runtime_error> RestfulFetcher::CtorDecoder<T>::operator()(std::string body) const\n{\n  try {\n    return T{std::move(body)};\n  } catch (std::runtime_error const &e) {\n    return {unexpected, e};\n  } catch (std::exception const &e) {\n    return {unexpected, std::runtime_error{e.what()}};\n  }\n}\n\ntemplate <typename T, typename Decoder>\nExpected<T, std::runtime_error> RestfulFetcher::sync_fetch(\n    std::string_view description,\n    std::string url,\n    Decoder &&decoder,\n    std::chrono::milliseconds const timeout,\n    std::size_t const retries,\n    std::chrono::milliseconds const initial_backoff,\n    std::chrono::milliseconds const maximum_backoff,\n    unsigned const backoff_geometric_ratio)\n{\n  curlpp::Easy request;\n\n  request.setOpt<curlpp::options::Url>(std::move(url));\n  request.setOpt<curlpp::options::HttpHeader>(headers_);\n\n  if (!proxy_.empty()) {\n    request.setOpt<curlpp::options::Proxy>(proxy_);\n    request.setOpt<curlpp::options::ProxyPort>(proxy_port_);\n    request.setOpt<curlpp::options::ProxyType>(proxy_type_);\n  }\n\n  if (timeout.count()) {\n    request.setOpt<curlpp::OptionTrait<long, CURLOPT_TIMEOUT_MS>>(timeout.count());\n  }\n\n  std::ostringstream body;\n  request.setOpt<curlpp::options::WriteStream>(&body);\n\n  auto backoff_interval = initial_backoff;\n\n  for (std::size_t attempt = 0; attempt <= retries; ++attempt) {\n    bool const last_attempt = attempt == retries;\n\n    auto const backoff = [&](std::string_view error) {\n      if (last_attempt) {\n        return false;\n      }\n\n      LOG::warn(\"backing-off for {} before trying again after failing to fetch {}: {}\", backoff_interval, description, error);\n\n      std::this_thread::sleep_for(backoff_interval);\n\n      backoff_interval = std::min(backoff_interval * backoff_geometric_ratio, maximum_backoff);\n\n      return true;\n    };\n\n    try {\n      request.perform();\n\n      auto const status = curlpp::infos::ResponseCode::get(request);\n\n      if (status != 200) {\n        auto error = fmt::format(\"error while fetching {}: status code {}\", description, status);\n        if (!backoff(error)) {\n          return {unexpected, std::move(error)};\n        }\n        continue;\n      }\n\n      auto decoded = decoder(body.str());\n      if (!decoded) {\n        auto error = fmt::format(\"error while decoding {}: {}\", description, decoded.error().what());\n        if (!backoff(error)) {\n          return {unexpected, std::move(error)};\n        }\n        continue;\n      }\n\n      return std::move(*decoded);\n    } catch (curlpp::LibcurlRuntimeError const &e) {\n      auto error = fmt::format(\"error while fetching {}: {}\", description, e.what());\n      if (!backoff(error)) {\n        return {unexpected, std::move(error)};\n      }\n      continue;\n    }\n  }\n\n  return {unexpected, fmt::format(\"unable to fetch {} after {} tries\", description, retries + 1)};\n}\n"
  },
  {
    "path": "util/short_string.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <jitbuf/jb.h>\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n#include <string>\n#include <string_view>\n\nstruct short_string_behavior {\n  struct truncate_t {\n  };\n  static constexpr truncate_t truncate = {};\n  struct dont_truncate_t {\n  };\n  static constexpr dont_truncate_t no_truncate = {};\n};\n\ntemplate <std::size_t N> struct short_string {\n  static constexpr std::size_t max_len = N;\n\n  /**\n   * default c'tor\n   */\n  short_string() = default;\n\n  void set(const char *other, std::size_t other_len)\n  {\n    assert(other_len <= max_len);\n    len = std::min(other_len, max_len);\n    std::memcpy(buf, other, len);\n  }\n\n  /**\n   * Initializer from buffer\n   */\n  short_string(const char *other, std::size_t other_len) { set(other, other_len); }\n\n  /**\n   * Initializer from char array\n   */\n  template <std::size_t Size> short_string(char const (&data)[Size])\n  {\n    // exclude null terminator if present\n    set(data, strnlen(data, Size / sizeof(*data)));\n  }\n\n  short_string(short_string_behavior::truncate_t, std::string_view view) { set(view.data(), std::min(view.size(), max_len)); }\n\n  short_string(short_string_behavior::dont_truncate_t, std::string_view view)\n  {\n    // TODO: add proper runtime checking and ensure call sites handle error case gracefully\n    set(view.data(), view.size());\n  }\n\n  short_string(short_string_behavior::truncate_t, jb_blob view)\n  {\n    set(view.data(), std::min(static_cast<std::size_t>(view.size()), max_len));\n  }\n\n  short_string(short_string_behavior::dont_truncate_t, jb_blob view)\n  {\n    // TODO: add proper runtime checking and ensure call sites handle error case gracefully\n    set(view.data(), view.size());\n  }\n\n  /**\n   * Copy c'tor\n   */\n  template <std::size_t Size, typename = std::enable_if_t<(Size <= max_len)>>\n  short_string(const short_string<Size> &other) : len(other.len)\n  {\n    std::memcpy(buf, other.buf, other.len);\n  }\n\n  template <std::size_t Size, typename = std::enable_if_t<(Size <= max_len)>>\n  short_string &operator=(const short_string<Size> &other)\n  {\n    set(other.buf, other.len);\n    return *this;\n  }\n\n  template <std::size_t Size, typename = std::enable_if_t<(Size <= max_len)>> short_string &operator=(char const (&data)[Size])\n  {\n    // exclude null terminator if present\n    set(data, Size - static_cast<bool>(Size && !data[Size - 1]));\n    return *this;\n  }\n\n  short_string &operator=(std::string_view view)\n  {\n    set(view.data(), view.size());\n    return *this;\n  }\n\n  short_string &operator=(jb_blob view)\n  {\n    set(view.data(), view.size());\n    return *this;\n  }\n\n  bool operator==(const short_string<N> &rhs) const\n  {\n    if (rhs.len != len)\n      return false;\n    return (std::memcmp(buf, rhs.buf, len) == 0);\n  }\n\n  bool operator!=(const short_string<N> &rhs) const { return !operator==(rhs); }\n\n  operator std::array<char, max_len>() const\n  {\n    std::array<char, max_len> result = {};\n    std::copy(buf, buf + len, result.data());\n    return result;\n  }\n\n  operator std::array<unsigned char, max_len>() const\n  {\n    std::array<unsigned char, max_len> result = {};\n    std::copy(buf, buf + len, result.data());\n    return result;\n  }\n\n  static constexpr short_string truncate(std::string_view value)\n  {\n    return short_string{value.data(), std::min(value.size(), max_len)};\n  }\n\n  static constexpr short_string truncate(jb_blob const &value)\n  {\n    return short_string{value.buf, std::min(static_cast<std::size_t>(value.len), max_len)};\n  }\n\n  /* convert to string */\n  std::string to_string() const { return std::string(buf, len); }\n  constexpr std::string_view to_string_view() const { return {buf, len}; }\n  constexpr operator std::string_view() const { return to_string_view(); }\n\n  char buf[N] = {0};\n  uint16_t len = 0;\n\n  // duck typing as a string-like thing\n  //\n  uint16_t size() const { return len; }\n  bool empty() const { return !len; }\n\n  unsigned char const *udata() const { return reinterpret_cast<unsigned char const *>(buf); }\n  unsigned char *udata() { return reinterpret_cast<unsigned char *>(buf); }\n  char const *data() const { return buf; }\n  char *data() { return buf; }\n  char const *begin() const { return buf; }\n  char const *end() const { return buf + len; }\n\n  char operator[](std::size_t i) const\n  {\n    assert(i < len);\n    return buf[i];\n  }\n\n  char &operator[](std::size_t i)\n  {\n    assert(i < len);\n    return buf[i];\n  }\n\n  template <typename Out> friend Out &operator<<(Out &&out, short_string const &s)\n  {\n    out.write(s.data(), s.size());\n    return out;\n  }\n};\n\ntemplate <typename Array, typename = std::enable_if_t<std::is_array_v<Array>>, std::size_t Size = std::extent_v<Array>>\nshort_string<Size> to_short_string(std::string_view s)\n{\n  static_assert(Size > 0);\n  return {s};\n}\n\ntemplate <typename Char, std::size_t Size> short_string<Size> to_short_string(Char const (&data)[Size])\n{\n  static_assert(Size > 0);\n  static_assert(sizeof(Char) == sizeof(char));\n  return {data, Size};\n}\n\ntemplate <typename... Char> auto as_short_string(Char const... c)\n{\n  constexpr std::size_t size = sizeof...(Char);\n  static_assert(size > 0);\n  char buffer[size] = {static_cast<char>(c)...};\n  return to_short_string(buffer);\n}\n\ntemplate <std::size_t N>\ninline std::string_view format_as(short_string<N> const &s)\n{\n  return s.to_string_view();\n}\n"
  },
  {
    "path": "util/signal_handler.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/signal_handler.h>\n\n#include <util/log.h>\n#include <util/log_formatters.h>\n\n#include <cassert>\n#include <csignal>\n#include <cstdlib>\n#include <stdexcept>\n#include <sys/resource.h>\n\n// Minimal SignalManager implementation without Breakpad/minidump support.\n\nSignalManager::SignalManager(::uv_loop_t &loop, std::string_view /*product*/) : loop_(loop) {}\n\nvoid SignalManager::handle()\n{\n  // Disable core dumps to keep prior behavior consistent.\n  struct rlimit const core_dump_limit = {.rlim_cur = 0, .rlim_max = 0};\n  ::setrlimit(RLIMIT_CORE, &core_dump_limit);\n\n  // Ignore SIGPIPE as libuv docs recommend for I/O robustness.\n  ::signal(SIGPIPE, SIG_IGN);\n}\n\nvoid SignalManager::handle_signals(std::initializer_list<int> signal_numbers, std::function<void()> on_signal)\n{\n  for (auto const signal_number : signal_numbers) {\n    signals_.emplace_back(*this, on_signal, signal_number);\n  }\n}\n\nvoid SignalManager::clear()\n{\n  signals_.clear();\n}\n\n////////////////////\n// signal_handler //\n////////////////////\n\nvoid SignalManager::SignalHandler::signal_handler(uv_signal_t *handle, int signal_number)\n{\n  auto &handler = *reinterpret_cast<SignalManager::SignalHandler *>(handle->data);\n  assert(signal_number == handler.signal_number());\n  handler.on_signal();\n}\n\nSignalManager::SignalHandler::SignalHandler(SignalManager &manager, std::function<void()> on_signal, int signal_number)\n    : manager_(manager), on_signal_(std::move(on_signal))\n{\n  if (auto const error = ::uv_signal_init(&manager_.loop(), &handler_)) {\n    throw std::runtime_error(fmt::format(\"Could not init handler for signal {}\", signal_number));\n  }\n\n  handler_.data = this;\n\n  if (auto const error = ::uv_signal_start(&handler_, signal_handler, signal_number)) {\n    throw std::runtime_error(fmt::format(\"Could not start handler for signal {}\", signal_number));\n  }\n}\n\nstatic void signal_handle_close_cb(uv_handle_t * /*handle*/)\n{\n  LOG::debug(\"Closed a signal handler handle\");\n}\n\nSignalManager::SignalHandler::~SignalHandler()\n{\n  ::uv_signal_stop(&handler_);\n  ::uv_close(reinterpret_cast<uv_handle_t *>(&handler_), signal_handle_close_cb);\n}\n\nvoid SignalManager::SignalHandler::on_signal()\n{\n  LOG::info(\"Caught signal {}\", handler_.signum);\n\n  // call on_signal callback\n  if (on_signal_) {\n    on_signal_();\n  }\n\n  manager_.clear();\n\n  ::exit(-handler_.signum);\n}\n"
  },
  {
    "path": "util/signal_handler.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <uv.h>\n\n#include <functional>\n#include <memory>\n#include <string_view>\n#include <vector>\n#include <list>\n\nstruct SignalManager {\n  explicit SignalManager(::uv_loop_t &loop, std::string_view product);\n\n  void handle();\n\n  void handle_signals(std::initializer_list<int> signal_numbers, std::function<void()> on_signal = {});\n\n  void clear();\n\nprivate:\n  ::uv_loop_t &loop() { return loop_; }\n\n  ::uv_loop_t &loop_;\n\n  struct SignalHandler {\n    SignalHandler(SignalManager &manager, std::function<void()> on_signal, int signal_number);\n\n    ~SignalHandler();\n\n    void on_signal();\n\n    int signal_number() const { return handler_.signum; }\n\n    static void signal_handler(uv_signal_t *handle, int signal_number);\n\n  private:\n    SignalManager &manager_;\n    std::function<void()> on_signal_;\n    uv_signal_t handler_;\n  };\n\n  std::list<SignalHandler> signals_;\n};\n"
  },
  {
    "path": "util/stop_watch.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/log_formatters.h>\n#include <util/time.h>\n\n#include <chrono>\n\ntemplate <typename Clock = ::monotonic_clock> class StopWatch {\n  using clock = Clock;\n  using time_point = typename clock::time_point;\n\n  static_assert(clock::is_steady, \"stop watch needs a monotonic clock\");\n\npublic:\n  using duration = typename clock::duration;\n\n  StopWatch() : start_(clock::now()) {}\n\n  /**\n   * Resets the stop watch.\n   */\n  void reset() { start_ = clock::now(); }\n\n  /**\n   * Computes elapsed time since start to now without resetting the stop watch.\n   *\n   * Returns the result in `Unit` time unit. Default time unit is unspecified.\n   */\n  template <typename Unit = duration> Unit elapsed() const { return std::chrono::duration_cast<Unit>(clock::now() - start_); }\n\n  /**\n   * Computes elapsed time in nanoseconds since start to now without resetting the stop watch.\n   */\n  u64 elapsed_ns() const { return integer_time<std::chrono::nanoseconds>(elapsed<std::chrono::nanoseconds>()); }\n\n  /**\n   * Tells whether the given duration has already been elapsed since start.\n   */\n  template <typename Duration> bool elapsed(Duration duration) const { return duration <= (clock::now() - start_); }\n\n  /**\n   * Computes elapsed time since start and resets the stop watch.\n   *\n   * Returns the result in `Unit` time unit. Default time unit is unspecified.\n   */\n  template <typename Unit = duration> Unit elapsed_reset()\n  {\n    auto const now = clock::now();\n    auto const result = now - start_;\n    start_ = now;\n    return std::chrono::duration_cast<Unit>(result);\n  }\n\n  template <typename Out> friend Out &&operator<<(Out &&out, StopWatch const &stop_watch)\n  {\n    out << stop_watch.elapsed();\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  time_point start_;\n};\n"
  },
  {
    "path": "util/string.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <string>\n#include <string_view>\n\ninline std::string_view last_token(std::string_view s, char delimiter)\n{\n  auto const i = s.rfind(delimiter);\n  return i == std::string_view::npos ? s : s.substr(i + 1);\n}\n\n/**\n * Converts the string `in` to type `T`.\n *\n * Returns the converted value on success.\n * On failure, returns `fallback`.\n */\ntemplate <typename T> T try_from_string(char const *in, T fallback = {});\n\n/**\n * Converts the null-terminated string `in` to integer type `T` and stores the\n * result in `out`.\n *\n * Returns `true` on success.\n * On failure, returns `false` and `out` is untouched.\n */\ntemplate <typename T> bool integer_from_string(char const *in, T &out);\n\n/**\n * Converts the null-terminated string `in` to floating point type `T` and\n * stores the result in `out`.\n *\n * Returns `true` on success.\n * On failure, returns `false` and `out` is untouched.\n */\ntemplate <typename T> bool floating_point_from_string(char const *in, T &out);\n\n/**\n * Converts the string `in` to integer type `T`.\n *\n * Returns the converted value on success.\n * On failure, returns `fallback`.\n */\n\ntemplate <typename T> T try_integer_from_string(char const *in, T fallback = {});\n/**\n * Converts the string `in` to floating point type `T`.\n *\n * Returns the converted value on success.\n * On failure, returns `fallback`.\n */\ntemplate <typename T> T try_floating_point_from_string(char const *in, T fallback = {});\n\n#include <util/string.inl>\n"
  },
  {
    "path": "util/string.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <type_traits>\n\n#include <cerrno>\n#include <cstdint>\n\ntemplate <typename T> bool integer_from_string(char const *in, T &out)\n{\n  using type = std::decay_t<T>;\n\n  if constexpr (std::is_enum_v<type>) {\n    if (std::underlying_type_t<type> underlying = {}; integer_from_string<std::underlying_type_t<type>>(in, underlying)) {\n      out = static_cast<type>(underlying);\n      return true;\n    } else {\n      return false;\n    }\n  } else {\n    // because C APIs suck regarding constness\n    char *end = const_cast<char *>(in);\n    errno = 0;\n\n    if constexpr (std::is_same_v<type, bool>) {\n      std::string_view s{in};\n      if (s == \"0\" || s == \"false\" || s == \"False\" || s == \"FALSE\" || s == \"no\" || s == \"No\" || s == \"NO\") {\n        out = false;\n      } else if (s == \"1\" || s == \"true\" || s == \"True\" || s == \"TRUE\" || s == \"yes\" || s == \"Yes\" || s == \"YES\") {\n        out = true;\n      } else {\n        return false;\n      }\n\n      return true;\n    } else if constexpr (\n        std::is_same_v<type, char> || std::is_same_v<type, std::int8_t> || std::is_same_v<type, short> ||\n        std::is_same_v<type, int> || std::is_same_v<type, long>) {\n      auto const result = std::strtol(in, &end, 10);\n      if (in == end || errno == ERANGE) {\n        return false;\n      }\n\n      if constexpr (!std::is_same_v<type, long>) {\n        if (result < static_cast<long>(std::numeric_limits<type>::min()) ||\n            result > static_cast<long>(std::numeric_limits<type>::max())) {\n          return false;\n        }\n      }\n\n      out = static_cast<type>(result);\n      return true;\n    } else if constexpr (\n        std::is_same_v<type, unsigned char> || std::is_same_v<type, std::uint8_t> || std::is_same_v<type, unsigned short> ||\n        std::is_same_v<type, unsigned int> || std::is_same_v<type, unsigned long>) {\n      auto const result = std::strtoul(in, &end, 10);\n      if (in == end || errno == ERANGE) {\n        return false;\n      }\n\n      if constexpr (!std::is_same_v<type, unsigned long>) {\n        if (result > static_cast<unsigned long>(std::numeric_limits<type>::max())) {\n          return false;\n        }\n      }\n\n      out = static_cast<type>(result);\n      return true;\n    } else if constexpr (std::is_same_v<type, long long>) {\n      auto const result = std::strtoll(in, &end, 10);\n      if (in == end || errno == ERANGE) {\n        return false;\n      }\n      out = result;\n      return true;\n    } else if constexpr (std::is_same_v<type, unsigned long long>) {\n      auto const result = std::strtoull(in, &end, 10);\n      if (in == end || errno == ERANGE) {\n        return false;\n      }\n      out = result;\n      return true;\n    } else {\n      static_assert(\n          // this big list is needed because gcc is not smart enough to realize\n          // that the static assert should only be evaluated if all other\n          // conditionals failed\n          std::is_same_v<type, bool> || std::is_same_v<type, char> || std::is_same_v<type, std::int8_t> ||\n              std::is_same_v<type, short> || std::is_same_v<type, int> || std::is_same_v<type, long> ||\n              std::is_same_v<type, unsigned char> || std::is_same_v<type, unsigned short> ||\n              std::is_same_v<type, unsigned int> || std::is_same_v<type, unsigned long> || std::is_same_v<type, long long> ||\n              std::is_same_v<type, unsigned long long>,\n          \"integer_from_string: unsupported type\");\n    }\n  }\n}\n\ntemplate <typename T> T try_integer_from_string(char const *in, T fallback)\n{\n  integer_from_string(in, fallback);\n  return fallback;\n}\n\ntemplate <typename T> bool floating_point_from_string(char const *in, T &out)\n{\n  using type = std::decay_t<T>;\n\n  // because C APIs suck regarding constness\n  char *end = const_cast<char *>(in);\n  errno = 0;\n\n  if constexpr (std::is_same_v<type, float>) {\n    auto const result = std::strtof(in, &end);\n    if (in == end || errno == ERANGE) {\n      return false;\n    }\n    out = result;\n    return true;\n  } else if constexpr (std::is_same_v<type, double>) {\n    auto const result = std::strtod(in, &end);\n    if (in == end || errno == ERANGE) {\n      return false;\n    }\n    out = result;\n    return true;\n  } else if constexpr (std::is_same_v<type, long double>) {\n    auto const result = std::strtold(in, &end);\n    if (in == end || errno == ERANGE) {\n      return false;\n    }\n    out = result;\n    return true;\n  } else {\n    static_assert(\n        // this big list is needed because gcc is not smart enough to realize\n        // that the static assert should only be evaluated if all other\n        // conditionals failed\n        std::is_same_v<type, float> || std::is_same_v<type, double> || std::is_same_v<type, long double>,\n        \"floating_point_from_string: unsupported type\");\n    return false;\n  }\n}\n\ntemplate <typename T> T try_floating_point_from_string(char const *in, T fallback)\n{\n  floating_point_from_string(in, fallback);\n  return fallback;\n}\n\ntemplate <typename T> bool from_string(char const *in, T &out)\n{\n  if constexpr (std::is_integral_v<T>) {\n    return integer_from_string(in, out);\n  } else if constexpr (std::is_floating_point_v<T>) {\n    return floating_point_from_string(in, out);\n  } else if constexpr (std::is_enum_v<T>) {\n    return enum_from_string(in, out);\n  } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {\n    out = in;\n    return true;\n  } else {\n    static_assert(\n        // this big list is needed because gcc is not smart enough to realize\n        // that the static assert should only be evaluated if all other\n        // conditionals failed\n        std::is_integral_v<T> || std::is_floating_point_v<T> || std::is_enum_v<T>,\n        \"from_string: unsupported type\");\n  }\n}\n\ntemplate <typename T> T try_from_string(char const *in, T fallback)\n{\n  from_string(in, fallback);\n  return fallback;\n}\n"
  },
  {
    "path": "util/string_view.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/string.h>\n\n#include <string_view>\n#include <type_traits>\n\nnamespace views {\n\nconstexpr std::string_view EOL = \"\\n\\r\";\nconstexpr std::string_view WHITESPACE = \" \\n\\t\\r\";\nconstexpr std::string_view NON_EOL_WHITESPACE = \" \\t\";\n\n/**\n * Behavior for functions that perform string lookups with delimiters.\n */\nenum class SeekBehavior : std::uint8_t {\n  /**\n   * Exclude delimiter/needle from result and leave it in the haystack.\n   */\n  EXCLUDE = 0b00,\n\n  /**\n   * Exclude delimiter/needle from result but consume it from the haystack.\n   */\n  CONSUME = 0b10,\n\n  /**\n   * Include delimiter/needle in result and remove it from the haystack.\n   */\n  INCLUDE = 0b11\n};\n\n/**\n * Count how many characters are in the beginning of `input` up until the first\n * occurence of a character not in `what`.\n *\n * Example:\n *\n *  // returns 5\n *  run_length(\"ABAABCADBEF\", \"AB\");\n *\n *  // returns 0\n *  run_length(\"ABAABCADBEF\", \"Z\");\n *\n *  // returns 6\n *  run_length(\"ABAABA\", \"AB\");\n */\nstd::size_t run_length(std::string_view input, std::string_view what);\n\n/**\n * Trims characters from the beginning of `input` up until the first occurence\n * of a character not in `what`. Returns a string view of the characters\n * trimmed.\n *\n * Example:\n *\n *  // returns \"ABAAB\", changes `input` to \"CADBEF\"\n *  trim_run(\"ABAABCADBEF\", \"AB\");\n *\n *  // returns \"\", leaves `input` unchanged\n *  trim_run(\"ABAABCADBEF\", \"Z\");\n *\n *  // returns \"ABAABA\", changes `input` to \"\"\n *  trim_run(\"ABAABA\", \"AB\");\n */\nstd::string_view trim_run(std::string_view &input, std::string_view what);\n\n/**\n * Count how many characters are in the beginning of `input` up until the first\n * occurence of a character in `what`.\n *\n * `include` tells whether the occurence should be included in the counting\n * or not.\n *\n * Example:\n *\n *  // returns 5\n *  count_up_to(\"ABAABCADBEF\", \"CDEF\", false);\n *\n *  // returns 0\n *  count_up_to(\"ABAABCADBEF\", \"AB\", false);\n *\n *  // returns 6\n *  count_up_to(\"ABAABA\", \"Z\", false);\n */\nstd::size_t count_up_to(std::string_view input, char what, bool include);\nstd::size_t count_up_to(std::string_view input, std::string_view what, bool include);\n\n/**\n * Trims characters from the beginning of `input` up until the first occurence\n * of a character in `what`. Returns a string view of the characters trimmed.\n *\n * `behavior` tells whether the occurence should be included in the trimming\n * or not.\n *\n * `what` can be either a character or a string / string view.\n *\n * Example:\n *\n *  // returns \"ABAAB\", changes `input` to \"CADBEF\"\n *  trim_up_to(\"ABAABCADBEF\", \"CDEF\", SeekBehavior::EXCLUDE);\n *\n *  // returns \"\", leaves `input` unchanged\n *  trim_up_to(\"ABAABCADBEF\", \"AB\", SeekBehavior::EXCLUDE);\n *\n *  // returns \"ABAABA\", changes `input` to \"\"\n *  trim_up_to(\"ABAABA\", \"Z\", SeekBehavior::EXCLUDE);\n */\nstd::string_view trim_up_to(std::string_view &input, char what, SeekBehavior behavior);\nstd::string_view trim_up_to(std::string_view &input, std::string_view what, SeekBehavior behavior);\n\n/**\n * Count how many characters are in the beginning of `input` up until the last\n * occurence of a character in `what`.\n *\n * `include` tells whether the occurence should be included in the counting\n * or not.\n *\n * Returns 0 if no occurences are found.\n *\n * Example:\n *\n *  // returns 11\n *  count_up_to_last(\"ABAABCADBEF\", \"CDEF\", true);\n *\n *  // returns 9\n *  count_up_to_last(\"ABAABCADBEF\", \"AB\", true);\n *\n *  // returns 0\n *  count_up_to_last(\"ABAABA\", \"Z\", true);\n */\nstd::size_t count_up_to_last(std::string_view input, char what, bool include);\nstd::size_t count_up_to_last(std::string_view input, std::string_view what, bool include);\n\n/**\n * Trims characters from the beginning of `input` up until the last occurence\n * of a character in `what`. Returns a string view of the characters trimmed.\n *\n * `behavior` tells whether the occurence should be included in the trimming\n * or not.\n *\n * Trims nothing if no occurences are found.\n *\n * `what` can be either a character or a string / string view.\n *\n * Example:\n *\n *  // returns \"ABAABCADBEF\", changes `input` to \"\"\n *  trim_up_to_last(\"ABAABCADBEF\", \"CDEF\", SeekBehavior::EXCLUDE);\n *\n *  // returns \"ABAABCADB\", changes `input` to \"EF\"\n *  trim_up_to_last(\"ABAABCADBEF\", \"AB\", SeekBehavior::EXCLUDE);\n *\n *  // returns \"\", leaves `input` unchanged\n *  trim_up_to_last(\"ABAABA\", \"Z\", SeekBehavior::EXCLUDE);\n */\nstd::string_view trim_up_to_last(std::string_view &input, char what, SeekBehavior behavior);\nstd::string_view trim_up_to_last(std::string_view &input, std::string_view what, SeekBehavior behavior);\n\n/**\n * Returns the last `size` characters from the string view.\n *\n * If the input is smaller than size, returns the full input.\n */\nstd::string_view slice_suffix(std::string_view view, std::size_t size);\n\n/**\n * Returns whether the view ends with the specified suffix.\n */\nbool ends_with(std::string_view view, std::string_view suffix);\n\n/**\n * Returns whether the view starts with the specified prefix.\n */\nbool starts_with(std::string_view view, std::string_view prefix);\n\n/**\n * Convenience function to remove leading whitespace.\n */\nstd::string_view ltrim_ws(std::string_view s);\n\n/**\n * Convenience function to remove trailing whitespace.\n */\nstd::string_view rtrim_ws(std::string_view s);\n\n/**\n * Convenience function to remove both leading and trailing\n * whitespace.\n */\nstd::string_view trim_ws(std::string_view s);\n\n/**\n * A helper to lazily convert a string view to a number.\n *\n * `T` must be either an integer or a floating point type.\n *\n * Note: due to the semantics of the underlying parsing functions being used\n * from the standard library, the string must either be null terminated or the\n * number being parsed must be followed by an invalid character (e.g.: a blank\n * space).\n *\n * Example:\n *\n *  std::string_view str = some_fn();\n *\n *  NumberView<int> view{str};\n *\n *  // parses the string view, return 0 on error\n *  int value = view.value(0);\n *\n *  std::string_view str2 = some_other_fn();\n *  view = str2;\n *\n *  // parses the other string view, return 5 on error\n *  int value2 = view.value(5);\n */\ntemplate <typename T> struct NumberView {\n  constexpr NumberView() = default;\n\n  constexpr explicit NumberView(std::string_view view) : view_(view) {}\n\n  static_assert(std::is_arithmetic_v<T> || std::is_enum_v<T>);\n\n  /**\n   * Parses the string view and returns the result, or `fallback` on error.\n   *\n   * NOTE: the underlying string view must be padded with '\\0' or another\n   * non-numerical character due to limitations in the standard library's\n   * number parsing routines.\n   */\n  constexpr T value(T fallback = {}) const;\n\n  /**\n   * Parses the string view and returns the result, or `fallback` on error.\n   *\n   * This is a slightly less performant alternative to `value()`, but it is\n   * guaranteed to work on unpadded string views.\n   */\n  constexpr T unpadded_value(T fallback = {}) const;\n\n  NumberView &operator=(std::string_view view)\n  {\n    view_ = view;\n    return *this;\n  }\n\n  constexpr std::string_view view() const { return view_; }\n\n  bool empty() const { return view_.empty(); }\n\n  explicit constexpr operator T() const { return unpadded_value(); }\n\n  constexpr bool operator==(T rhs) const { return static_cast<T>(*this) == rhs; }\n  constexpr bool operator!=(T rhs) const { return static_cast<T>(*this) != rhs; }\n  constexpr bool operator<(T rhs) const { return static_cast<T>(*this) < rhs; }\n  constexpr bool operator<=(T rhs) const { return static_cast<T>(*this) <= rhs; }\n  constexpr bool operator>(T rhs) const { return static_cast<T>(*this) > rhs; }\n  constexpr bool operator>=(T rhs) const { return static_cast<T>(*this) >= rhs; }\n\n  friend constexpr bool operator==(T lhs, NumberView rhs) { return lhs == static_cast<T>(rhs); }\n  friend constexpr bool operator!=(T lhs, NumberView rhs) { return lhs != static_cast<T>(rhs); }\n  friend constexpr bool operator<(T lhs, NumberView rhs) { return lhs < static_cast<T>(rhs); }\n  friend constexpr bool operator<=(T lhs, NumberView rhs) { return lhs <= static_cast<T>(rhs); }\n  friend constexpr bool operator>(T lhs, NumberView rhs) { return lhs > static_cast<T>(rhs); }\n  friend constexpr bool operator>=(T lhs, NumberView rhs) { return lhs >= static_cast<T>(rhs); }\n\n  constexpr explicit operator bool() const { return !empty(); }\n  constexpr bool operator!() const { return empty(); }\n\nprivate:\n  std::string_view view_;\n};\n\n} // namespace views\n\n#include <util/string_view.inl>\n"
  },
  {
    "path": "util/string_view.inl",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <algorithm>\n#include <cassert>\n#include <climits>\n\nnamespace views {\n\ninline std::size_t run_length(std::string_view input, std::string_view what)\n{\n  auto const index = input.find_first_not_of(what);\n  return index == std::string_view::npos ? input.size() : index;\n}\n\ninline std::string_view trim_run(std::string_view &input, std::string_view what)\n{\n  auto const count = run_length(input, what);\n  assert(count <= input.size());\n  auto result = input.substr(0, count);\n  input.remove_prefix(count);\n  return result;\n}\n\ninline std::size_t count_up_to(std::string_view input, char what, bool include)\n{\n  auto const index = input.find(what);\n  return index == std::string_view::npos ? input.size() : index + include;\n}\n\ninline std::size_t count_up_to(std::string_view input, std::string_view what, bool include)\n{\n  auto const index = input.find_first_of(what);\n  return index == std::string_view::npos ? input.size() : index + include;\n}\n\ninline std::string_view trim_up_to(std::string_view &input, char what, SeekBehavior behavior)\n{\n  auto const index = input.find(what);\n  if (index == std::string_view::npos) {\n    auto result = input;\n    input = {};\n    return result;\n  } else {\n    assert(index < input.size());\n\n    auto const result_count = index + (static_cast<std::uint8_t>(behavior) & 0b01);\n    assert(result_count <= input.size());\n    auto result = input.substr(0, result_count);\n\n    auto const remove_count = index + ((static_cast<std::uint8_t>(behavior) & 0b10) >> 1);\n    assert(remove_count <= input.size());\n    input.remove_prefix(remove_count);\n\n    return result;\n  }\n}\n\ninline std::string_view trim_up_to(std::string_view &input, std::string_view what, SeekBehavior behavior)\n{\n  auto const index = input.find_first_of(what);\n  if (index == std::string_view::npos) {\n    auto result = input;\n    input = {};\n    return result;\n  } else {\n    assert(index < input.size());\n\n    auto const result_count = index + (static_cast<std::uint8_t>(behavior) & 0b01);\n    assert(result_count <= input.size());\n    auto result = input.substr(0, result_count);\n\n    auto const remove_count = index + ((static_cast<std::uint8_t>(behavior) & 0b10) >> 1);\n    assert(remove_count <= input.size());\n    input.remove_prefix(remove_count);\n\n    return result;\n  }\n}\n\ninline std::size_t count_up_to_last(std::string_view input, char what, bool include)\n{\n  auto const index = input.rfind(what);\n  assert(index == std::string_view::npos || index < input.size());\n  return index == std::string_view::npos ? 0 : index + include;\n}\n\ninline std::size_t count_up_to_last(std::string_view input, std::string_view what, bool include)\n{\n  auto const index = input.find_last_of(what);\n  assert(index == std::string_view::npos || index < input.size());\n  return index == std::string_view::npos ? 0 : index + include;\n}\n\ninline std::string_view trim_up_to_last(std::string_view &input, char what, SeekBehavior behavior)\n{\n  auto const index = input.rfind(what);\n  if (index == std::string_view::npos) {\n    return std::string_view{};\n  } else {\n    assert(index < input.size());\n\n    auto const result_count = index + (static_cast<std::uint8_t>(behavior) & 0b01);\n    assert(result_count <= input.size());\n    auto result = input.substr(0, result_count);\n\n    auto const remove_count = index + ((static_cast<std::uint8_t>(behavior) & 0b10) >> 1);\n    assert(remove_count <= input.size());\n    input.remove_prefix(remove_count);\n\n    return result;\n  }\n}\n\ninline std::string_view trim_up_to_last(std::string_view &input, std::string_view what, SeekBehavior behavior)\n{\n  auto const index = input.find_last_of(what);\n  if (index == std::string_view::npos) {\n    return std::string_view{};\n  } else {\n    assert(index < input.size());\n\n    auto const result_count = index + (static_cast<std::uint8_t>(behavior) & 0b01);\n    assert(result_count <= input.size());\n    auto result = input.substr(0, result_count);\n\n    auto const remove_count = index + ((static_cast<std::uint8_t>(behavior) & 0b10) >> 1);\n    assert(remove_count <= input.size());\n    input.remove_prefix(remove_count);\n\n    return result;\n  }\n}\n\ninline std::string_view slice_suffix(std::string_view view, std::size_t size)\n{\n  if (view.size() > size) {\n    view.remove_prefix(view.size() - size);\n  }\n\n  return view;\n}\n\ninline bool starts_with(std::string_view view, std::string_view prefix)\n{\n  // substr handles the case when the view.length() < prefix.length() gracefully\n  return view.substr(0, prefix.length()) == prefix;\n}\n\ninline bool ends_with(std::string_view view, std::string_view suffix)\n{\n  if (view.length() < suffix.length()) {\n    return false;\n  }\n\n  return view.substr(view.length() - suffix.length()) == suffix;\n}\n\ninline std::string_view ltrim_ws(std::string_view s)\n{\n  s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(), [](int c) { return !std::isspace(c); })));\n\n  return s;\n}\n\ninline std::string_view rtrim_ws(std::string_view s)\n{\n  s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(), [](int c) { return !std::isspace(c); })));\n\n  return s;\n}\n\ninline std::string_view trim_ws(std::string_view s)\n{\n  return ltrim_ws(rtrim_ws(s));\n}\n\ntemplate <typename T> constexpr T NumberView<T>::value(T fallback) const\n{\n  if constexpr (std::is_integral_v<T> || std::is_enum_v<T>) {\n    integer_from_string(view_.data(), fallback);\n  } else {\n    floating_point_from_string(view_.data(), fallback);\n  }\n  return fallback;\n}\n\ntemplate <typename T> constexpr T NumberView<T>::unpadded_value(T fallback) const\n{\n  // correct size is `ceil(log_10(bit-width)) + 1`, but doing that math a\n  // runtime defeats the purpose of a fast number view facility, and doing it\n  // at compile time requires too much stuff to be implemented to be worth it\n  constexpr std::size_t buffer_size = sizeof(T) * CHAR_BIT + 1;\n  static_assert(std::is_integral_v<T> || std::is_enum_v<T>, \"TODO: compute correct buffer size for floating point parsing\");\n\n  char buffer[buffer_size] = {0};\n  auto const end = std::min(buffer_size - 1, view_.size());\n  view_.copy(buffer, end);\n\n  if constexpr (std::is_integral_v<T> || std::is_enum_v<T>) {\n    integer_from_string(buffer, fallback);\n  } else {\n    floating_point_from_string(buffer, fallback);\n  }\n\n  return fallback;\n}\n\n} // namespace views\n"
  },
  {
    "path": "util/string_view_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <gtest/gtest.h>\n\n#include <platform/types.h>\n#include <util/string_view.h>\n\nnamespace views {\n\nTEST(string_view, run_length)\n{\n  EXPECT_EQ(5u, run_length(\"ABAABCADBEF\", \"AB\"));\n  EXPECT_EQ(0u, run_length(\"ABAABCADBEF\", \"Z\"));\n  EXPECT_EQ(6u, run_length(\"ABAABA\", \"AB\"));\n}\n\nTEST(string_view, trim_run)\n{\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAAB\", trim_run(data, \"AB\"));\n    EXPECT_EQ(data, \"CADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"\", trim_run(data, \"Z\"));\n    EXPECT_EQ(data, \"ABAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_run(data, \"AB\"));\n    EXPECT_EQ(data, \"\");\n  }\n}\n\nTEST(string_view, count_up_to_char)\n{\n  EXPECT_EQ(5u, count_up_to(\"ABAABCADBEF\", 'C', false));\n  EXPECT_EQ(0u, count_up_to(\"ABAABCADBEF\", 'A', false));\n  EXPECT_EQ(6u, count_up_to(\"ABAABA\", 'Z', false));\n\n  EXPECT_EQ(6u, count_up_to(\"ABAABCADBEF\", 'C', true));\n  EXPECT_EQ(1u, count_up_to(\"ABAABCADBEF\", 'A', true));\n  EXPECT_EQ(6u, count_up_to(\"ABAABA\", 'Z', true));\n}\n\nTEST(string_view, count_up_to_string_view)\n{\n  EXPECT_EQ(5u, count_up_to(\"ABAABCADBEF\", \"CDEF\", false));\n  EXPECT_EQ(0u, count_up_to(\"ABAABCADBEF\", \"AB\", false));\n  EXPECT_EQ(6u, count_up_to(\"ABAABA\", \"Z\", false));\n\n  EXPECT_EQ(6u, count_up_to(\"ABAABCADBEF\", \"CDEF\", true));\n  EXPECT_EQ(1u, count_up_to(\"ABAABCADBEF\", \"AB\", true));\n  EXPECT_EQ(6u, count_up_to(\"ABAABA\", \"Z\", true));\n}\n\nTEST(string_view, trim_up_to_char)\n{\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAAB\", trim_up_to(data, 'C', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"CADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"\", trim_up_to(data, 'A', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"ABAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, 'Z', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAAB\", trim_up_to(data, 'C', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"ADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"\", trim_up_to(data, 'A', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"BAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, 'Z', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABC\", trim_up_to(data, 'C', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"ADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"A\", trim_up_to(data, 'A', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"BAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, 'Z', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n}\n\nTEST(string_view, trim_up_to_string_view)\n{\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAAB\", trim_up_to(data, \"CDEF\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"CADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"\", trim_up_to(data, \"AB\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"ABAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, \"Z\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAAB\", trim_up_to(data, \"CDEF\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"ADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"\", trim_up_to(data, \"AB\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"BAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, \"Z\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABC\", trim_up_to(data, \"CDEF\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"ADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"A\", trim_up_to(data, \"AB\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"BAABCADBEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"ABAABA\", trim_up_to(data, \"Z\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n}\n\nTEST(string_view, count_up_to_last_char)\n{\n  EXPECT_EQ(10u, count_up_to_last(\"ABAABCADBEF\", 'F', false));\n  EXPECT_EQ(8u, count_up_to_last(\"ABAABCADBEF\", 'B', false));\n  EXPECT_EQ(0u, count_up_to_last(\"ABAABA\", 'Z', false));\n\n  EXPECT_EQ(11u, count_up_to_last(\"ABAABCADBEF\", 'F', true));\n  EXPECT_EQ(9u, count_up_to_last(\"ABAABCADBEF\", 'B', true));\n  EXPECT_EQ(0u, count_up_to_last(\"ABAABA\", 'Z', true));\n}\n\nTEST(string_view, count_up_to_last_string_view)\n{\n  EXPECT_EQ(10u, count_up_to_last(\"ABAABCADBEF\", \"CDEF\", false));\n  EXPECT_EQ(8u, count_up_to_last(\"ABAABCADBEF\", \"AB\", false));\n  EXPECT_EQ(0u, count_up_to_last(\"ABAABA\", \"Z\", false));\n\n  EXPECT_EQ(11u, count_up_to_last(\"ABAABCADBEF\", \"CDEF\", true));\n  EXPECT_EQ(9u, count_up_to_last(\"ABAABCADBEF\", \"AB\", true));\n  EXPECT_EQ(0u, count_up_to_last(\"ABAABA\", \"Z\", true));\n}\n\nTEST(string_view, trim_up_to_last_char)\n{\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBE\", trim_up_to_last(data, 'F', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"F\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCAD\", trim_up_to_last(data, 'B', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"BEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, 'Z', SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBE\", trim_up_to_last(data, 'F', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCAD\", trim_up_to_last(data, 'B', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"EF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, 'Z', SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBEF\", trim_up_to_last(data, 'F', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADB\", trim_up_to_last(data, 'B', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"EF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, 'Z', SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n}\n\nTEST(string_view, trim_up_to_last_string_view)\n{\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBE\", trim_up_to_last(data, \"CDEF\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"F\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCAD\", trim_up_to_last(data, \"AB\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"BEF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, \"Z\", SeekBehavior::EXCLUDE));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBE\", trim_up_to_last(data, \"CDEF\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCAD\", trim_up_to_last(data, \"AB\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"EF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, \"Z\", SeekBehavior::CONSUME));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADBEF\", trim_up_to_last(data, \"CDEF\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"\");\n  }\n\n  {\n    std::string_view data = \"ABAABCADBEF\";\n    EXPECT_EQ(\"ABAABCADB\", trim_up_to_last(data, \"AB\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"EF\");\n  }\n\n  {\n    std::string_view data = \"ABAABA\";\n    EXPECT_EQ(\"\", trim_up_to_last(data, \"Z\", SeekBehavior::INCLUDE));\n    EXPECT_EQ(data, \"ABAABA\");\n  }\n}\n\nTEST(NumberView, integer_u8)\n{\n  EXPECT_EQ(0, NumberView<u8>(\"0\").value());\n  EXPECT_EQ(1, NumberView<u8>(\"1\").value());\n  EXPECT_EQ(10, NumberView<u8>(\"10\").value());\n  EXPECT_EQ(200, NumberView<u8>(\"200\").value());\n  EXPECT_EQ(255, NumberView<u8>(\"255\").value());\n}\n\nTEST(NumberView, integer_s8)\n{\n  EXPECT_EQ(0, NumberView<s8>(\"0\").value());\n  EXPECT_EQ(1, NumberView<s8>(\"1\").value());\n  EXPECT_EQ(10, NumberView<s8>(\"10\").value());\n  EXPECT_EQ(-100, NumberView<s8>(\"-100\").value());\n  EXPECT_EQ(100, NumberView<s8>(\"100\").value());\n  EXPECT_EQ(100, NumberView<s8>(\"+100\").value());\n  EXPECT_EQ(127, NumberView<s8>(\"127\").value());\n  EXPECT_EQ(-128, NumberView<s8>(\"-128\").value());\n}\n\nTEST(NumberView, integer_u16)\n{\n  EXPECT_EQ(0, NumberView<u16>(\"0\").value());\n  EXPECT_EQ(1, NumberView<u16>(\"1\").value());\n  EXPECT_EQ(10, NumberView<u16>(\"10\").value());\n  EXPECT_EQ(200, NumberView<u16>(\"200\").value());\n  EXPECT_EQ(255, NumberView<u16>(\"255\").value());\n  EXPECT_EQ(10000, NumberView<u16>(\"10000\").value());\n  EXPECT_EQ(1000, NumberView<u16>(\"+1000\").value());\n  EXPECT_EQ(31127, NumberView<u16>(\"31127\").value());\n  EXPECT_EQ(61128, NumberView<u16>(\"61128\").value());\n}\n\nTEST(NumberView, integer_s16)\n{\n  EXPECT_EQ(0, NumberView<s16>(\"0\").value());\n  EXPECT_EQ(1, NumberView<s16>(\"1\").value());\n  EXPECT_EQ(10, NumberView<s16>(\"10\").value());\n  EXPECT_EQ(-100, NumberView<s16>(\"-100\").value());\n  EXPECT_EQ(100, NumberView<s16>(\"100\").value());\n  EXPECT_EQ(100, NumberView<s16>(\"+100\").value());\n  EXPECT_EQ(127, NumberView<s16>(\"127\").value());\n  EXPECT_EQ(-128, NumberView<s16>(\"-128\").value());\n  EXPECT_EQ(-1000, NumberView<s16>(\"-1000\").value());\n  EXPECT_EQ(10000, NumberView<s16>(\"10000\").value());\n  EXPECT_EQ(1000, NumberView<s16>(\"+1000\").value());\n  EXPECT_EQ(31127, NumberView<s16>(\"31127\").value());\n  EXPECT_EQ(-29128, NumberView<s16>(\"-29128\").value());\n}\n\nTEST(NumberView, integer_u32)\n{\n  EXPECT_EQ(0u, NumberView<u32>(\"0\").value());\n  EXPECT_EQ(1u, NumberView<u32>(\"1\").value());\n  EXPECT_EQ(10u, NumberView<u32>(\"10\").value());\n  EXPECT_EQ(200u, NumberView<u32>(\"200\").value());\n  EXPECT_EQ(255u, NumberView<u32>(\"255\").value());\n  EXPECT_EQ(10000u, NumberView<u32>(\"10000\").value());\n  EXPECT_EQ(1000u, NumberView<u32>(\"+1000\").value());\n  EXPECT_EQ(31127u, NumberView<u32>(\"31127\").value());\n  EXPECT_EQ(61128u, NumberView<u32>(\"61128\").value());\n  EXPECT_EQ(+751000u, NumberView<u32>(\"+751000\").value());\n  EXPECT_EQ(81000u, NumberView<u32>(\"+81000\").value());\n  EXPECT_EQ(9931127u, NumberView<u32>(\"9931127\").value());\n  EXPECT_EQ(+19229128u, NumberView<u32>(\"+19229128\").value());\n}\n\nTEST(NumberView, integer_s32)\n{\n  EXPECT_EQ(0, NumberView<s32>(\"0\").value());\n  EXPECT_EQ(1, NumberView<s32>(\"1\").value());\n  EXPECT_EQ(10, NumberView<s32>(\"10\").value());\n  EXPECT_EQ(-100, NumberView<s32>(\"-100\").value());\n  EXPECT_EQ(100, NumberView<s32>(\"100\").value());\n  EXPECT_EQ(100, NumberView<s32>(\"+100\").value());\n  EXPECT_EQ(127, NumberView<s32>(\"127\").value());\n  EXPECT_EQ(-128, NumberView<s32>(\"-128\").value());\n  EXPECT_EQ(-751000, NumberView<s32>(\"-751000\").value());\n  EXPECT_EQ(10000, NumberView<s32>(\"10000\").value());\n  EXPECT_EQ(81000, NumberView<s32>(\"+81000\").value());\n  EXPECT_EQ(9931127, NumberView<s32>(\"9931127\").value());\n  EXPECT_EQ(-19229128, NumberView<s32>(\"-19229128\").value());\n}\n\nTEST(NumberView, integer_u64)\n{\n  EXPECT_EQ(0ul, NumberView<u64>(\"0\").value());\n  EXPECT_EQ(1ul, NumberView<u64>(\"1\").value());\n  EXPECT_EQ(10ul, NumberView<u64>(\"10\").value());\n  EXPECT_EQ(200ul, NumberView<u64>(\"200\").value());\n  EXPECT_EQ(255ul, NumberView<u64>(\"255\").value());\n  EXPECT_EQ(10000ul, NumberView<u64>(\"10000\").value());\n  EXPECT_EQ(1000ul, NumberView<u64>(\"+1000\").value());\n  EXPECT_EQ(31127ul, NumberView<u64>(\"31127\").value());\n  EXPECT_EQ(61128ul, NumberView<u64>(\"61128\").value());\n  EXPECT_EQ(+751000ul, NumberView<u64>(\"+751000\").value());\n  EXPECT_EQ(81000ul, NumberView<u64>(\"+81000\").value());\n  EXPECT_EQ(9931127ul, NumberView<u64>(\"9931127\").value());\n  EXPECT_EQ(+19229128ul, NumberView<u64>(\"+19229128\").value());\n}\n\nTEST(NumberView, integer_s64)\n{\n  EXPECT_EQ(0l, NumberView<s64>(\"0\").value());\n  EXPECT_EQ(1l, NumberView<s64>(\"1\").value());\n  EXPECT_EQ(10l, NumberView<s64>(\"10\").value());\n  EXPECT_EQ(-100l, NumberView<s64>(\"-100\").value());\n  EXPECT_EQ(100l, NumberView<s64>(\"100\").value());\n  EXPECT_EQ(100l, NumberView<s64>(\"+100\").value());\n  EXPECT_EQ(127l, NumberView<s64>(\"127\").value());\n  EXPECT_EQ(-128l, NumberView<s64>(\"-128\").value());\n  EXPECT_EQ(-751000l, NumberView<s64>(\"-751000\").value());\n  EXPECT_EQ(10000l, NumberView<s64>(\"10000\").value());\n  EXPECT_EQ(81000l, NumberView<s64>(\"+81000\").value());\n  EXPECT_EQ(9931127l, NumberView<s64>(\"9931127\").value());\n  EXPECT_EQ(-19229128l, NumberView<s64>(\"-19229128\").value());\n}\n\nenum class my_enum : u64 {};\n\nTEST(NumberView, my_enum)\n{\n  EXPECT_EQ(static_cast<my_enum>(0ul), NumberView<my_enum>(\"0\").value());\n  EXPECT_EQ(static_cast<my_enum>(1ul), NumberView<my_enum>(\"1\").value());\n  EXPECT_EQ(static_cast<my_enum>(10ul), NumberView<my_enum>(\"10\").value());\n  EXPECT_EQ(static_cast<my_enum>(200ul), NumberView<my_enum>(\"200\").value());\n  EXPECT_EQ(static_cast<my_enum>(255ul), NumberView<my_enum>(\"255\").value());\n  EXPECT_EQ(static_cast<my_enum>(10000ul), NumberView<my_enum>(\"10000\").value());\n  EXPECT_EQ(static_cast<my_enum>(1000ul), NumberView<my_enum>(\"+1000\").value());\n  EXPECT_EQ(static_cast<my_enum>(31127ul), NumberView<my_enum>(\"31127\").value());\n  EXPECT_EQ(static_cast<my_enum>(61128ul), NumberView<my_enum>(\"61128\").value());\n  EXPECT_EQ(static_cast<my_enum>(+751000ul), NumberView<my_enum>(\"+751000\").value());\n  EXPECT_EQ(static_cast<my_enum>(81000ul), NumberView<my_enum>(\"+81000\").value());\n  EXPECT_EQ(static_cast<my_enum>(9931127ul), NumberView<my_enum>(\"9931127\").value());\n  EXPECT_EQ(static_cast<my_enum>(+19229128ul), NumberView<my_enum>(\"+19229128\").value());\n}\n\n} // namespace views\n"
  },
  {
    "path": "util/system_ops.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/system_ops.h>\n\n#include <sys/resource.h>\n#include <unistd.h>\n\n#include <cerrno>\n#include <cstring>\n\nExpected<struct ::sysinfo, std::system_error> system_info()\n{\n  struct ::sysinfo info;\n\n  if (::sysinfo(&info)) {\n    return {unexpected, errno, std::generic_category()};\n  }\n\n  return info;\n}\n\nExpected<ResourceUsage, std::system_error> get_resource_usage()\n{\n  struct ::rusage info;\n  struct ::rusage children;\n\n  if (::getrusage(RUSAGE_SELF, &info)) {\n    return {unexpected, errno, std::generic_category()};\n  }\n\n  if (::getrusage(RUSAGE_CHILDREN, &children)) {\n    return {unexpected, errno, std::generic_category()};\n  }\n\n  return ResourceUsage{\n      .user_mode_time = std::chrono::seconds(info.ru_utime.tv_sec + children.ru_utime.tv_sec) +\n                        std::chrono::microseconds(info.ru_utime.tv_usec + children.ru_utime.tv_usec),\n      .kernel_mode_time = std::chrono::seconds(info.ru_stime.tv_sec + children.ru_stime.tv_sec) +\n                          std::chrono::microseconds(info.ru_stime.tv_usec + children.ru_stime.tv_usec),\n      .max_resident_set_size = static_cast<std::uint64_t>(info.ru_maxrss + children.ru_maxrss) * 1024,\n      .minor_page_faults = static_cast<std::uint32_t>(info.ru_minflt + children.ru_minflt),\n      .major_page_faults = static_cast<std::uint32_t>(info.ru_majflt + children.ru_majflt),\n      .block_input_count = static_cast<std::uint32_t>(info.ru_inblock + children.ru_inblock),\n      .block_output_count = static_cast<std::uint32_t>(info.ru_oublock + children.ru_oublock),\n      .voluntary_context_switch_count = static_cast<std::uint32_t>(info.ru_nvcsw + children.ru_nvcsw),\n      .involuntary_context_switch_count = static_cast<std::uint32_t>(info.ru_nivcsw + children.ru_nivcsw),\n      .cpu_usage_by_process = 0};\n}\n\nExpected<struct ::utsname, std::system_error> get_host_info()\n{\n  struct ::utsname info;\n\n  if (::uname(&info)) {\n    return {unexpected, errno, std::generic_category()};\n  }\n\n  return info;\n}\n\nExpected<std::string, std::error_code> get_host_name(std::size_t max_length)\n{\n  std::string hostname(max_length, '\\0');\n  if (auto const size = ::gethostname(hostname.data(), hostname.size())) {\n    return {unexpected, std::error_code{errno, std::generic_category()}};\n  } else {\n    hostname.resize(strnlen(hostname.data(), hostname.size()));\n    return std::move(hostname);\n  }\n}\n\nExpected<std::size_t, std::error_code> memory_page_size()\n{\n  if (auto const result = sysconf(_SC_PAGESIZE); result != -1) {\n    return static_cast<std::size_t>(result);\n  }\n  return {unexpected, std::error_code{errno, std::generic_category()}};\n}\n"
  },
  {
    "path": "util/system_ops.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This file contains helper functions for system-level operations\n// like gathering information about the system.\n\n#include <util/expected.h>\n#include <util/resource_usage.h>\n\n#include <sys/sysinfo.h>\n#include <sys/utsname.h>\n\n#include <string>\n#include <string_view>\n#include <system_error>\n\nstatic constexpr std::size_t MAX_HOSTNAME_LENGTH = 256;\n\n// Retrieves information about the running system, as per `sysinfo(2)`.\n//\n// Returns the populated `sysinfo` structure on success, or the error\n// information on failure.\nExpected<struct ::sysinfo, std::system_error> system_info();\n\n// Retrieves information about the host, as per `uname(2)`.\n//\n// Returns the populated `utsname` structure on success, or the error\n// information on failure.\n// Note: the member `cpu_usage_by_process` will be set to 0. To correctly\n// get a time-series of cpu usage, take snapshots and compute the\n// difference of user+system time, divided by the interval between the\n// snapshots.\nExpected<ResourceUsage, std::system_error> get_resource_usage();\n\n// Retrieves information about the host, as per `uname(2)`.\n//\n// Returns the populated `utsname` structure on success, or the error\n// information on failure.\nExpected<struct ::utsname, std::system_error> get_host_info();\n\n// Retrieves this host's hostname as per `uname(2)`'s `nodename`.\n//\n// If `max_length` is greater than 0 then the hostname will be truncated if it\n// exceeds this size.\n//\n// On failure the error information is returned.\nExpected<std::string, std::error_code> get_host_name(std::size_t max_length = 0);\n\n// Retrieves the system-wide memory page size.\n//\n// On failure the error information is returned.\nExpected<std::size_t, std::error_code> memory_page_size();\n"
  },
  {
    "path": "util/tdigest.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/tdigest.h\"\n\n#include <algorithm>\n#include <assert.h>\n#include <thread>\nnamespace util {\nnamespace {\n\nthread_local TDigestMerger default_merger;\n\ndouble k_to_q(double k, double d)\n{\n  double k_div_d = k / d;\n  if (k_div_d >= 0.5) {\n    double base = 1 - k_div_d;\n    return 1 - 2 * base * base;\n  } else {\n    return 2 * k_div_d * k_div_d;\n  }\n}\n\ndouble clamp(double v, double lo, double hi)\n{\n  if (v > hi) {\n    return hi;\n  } else if (v < lo) {\n    return lo;\n  }\n  return v;\n}\n} // namespace\n\nvoid TDigest::merge_from_values(double *values, u32 value_count)\n{\n  default_merger.merge_from_values(*this, values, value_count);\n}\n\nvoid TDigest::merge(const TDigest &other)\n{\n  default_merger.merge(*this, other);\n}\n\ndouble TDigest::estimate_value_at_quantile(double q) const\n{\n  if (centroid_count_ == 0) {\n    return 0.0;\n  }\n\n  if (q >= 1.0) {\n    return max_;\n  }\n  if (q <= 0.0) {\n    return min_;\n  }\n\n  const double rank = q * value_count_;\n  double t = 0.0;\n  u32 pos = 0;\n\n  t = value_count_ * 1.0;\n  pos = centroid_count_;\n  for (u32 i = 0; i < centroid_count_; i++) {\n    pos--;\n    t -= centroids_[pos].weight;\n    if (rank >= t) {\n      break;\n    }\n  }\n  double delta = 0;\n  double min = min_;\n  double max = max_;\n\n  if (centroid_count_ > 1) {\n    if (pos == 0) {\n      delta = centroids_[pos + 1].mean - centroids_[pos].mean;\n      max = centroids_[pos + 1].mean;\n    } else if (pos == centroid_count_ - 1) {\n      delta = centroids_[pos].mean - centroids_[pos - 1].mean;\n      min = centroids_[pos - 1].mean;\n    } else {\n      delta = (centroids_[pos + 1].mean - centroids_[pos - 1].mean) / 2.0;\n      min = centroids_[pos - 1].mean;\n      max = centroids_[pos + 1].mean;\n    }\n  }\n  auto value = centroids_[pos].mean + ((rank - t) / centroids_[pos].weight - 0.5) * delta;\n  return clamp(value, min, max);\n}\n\nvoid TDigestAccumulator::add(double value)\n{\n  assert(value_count_ < TDIGEST_VALUE_BUFFER_SIZE);\n  value_buffer_[value_count_] = value;\n  value_count_++;\n  if (value_count_ == TDIGEST_VALUE_BUFFER_SIZE) {\n    flush();\n  }\n}\n\nvoid TDigestAccumulator::flush()\n{\n  if (value_count_ == 0) {\n    return;\n  }\n  tdigest_.merge_from_values(value_buffer_, value_count_);\n  value_count_ = 0;\n}\n\nvoid TDigestMerger::merge_from_values(TDigest &tdigest, double *values, u32 value_count)\n{\n  if (value_count == 0) {\n    return;\n  }\n  assert(value_count <= TDIGEST_VALUE_BUFFER_SIZE);\n\n  // TODO: use double_radix_sort.\n  std::sort(values, (values + value_count));\n\n  tdigest.min_ = (tdigest.value_count_ == 0) ? *values : (std::min(tdigest.min_, *values));\n  tdigest.max_ =\n      (tdigest.value_count_ == 0) ? *(values + value_count - 1) : (std::max(tdigest.max_, *(values + value_count - 1)));\n\n  tdigest.value_count_ += value_count;\n  tdigest.sum_ = 0; // recompute as we go.\n\n  double k_limit = 1;\n  double q_limit_times_count = k_to_q(k_limit++, TDIGEST_CENTROID_COUNT_MAX) * tdigest.value_count_;\n\n  Centroid *curr = &merging_buffer_[0];\n  Centroid *next = curr + 1;\n\n  const Centroid *sp = tdigest.centroids_;\n  const Centroid *sp_end = sp + tdigest.centroid_count_;\n\n  const double *vp = values;\n  const double *vp_end = vp + value_count;\n\n  // put the centroid with minimal mean into curr, either from sp or values\n  if (sp != sp_end && sp->mean < *vp) {\n    *curr = *sp;\n    sp++;\n  } else {\n    curr->mean = *vp;\n    curr->weight = 1.0;\n    vp++;\n  }\n\n  // weights_so_far: total weight up to and including currently considered\n  // centroid\n  double weights_so_far = curr->weight;\n\n  // quantity to be merged into curr\n  double sums_to_merge = 0;\n  double weights_to_merge = 0.0;\n\n  while (sp != sp_end || vp != vp_end) {\n    // get the next smallest mean into next\n    if (sp != sp_end && (vp == vp_end || sp->mean < *vp)) {\n      *next = *sp;\n      sp++;\n    } else {\n      next->mean = *vp;\n      next->weight = 1.0;\n      vp++;\n    }\n\n    double next_sum = next->mean * next->weight;\n    weights_so_far += next->weight;\n\n    if (weights_so_far <= q_limit_times_count) {\n      // will merge this centroid into curr\n      sums_to_merge += next_sum;\n      weights_to_merge += next->weight;\n    } else {\n      // close the centroid\n      tdigest.sum_ += curr->add(sums_to_merge, weights_to_merge);\n      sums_to_merge = 0;\n      weights_to_merge = 0;\n      curr++;\n      next++;\n      q_limit_times_count = k_to_q(k_limit++, TDIGEST_CENTROID_COUNT_MAX) * tdigest.value_count_;\n    }\n  }\n\n  tdigest.sum_ += curr->add(sums_to_merge, weights_to_merge);\n\n  // Deal with floating point precision\n  // Switch to insert sort\n  std::sort(merging_buffer_, next);\n\n  curr = merging_buffer_;\n  Centroid *target = tdigest.centroids_;\n  std::copy(merging_buffer_, next, target);\n\n  tdigest.centroid_count_ = next - merging_buffer_;\n}\n\nvoid TDigestMerger::merge(TDigest &left, const TDigest &right)\n{\n  if (right.value_count_ == 0) {\n    return;\n  }\n\n  if (left.value_count_ == 0) {\n    left = right;\n    return;\n  }\n\n  left.min_ = std::min(left.min_, right.min_);\n  left.max_ = std::max(left.max_, right.max_);\n\n  left.value_count_ += right.value_count_;\n  left.sum_ = 0; // recompute as we go.\n\n  double k_limit = 1;\n  double q_limit_times_count = k_to_q(k_limit++, TDIGEST_CENTROID_COUNT_MAX) * left.value_count_;\n\n  Centroid *curr = merging_buffer_;\n  Centroid *next = curr + 1;\n\n  const Centroid *lp = left.centroids_;\n  const Centroid *lp_end = lp + left.centroid_count_;\n\n  const Centroid *rp = right.centroids_;\n  const Centroid *rp_end = rp + right.centroid_count_;\n\n  if (lp->mean < rp->mean) {\n    *curr = *lp;\n    lp++;\n  } else {\n    *curr = *rp;\n    rp++;\n  }\n\n  double weights_so_far = curr->weight;\n  double sums_to_merge = 0;\n  double weights_to_merge = 0.0;\n\n  while (lp != lp_end || rp != rp_end) {\n    if (lp != lp_end && (rp == rp_end || lp->mean < rp->mean)) {\n      *next = *lp;\n      lp++;\n    } else {\n      *next = *rp;\n      rp++;\n    }\n\n    double next_sum = next->mean * next->weight;\n    weights_so_far += next->weight;\n\n    if (weights_so_far <= q_limit_times_count) {\n      sums_to_merge += next_sum;\n      weights_to_merge += next->weight;\n    } else {\n      left.sum_ += curr->add(sums_to_merge, weights_to_merge);\n      sums_to_merge = 0;\n      weights_to_merge = 0;\n      curr++;\n      next++;\n      q_limit_times_count = k_to_q(k_limit++, TDIGEST_CENTROID_COUNT_MAX) * left.value_count_;\n    }\n  }\n\n  left.sum_ += curr->add(sums_to_merge, weights_to_merge);\n\n  // Deal with floating point precision\n  // Switch to insert_sort\n  std::sort(merging_buffer_, next);\n\n  curr = merging_buffer_;\n  Centroid *target = left.centroids_;\n  std::copy(merging_buffer_, next, target);\n\n  left.centroid_count_ = next - merging_buffer_;\n}\n} // namespace util\n"
  },
  {
    "path": "util/tdigest.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n#include \"platform/types.h\"\n#include <stdlib.h>\n\nnamespace util {\n\n#define TDIGEST_CENTROID_COUNT_MAX 100\n#define TDIGEST_VALUE_BUFFER_SIZE 500\n\nclass TDigestAccumulator;\nclass TDigestMerger;\n\n// TDigests is a biased quantile estimator designed to estimate the values of\n// the quantiles of streaming data.\n//\n// In current implementation, TDigest object can only be modified by the\n// associating TDigestAccumulator and TDigestMerger.\nclass TDigest {\npublic:\n  TDigest() {}\n  ~TDigest() {}\n\n  inline double mean() const { return (value_count_ != 0) ? (sum_ / value_count_) : 0.0; }\n\n  inline size_t value_count() const { return value_count_; }\n  inline double max() const { return max_; }\n  inline double min() const { return min_; }\n  inline double sum() const { return sum_; }\n\n  // Estimates the value at given quantile |q|\n  double estimate_value_at_quantile(double q) const;\n\n  // Merges |values|.\n  // No-op if |value_count| == 0\n  void merge_from_values(double *values, u32 value_count);\n\n  // Merges |other| in place.\n  // No-op if |other| does not contain any data.\n  void merge(const TDigest &other);\n\nprivate:\n  friend class TDigestAccumulator;\n  friend class TDigestMerger;\n\n  // Total number of values that this tdigest object has consumed so far.\n  size_t value_count_ = 0;\n\n  // Sum, min & max of all values.\n  double sum_ = 0.0;\n  double min_ = 0.0;\n  double max_ = 0.0;\n\n  struct Centroid {\n    Centroid() = default;\n    ~Centroid() = default;\n    Centroid(const Centroid &) = default;\n    Centroid(Centroid &&) = default;\n    Centroid &operator=(const Centroid &) = default;\n    Centroid &operator=(Centroid &&) = default;\n\n    inline bool operator<(const Centroid &other) const { return mean < other.mean; }\n\n    // Adds Centroid{|new_sum|, |new_weight|}, returns resulting sum.\n    inline double add(double new_sum, double new_weight)\n    {\n      new_sum += (mean * weight);\n      weight += new_weight;\n      mean = new_sum / weight;\n      return new_sum;\n    }\n\n    double mean = 0.0;\n    double weight = 1.0;\n  };\n\n  u32 centroid_count_ = 0;\n  Centroid centroids_[TDIGEST_CENTROID_COUNT_MAX];\n};\n\n// TDigestAccumulator buffers incoming stream data points, and flushes them\n// to the associated TDigest object when needed.\n//\n// It's expected that TDigest object and TDigestAccumulator object are\n// 1:1 mapped.\nclass TDigestAccumulator {\npublic:\n  TDigestAccumulator(TDigest &tdigest) : tdigest_(tdigest) {}\n  ~TDigestAccumulator() {}\n\n  // Adds an incoming data point to the internal buffer, flushes them\n  // to the associated TDigest object if needed.\n  void add(double value);\n\n  // Flushes data points in the internal buffer to the associated TDigest\n  // object.\n  void flush();\n\n  TDigest &tdigest() { return tdigest_; }\n\nprivate:\n  TDigest &tdigest_;\n\n  u32 value_count_ = 0;\n  double value_buffer_[TDIGEST_VALUE_BUFFER_SIZE];\n};\n\n// TDigestMerger merges a TDigest object and list of value points, or\n// two TDigest objects.\n//\n// It's expected a TDigestMerger object is shared by multiple\n// TDigest/TDigestAcumulator objects. It's caller's responsibilty to ensure\n// thread safty.\nclass TDigestMerger {\npublic:\n  TDigestMerger() {}\n  ~TDigestMerger() {}\n\n  // Merges |values| into |tdigest| object.\n  // No-op if |value_count| == 0\n  void merge_from_values(TDigest &tdigest, double *values, u32 value_count);\n\n  // Merges |target| and |other|, and stores result in |target|.\n  // No-op if |other| does not contain any data.\n  void merge(TDigest &target, const TDigest &other);\n\nprivate:\n  using Centroid = TDigest::Centroid;\n\n  // Note additional 1 Centroid space for scratching during merging.\n  Centroid merging_buffer_[TDIGEST_CENTROID_COUNT_MAX + 1];\n};\n\n} // namespace util\n"
  },
  {
    "path": "util/tdigest_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/tdigest.h\"\n#include \"gtest/gtest.h\"\n\nnamespace util {\nnamespace {\n\nTEST(TDigestTest, Basic)\n{\n  TDigest digest;\n  TDigestAccumulator accumulator(digest);\n\n  for (int i = 1; i <= 100; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  EXPECT_EQ(digest.value_count(), 100u);\n  EXPECT_EQ(digest.sum(), 5050.0);\n  EXPECT_EQ(digest.mean(), 50.5);\n  EXPECT_EQ(digest.min(), 1);\n  EXPECT_EQ(digest.max(), 100);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 2.0 - 0.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 50.375);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 99.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 100);\n}\n\nTEST(TDigestTest, MergeMore)\n{\n  TDigest digest;\n  TDigestAccumulator accumulator(digest);\n\n  for (int i = 1; i <= 100; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  for (int i = 101; i <= 200; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  EXPECT_EQ(digest.value_count(), 200u);\n  EXPECT_EQ(digest.sum(), 20100);\n  EXPECT_EQ(digest.mean(), 100.5);\n  EXPECT_EQ(digest.min(), 1);\n  EXPECT_EQ(digest.max(), 200);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 4.0 - 1.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 100.25);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 200.0 - 1.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 200.0);\n}\n\nTEST(TDigestTest, OnlyOne)\n{\n  TDigest digest;\n  TDigestAccumulator accumulator(digest);\n\n  accumulator.add(1.0);\n  accumulator.flush();\n\n  EXPECT_EQ(digest.value_count(), 1u);\n  EXPECT_EQ(digest.sum(), 1);\n  EXPECT_EQ(digest.mean(), 1);\n  EXPECT_EQ(digest.min(), 1);\n  EXPECT_EQ(digest.max(), 1);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1.0);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 1.0);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 1.0);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 1.0);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 1.0);\n}\n\nTEST(TDigestTest, OneK)\n{\n  TDigest digest;\n  TDigestAccumulator accumulator(digest);\n\n  for (int i = 1; i <= 1000; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  EXPECT_EQ(digest.value_count(), 1000u);\n  EXPECT_EQ(digest.sum(), 500500.0);\n  EXPECT_EQ(digest.mean(), 500.5);\n  EXPECT_EQ(digest.min(), 1);\n  EXPECT_EQ(digest.max(), 1000);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 10.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 500.25);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 990.25);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 999.5);\n}\n\nTEST(TDigestTest, NegativeValue)\n{\n  TDigest digest;\n  TDigestAccumulator accumulator(digest);\n\n  for (int i = 1; i <= 100; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n    accumulator.add(static_cast<double>(i * -1.0));\n  }\n\n  accumulator.flush();\n\n  EXPECT_EQ(digest.value_count(), 200u);\n  EXPECT_EQ(digest.sum(), 0);\n  EXPECT_EQ(digest.mean(), 0);\n  EXPECT_EQ(digest.min(), -100);\n  EXPECT_EQ(digest.max(), 100);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), -100);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), -98.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 0);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 98.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 100);\n}\n\nTEST(TDigestTest, TwoTDigiests)\n{\n  TDigest digest;\n  TDigest other;\n\n  TDigestAccumulator accumulator(digest);\n  TDigestAccumulator other_accumulator(other);\n\n  for (int i = 1; i <= 50; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  for (int i = 51; i <= 100; ++i) {\n    other_accumulator.add(static_cast<double>(i * 1.0));\n  }\n  other_accumulator.flush();\n\n  digest.merge(other);\n\n  EXPECT_EQ(digest.value_count(), 100u);\n  EXPECT_EQ(digest.sum(), 5050.0);\n  EXPECT_EQ(digest.mean(), 50.5);\n  EXPECT_EQ(digest.min(), 1);\n  EXPECT_EQ(digest.max(), 100);\n\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 2.0 - 0.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 50.375);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 99.5);\n  EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 100);\n}\n\nTEST(TDigestTest, TwoLargeTDigiests)\n{\n  TDigest digest;\n  TDigest other;\n\n  TDigestAccumulator accumulator(digest);\n  TDigestAccumulator other_accumulator(other);\n\n  for (int i = 1; i <= 100; ++i) {\n    accumulator.add(static_cast<double>(i * 1.0));\n  }\n  accumulator.flush();\n\n  for (int i = 1; i <= 100; ++i) {\n    other_accumulator.add(static_cast<double>(i * 1.0));\n  }\n  other_accumulator.flush();\n\n  digest.merge(other);\n  /*\n    EXPECT_EQ(digest.value_count(), 100u);\n    EXPECT_EQ(digest.sum(), 5050.0);\n    EXPECT_EQ(digest.mean(), 50.5);\n    EXPECT_EQ(digest.min(), 1);\n    EXPECT_EQ(digest.max(), 100);\n\n    EXPECT_EQ(digest.estimate_value_at_quantile(0.001), 1);\n    EXPECT_EQ(digest.estimate_value_at_quantile(0.01), 2.0 - 0.5);\n    EXPECT_EQ(digest.estimate_value_at_quantile(0.5), 50.375);\n    EXPECT_EQ(digest.estimate_value_at_quantile(0.99), 99.5);\n    EXPECT_EQ(digest.estimate_value_at_quantile(0.999), 100);\n  */\n}\n} // namespace\n} // namespace util\n"
  },
  {
    "path": "util/time.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/time.h>\n\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <stdexcept>\n#include <system_error>\n\n#include <cerrno>\n\nstd::uint64_t const clock_ticks_per_second = [] {\n  if (auto const result = sysconf(_SC_CLK_TCK); result != -1) {\n    return static_cast<std::uint64_t>(result);\n  }\n\n  throw std::system_error{errno, std::generic_category()};\n}();\n"
  },
  {
    "path": "util/time.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/generic.h>\n\n#include <chrono>\n#include <ratio>\n#include <string>\n\n#include <cassert>\n#include <cstdint>\n#include <ctime>\n\n/**\n * Converts duration to desired unit and returns its value in integer form.\n *\n * Example:\n *\n *  void foo(std::chrono::nanoseconds timestamp) {\n *    std::cout << \"Unix time: \" << integer_time<std::chrono::seconds>(timestamp);\n *  }\n */\ntemplate <typename OutputDuration, typename Output = typename OutputDuration::rep, typename R, typename P>\nconstexpr Output integer_time(std::chrono::duration<R, P> duration)\n{\n  return std::chrono::duration_cast<OutputDuration>(duration).count();\n}\n\n/**\n * Converts time point to desired unit and returns its value in integer form.\n *\n * Example:\n *\n *  auto const timestamp = std::chrono::system_clock::now();\n *  std::cout << \"Unix time: \" << integer_time<std::chrono::seconds>(timestamp);\n */\ntemplate <typename OutputDuration, typename Output = typename OutputDuration::rep, typename C, typename D>\nconstexpr Output integer_time(std::chrono::time_point<C, D> time_point)\n{\n  return integer_time<OutputDuration>(time_point.time_since_epoch());\n}\n\n/**\n * Converts time point to a string according to the specified format.\n *\n * See the `strftime` man page for the format string specification:\n * https://man7.org/linux/man-pages/man3/strftime.3.html\n *\n * Beware that the maximum length of the resulting text is 256 characters.\n * If the conversion requires more space, the resulting text will be truncated.\n */\ntemplate <class Clock, typename Duration = typename Clock::duration>\nstd::string string_time(std::chrono::time_point<Clock, Duration> time_point, char const *format)\n{\n  constexpr size_t strftime_buf_size = 256;\n\n  time_t time = Clock::to_time_t(time_point);\n\n  struct tm tm_buf;\n  char strftime_buf[strftime_buf_size];\n  size_t n = strftime(strftime_buf, sizeof(strftime_buf), format, gmtime_r(&time, &tm_buf));\n\n  return std::string(strftime_buf, n);\n}\n\n/**\n * The number of clock ticks per second.\n *\n * This is resolved at process start up time. An exception is thrown on failure.\n */\nextern std::uint64_t const clock_ticks_per_second;\n\n/**\n * Converts clock ticks to desired unit.\n *\n * Example:\n *\n *  void foo(std::uint64_t clock_ticks) {\n *    std::cout << \"Unix time: \" << from_clock_ticks<std::chrono::seconds>(clock_ticks);\n *  }\n */\ntemplate <typename OutputDuration, typename T>\nconstexpr OutputDuration from_clock_ticks(T clock_ticks, std::uint64_t clock_ticks_per_second)\n{\n  static_assert(\n      std::ratio_less_equal_v<typename OutputDuration::period, std::chrono::seconds::period>,\n      \"can't convert clock ticks to a unit less precise than seconds\");\n  return OutputDuration{integer_time<OutputDuration>(std::chrono::seconds{clock_ticks}) / clock_ticks_per_second};\n}\n\ntemplate <typename OutputDuration, typename T> OutputDuration from_clock_ticks(T clock_ticks)\n{\n  return from_clock_ticks<OutputDuration>(clock_ticks, clock_ticks_per_second);\n}\n\n/**\n * Realtime, std::chrono compatible clock using `CLOCK_REALTIME` as the backend.\n *\n * Note: `glib` should take advantage of `vdso` to avoid making a system call.\n */\nstruct realtime_clock {\n  constexpr static bool is_steady = false;\n\n  using rep = std::uint_fast64_t;\n  using period = std::nano;\n  using duration = std::chrono::duration<rep, period>;\n  using time_point = std::chrono::time_point<realtime_clock>;\n\n  static inline __attribute__((always_inline)) time_point now()\n  {\n    struct timespec time;\n    int const result = clock_gettime(CLOCK_REALTIME, &time);\n    if (unlikely(result)) {\n      return time_point{duration::zero()};\n    }\n\n    return time_point{duration{static_cast<u64>((time.tv_sec * (1000 * 1000 * 1000)) + time.tv_nsec)}};\n  }\n};\n\n/**\n * Monotonic, std::chrono compatible clock using `CLOCK_MONOTONIC` as the backend.\n *\n * Note: `glib` should take advantage of `vdso` to avoid making a system call.\n */\nstruct monotonic_clock {\n  constexpr static bool is_steady = true;\n\n  using rep = std::uint_fast64_t;\n  using period = std::nano;\n  using duration = std::chrono::duration<rep, period>;\n  using time_point = std::chrono::time_point<monotonic_clock>;\n\n  static inline __attribute__((always_inline)) time_point now()\n  {\n    struct timespec time;\n    int const result = clock_gettime(CLOCK_MONOTONIC, &time);\n    if (unlikely(result)) {\n      return time_point{duration::zero()};\n    }\n\n    return time_point{duration{static_cast<u64>((time.tv_sec * (1000 * 1000 * 1000)) + time.tv_nsec)}};\n  }\n};\n\n#ifdef __x86_64__\n#include <x86intrin.h>\n#endif\n/**\n * Monotonic, std::chrono compatible clock using `__rdtsc()` as the backend.\n */\nstruct rdtsc_clock {\n  constexpr static bool is_steady = true;\n\n  using rep = std::uint_fast64_t;\n  using period = std::nano;\n  using duration = std::chrono::duration<rep, period>;\n  using time_point = std::chrono::time_point<monotonic_clock>;\n\n#ifdef __aarch64__\n  static inline __attribute__((always_inline)) time_point now() {\n\t  uint64_t val;\n\t  __asm__ __volatile__(\"mrs %0, cntvct_el0\" : \"=r\"(val));\n\t  return time_point{duration{val}};\n  }\n#else\n  static inline __attribute__((always_inline)) time_point now() { return time_point{duration{__rdtsc()}}; }\n#endif\n};\n"
  },
  {
    "path": "util/time_test.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/time.h>\n\n#include <platform/types.h>\n#include <util/log_formatters.h>\n\n#include <gtest/gtest.h>\n\n#include <chrono>\n\nconstexpr std::size_t ITERATIONS = 1'000'000;\n\nTEST(time, integer_time_duration)\n{\n  EXPECT_EQ(1, integer_time<std::chrono::seconds>(1s));\n  EXPECT_EQ(1'000, integer_time<std::chrono::milliseconds>(1s));\n  EXPECT_EQ(1'000'000, integer_time<std::chrono::microseconds>(1s));\n  EXPECT_EQ(1'000'000'000, integer_time<std::chrono::nanoseconds>(1s));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(1ms));\n  EXPECT_EQ(1, integer_time<std::chrono::milliseconds>(1ms));\n  EXPECT_EQ(1'000, integer_time<std::chrono::microseconds>(1ms));\n  EXPECT_EQ(1'000'000, integer_time<std::chrono::nanoseconds>(1ms));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(1us));\n  EXPECT_EQ(0, integer_time<std::chrono::milliseconds>(1us));\n  EXPECT_EQ(1, integer_time<std::chrono::microseconds>(1us));\n  EXPECT_EQ(1'000, integer_time<std::chrono::nanoseconds>(1us));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(1ns));\n  EXPECT_EQ(0, integer_time<std::chrono::milliseconds>(1ns));\n  EXPECT_EQ(0, integer_time<std::chrono::microseconds>(1ns));\n  EXPECT_EQ(1, integer_time<std::chrono::nanoseconds>(1ns));\n}\n\ntemplate <typename R, typename P> auto tp(std::chrono::duration<R, P> duration)\n{\n  return std::chrono::time_point<monotonic_clock, std::chrono::duration<R, P>>{duration};\n}\n\nTEST(time, integer_time_timepoint)\n{\n  EXPECT_EQ(1, integer_time<std::chrono::seconds>(tp(1s)));\n  EXPECT_EQ(1'000, integer_time<std::chrono::milliseconds>(tp(1s)));\n  EXPECT_EQ(1'000'000, integer_time<std::chrono::microseconds>(tp(1s)));\n  EXPECT_EQ(1'000'000'000, integer_time<std::chrono::nanoseconds>(tp(1s)));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(tp(1ms)));\n  EXPECT_EQ(1, integer_time<std::chrono::milliseconds>(tp(1ms)));\n  EXPECT_EQ(1'000, integer_time<std::chrono::microseconds>(tp(1ms)));\n  EXPECT_EQ(1'000'000, integer_time<std::chrono::nanoseconds>(tp(1ms)));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(tp(1us)));\n  EXPECT_EQ(0, integer_time<std::chrono::milliseconds>(tp(1us)));\n  EXPECT_EQ(1, integer_time<std::chrono::microseconds>(tp(1us)));\n  EXPECT_EQ(1'000, integer_time<std::chrono::nanoseconds>(tp(1us)));\n\n  EXPECT_EQ(0, integer_time<std::chrono::seconds>(tp(1ns)));\n  EXPECT_EQ(0, integer_time<std::chrono::milliseconds>(tp(1ns)));\n  EXPECT_EQ(0, integer_time<std::chrono::microseconds>(tp(1ns)));\n  EXPECT_EQ(1, integer_time<std::chrono::nanoseconds>(tp(1ns)));\n}\n\nTEST(time, from_clock_ticks)\n{\n  EXPECT_EQ(1, from_clock_ticks<std::chrono::seconds>(clock_ticks_per_second).count());\n  EXPECT_EQ(1'000, from_clock_ticks<std::chrono::milliseconds>(clock_ticks_per_second).count());\n  EXPECT_EQ(1'000'000, from_clock_ticks<std::chrono::microseconds>(clock_ticks_per_second).count());\n  EXPECT_EQ(1'000'000'000, from_clock_ticks<std::chrono::nanoseconds>(clock_ticks_per_second).count());\n}\n\n#define TEST_MONOTONIC_CLOCK(Clock)                                                                                            \\\n  do {                                                                                                                         \\\n    using clock_type = Clock;                                                                                                  \\\n    auto const start = clock_type::now();                                                                                      \\\n    auto timestamp = start;                                                                                                    \\\n                                                                                                                               \\\n    for (auto i = ITERATIONS; i--;) {                                                                                          \\\n      auto const now = clock_type::now();                                                                                      \\\n      EXPECT_LE(timestamp, now);                                                                                               \\\n      timestamp = now;                                                                                                         \\\n    }                                                                                                                          \\\n                                                                                                                               \\\n    std::cout << \"finished \" << ITERATIONS << \" iterations of \" #Clock \"::now() in \" << (timestamp - start)                    \\\n              << \" with an average of \" << ((timestamp - start) / ITERATIONS) << \" per iteration\" << std::endl;                \\\n  } while (false)\n\nTEST(time, monotonic_clock)\n{\n  TEST_MONOTONIC_CLOCK(monotonic_clock);\n}\n\nTEST(time, rdtsc_clock)\n{\n  TEST_MONOTONIC_CLOCK(rdtsc_clock);\n}\n\nTEST(time, steady_clock)\n{\n  TEST_MONOTONIC_CLOCK(std::chrono::steady_clock);\n}\n"
  },
  {
    "path": "util/utility.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <util/enum.h>\n\n#define ENUM_NAME Utility\n#define ENUM_TYPE std::uint8_t\n#define ENUM_ELEMENTS(X)                                                                                                       \\\n  X(none, 0, \"\")                                                                                                               \\\n  X(curl, 1, \"\")\n#define ENUM_DEFAULT none\n#include <util/enum_operators.inl>\n"
  },
  {
    "path": "util/uv_helpers.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"util/uv_helpers.h\"\n\n#include <uv.h>\n\n#include <condition_variable>\n#include <mutex>\n#include <tuple>\n\nstatic void close_cb(uv_handle_t *const handle, void *const arg)\n{\n  if (!uv_is_closing(handle)) {\n    uv_close(handle, nullptr);\n  }\n}\n\nvoid close_uv_loop_cleanly(uv_loop_t *const loop)\n{\n  // TODO: Check `loop->stop_flag`?\n  uv_walk(loop, &close_cb, nullptr);\n  uv_run(loop, UV_RUN_DEFAULT);\n  CHECK_UV(uv_loop_close(loop));\n}\n\nvoid close_uv_handle_cleanly(uv_handle_t *handle, void (*cb)(uv_handle_t *))\n{\n  if (!uv_is_closing(handle)) {\n    uv_close(handle, cb);\n  }\n}\n\nvoid sync_uv_run(::uv_loop_t &loop, std::function<void()> fn)\n{\n  std::mutex lock;\n  std::condition_variable sync;\n\n  struct Context {\n    std::function<void()> fn;\n    std::mutex lock;\n    std::condition_variable sync;\n    ::uv_async_t async;\n  } context{\n      .fn = std::move(fn),\n  };\n\n  CHECK_UV(::uv_async_init(&loop, &context.async, [](::uv_async_t *handle) {\n    auto &context = *reinterpret_cast<Context *>(handle->data);\n    {\n      std::unique_lock<std::mutex> guard(context.lock);\n      context.fn();\n    }\n    ::uv_close(reinterpret_cast<::uv_handle_t *>(&context.async), [](::uv_handle_t *handle) {\n      auto &context = *reinterpret_cast<Context *>(handle->data);\n      context.sync.notify_all();\n    });\n  }));\n  context.async.data = &context;\n\n  std::unique_lock<std::mutex> guard(context.lock);\n  CHECK_UV(::uv_async_send(&context.async));\n  context.sync.wait(guard);\n}\n\nstruct libuv_error_category : std::error_category {\n  char const *name() const noexcept override { return \"libuv\"; }\n\n  std::string message(int condition) const override\n  {\n    std::ostringstream out;\n    out << uv_error_t(condition);\n    return out.str();\n  }\n};\n\nstatic libuv_error_category libuv_category_;\n\nstd::error_category const &libuv_category() noexcept\n{\n  return libuv_category_;\n}\n"
  },
  {
    "path": "util/uv_helpers.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n// This file contains a number of helper utilities to use when interacting with\n// libuv.\n\n#include <util/log.h>\n\n#include <uv.h>\n\n#include <sstream>\n\n#include <functional>\n#include <system_error>\n#include <utility>\n\n/**\n * This wrapper can be passed to logging functions and output streams with\n * error codes from libuv, so that error messages will be pretty printed.\n *\n * Error message resolution will take place lazily so that the cost or pretty\n * printing won't be paid upfront when a logging level is not going to be\n * printed.\n */\nstruct uv_error_t {\n  inline uv_error_t(int error) : error_(error) {}\n\n  int operator*() const { return error_; }\n\n  bool operator!() const { return !error_; }\n  explicit operator bool() const { return !!error_; }\n\n  template <typename Out> friend Out &&operator<<(Out &&out, uv_error_t const &what)\n  {\n    char buffer[ERROR_BUFFER_SIZE];\n\n    ::uv_err_name_r(*what, buffer, ERROR_BUFFER_SIZE);\n    buffer[ERROR_BUFFER_SIZE - 1] = '\\0';\n    out << '<' << buffer << \"> \";\n\n    ::uv_strerror_r(*what, buffer, ERROR_BUFFER_SIZE);\n    buffer[ERROR_BUFFER_SIZE - 1] = '\\0';\n    out << buffer;\n\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  int error_;\n\n  static constexpr std::size_t ERROR_BUFFER_SIZE = 128;\n};\n\n// Provide fmt support for uv_error_t using its ostream representation.\nnamespace fmt {\ntemplate <> struct formatter<uv_error_t> {\n  template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }\n  template <typename FormatContext> auto format(uv_error_t const &err, FormatContext &ctx) const\n  {\n    std::ostringstream os;\n    os << err;\n    return fmt::format_to(ctx.out(), \"{}\", os.str());\n  }\n};\n} // namespace fmt\n\n// Runs `fn` in a thread-safe manner within the `loop`'s thread and waits for\n// its execution to finish before returning.\n// NOTE: this is not optimized for speed - it's a blocking operation. Use only\n// in test scenarios or application startup / shutdown where speed is not\n// critical.\nvoid sync_uv_run(::uv_loop_t &loop, std::function<void()> fn);\n\n// Evaluates the libuv-error-returning expression. If an error occurs, will\n// panic and log the error message.\n#define CHECK_UV(...)                                                                                                          \\\n  {                                                                                                                            \\\n    const auto result = (__VA_ARGS__);                                                                                         \\\n    if (result != 0 /* success */) {                                                                                           \\\n      LOG::critical(\"libuv failure in {}:{} - {}\", __FILE__, __LINE__, uv_strerror(result));                                   \\\n      std::exit(1);                                                                                                            \\\n    }                                                                                                                          \\\n  }\n\n// Closes the loop and its associated handles. The caller is still responsible\n// for freeing the handles' allocated data. This should be run after the loop\n// has already stopped. Example:\n//\n//   uv_loop_t *loop = ....;\n//   uv_run(loop, UV_RUN_DEFAULT);\n//   close_uv_loop_cleanly(loop);\n//\nvoid close_uv_loop_cleanly(uv_loop_t *loop);\n\n// Close the handle if it isn't already closed or in the process of being closed.\nvoid close_uv_handle_cleanly(uv_handle_t *handle, void (*cb)(uv_handle_t *));\n\nstd::error_category const &libuv_category() noexcept;\n"
  },
  {
    "path": "util/version.cc",
    "content": "// Copyright The OpenTelemetry Authors\n// SPDX-License-Identifier: Apache-2.0\n\n#include <util/version_config.h>\n#include <util/version.h>\n\nnamespace versions {\n\nextern const VersionInfo release{EBPF_NET_MAJOR_VERSION, EBPF_NET_MINOR_VERSION, EBPF_NET_PATCH_VERSION};\n\n} // namespace versions\n"
  },
  {
    "path": "util/version.h",
    "content": "/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#pragma once\n\n#include <platform/types.h>\n\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <utility>\n\nclass VersionInfo {\npublic:\n  constexpr explicit VersionInfo(u32 major = 0, u32 minor = 0, u32 patch = 0, std::string_view signature = {})\n      : version_(major, minor, patch, signature)\n  {}\n\n  constexpr VersionInfo(VersionInfo const &) = default;\n  constexpr VersionInfo(VersionInfo &&) = default;\n\n  void set(u32 major, u32 minor, u32 patch = 0, std::string_view signature = {})\n  {\n    version_ = std::make_tuple(major, minor, patch, signature);\n  }\n\n  constexpr u32 major() const { return std::get<0>(version_); }\n  constexpr u32 minor() const { return std::get<1>(version_); }\n  constexpr u32 patch() const { return std::get<2>(version_); }\n  constexpr std::string_view signature() const { return std::get<3>(version_); }\n\n  constexpr bool operator==(VersionInfo const &rhs) const { return version_ == rhs.version_; }\n  constexpr bool operator!=(VersionInfo const &rhs) const { return !(*this == rhs); }\n\n  constexpr bool operator<(VersionInfo const &rhs) const { return version_ < rhs.version_; }\n  constexpr bool operator<=(VersionInfo const &rhs) const { return !(rhs < *this); }\n  constexpr bool operator>(VersionInfo const &rhs) const { return rhs < *this; }\n  constexpr bool operator>=(VersionInfo const &rhs) const { return !(*this < rhs); }\n\n  VersionInfo &operator=(VersionInfo const &) = default;\n  VersionInfo &operator=(VersionInfo &&) = default;\n\n  template <typename Out> friend Out &&operator<<(Out &&out, VersionInfo const &value)\n  {\n    out << value.major() << '.' << value.minor() << '.' << value.patch();\n    if (auto signature = value.signature(); !signature.empty()) {\n      out << '-' << signature;\n    }\n    return std::forward<Out>(out);\n  }\n\nprivate:\n  std::tuple<u32, u32, u32, std::string_view> version_;\n};\n\n// Provide an fmt-friendly customization without requiring fmt headers here.\n// fmt v10+ will detect this via ADL and format VersionInfo as the returned type.\ninline std::string format_as(VersionInfo const &value)\n{\n  std::string s = std::to_string(value.major());\n  s.push_back('.');\n  s += std::to_string(value.minor());\n  s.push_back('.');\n  s += std::to_string(value.patch());\n  if (auto sig = value.signature(); !sig.empty()) {\n    s.push_back('-');\n    s.append(sig.begin(), sig.end());\n  }\n  return s;\n}\n"
  },
  {
    "path": "util/version_config.h.cmake_in",
    "content": "// Generated at configure time with project version numbers\n#define EBPF_NET_MAJOR_VERSION ${EBPF_NET_MAJOR_VERSION}\n#define EBPF_NET_MINOR_VERSION ${EBPF_NET_MINOR_VERSION}\n#define EBPF_NET_PATCH_VERSION ${EBPF_NET_PATCH_VERSION}\n"
  },
  {
    "path": "version.sh",
    "content": "#!/usr/bin/env bash\n# Copyright The OpenTelemetry Authors\n# SPDX-License-Identifier: Apache-2.0\n\n\nset -e\n\n# -----\n# Version: read from VERSION file in the same directory as this script.\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nVERSION_FILE=\"${SCRIPT_DIR}/VERSION\"\n\nif [[ ! -f \"$VERSION_FILE\" ]]; then\n  echo \"ERROR: VERSION file not found at '${VERSION_FILE}'. Please create it with the current semantic version (e.g. 0.12.0).\" >&2\n  # Allow failure whether the script is sourced or executed.\n  return 1 2>/dev/null || exit 1\nfi\n\nEBPF_NET_VERSION=\"$(tr -d '\\n' < \"$VERSION_FILE\")\"\n\nEBPF_NET_MAJOR_VERSION=\"${EBPF_NET_VERSION%%.*}\"\nEBPF_NET_MINOR_VERSION=\"${EBPF_NET_VERSION#*.}\"\nEBPF_NET_MINOR_VERSION=\"${EBPF_NET_MINOR_VERSION%%.*}\"\nEBPF_NET_PATCH_VERSION=\"${EBPF_NET_VERSION##*.}\"\n\nexport EBPF_NET_MAJOR_VERSION EBPF_NET_MINOR_VERSION EBPF_NET_PATCH_VERSION\n\n# -----\n# Build number is incremented automatically, so we can release directly from\n# CI/CD, releasing the same exact binaries and containers that we already tested\n# in dev/staging environements.\n\n# COLLECTOR_BUILD_NUMBER_BASE is the commit-ish we count the build number from.\n# to reset the build number, change this. Make sure this also happens together with\n# bumping the major or minor versions, so there is no ambiguity with previously\n# built versions.\n# The commit hash must be a valid git hash in the opentelemetry-ebpf repository.\nCOLLECTOR_BUILD_NUMBER_BASE=\"3598385a6f5288ced0f7bbe9150837c4497d6bc8\"\nGIT_REVISION=\"HEAD\"\n\n# build number is the number of commits since the latest tag\nEBPF_NET_COLLECTOR_BUILD_NUMBER=\"$(git rev-list --count \"${COLLECTOR_BUILD_NUMBER_BASE}..${GIT_REVISION}\")\"\n# adjust build number to keep the increasing order\nEBPF_NET_COLLECTOR_BUILD_NUMBER=\"$(($EBPF_NET_COLLECTOR_BUILD_NUMBER + 4000))\"\nexport EBPF_NET_COLLECTOR_BUILD_NUMBER\n"
  }
]