1cb21ac58SAndrii Nakryiko================
2cb21ac58SAndrii Nakryikobpftool-gen
3cb21ac58SAndrii Nakryiko================
4cb21ac58SAndrii Nakryiko-------------------------------------------------------------------------------
5cb21ac58SAndrii Nakryikotool for BPF code-generation
6cb21ac58SAndrii Nakryiko-------------------------------------------------------------------------------
7cb21ac58SAndrii Nakryiko
8cb21ac58SAndrii Nakryiko:Manual section: 8
9cb21ac58SAndrii Nakryiko
10cb21ac58SAndrii NakryikoSYNOPSIS
11cb21ac58SAndrii Nakryiko========
12cb21ac58SAndrii Nakryiko
13cb21ac58SAndrii Nakryiko	**bpftool** [*OPTIONS*] **gen** *COMMAND*
14cb21ac58SAndrii Nakryiko
15*8cc8c635SQuentin Monnet	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-d** | **--debug** } |
16*8cc8c635SQuentin Monnet		{ **-L** | **--use-loader** } }
17cb21ac58SAndrii Nakryiko
18d80b2fcbSAndrii Nakryiko	*COMMAND* := { **object** | **skeleton** | **help** }
19cb21ac58SAndrii Nakryiko
20cb21ac58SAndrii NakryikoGEN COMMANDS
21cb21ac58SAndrii Nakryiko=============
22cb21ac58SAndrii Nakryiko
23d80b2fcbSAndrii Nakryiko|	**bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
24c4122665SAndrii Nakryiko|	**bpftool** **gen skeleton** *FILE* [**name** *OBJECT_NAME*]
25cb21ac58SAndrii Nakryiko|	**bpftool** **gen help**
26cb21ac58SAndrii Nakryiko
27cb21ac58SAndrii NakryikoDESCRIPTION
28cb21ac58SAndrii Nakryiko===========
29d80b2fcbSAndrii Nakryiko	**bpftool gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
30d80b2fcbSAndrii Nakryiko		  Statically link (combine) together one or more *INPUT_FILE*'s
31d80b2fcbSAndrii Nakryiko		  into a single resulting *OUTPUT_FILE*. All the files involved
32d80b2fcbSAndrii Nakryiko		  are BPF ELF object files.
33d80b2fcbSAndrii Nakryiko
34d80b2fcbSAndrii Nakryiko		  The rules of BPF static linking are mostly the same as for
35d80b2fcbSAndrii Nakryiko		  user-space object files, but in addition to combining data
36d80b2fcbSAndrii Nakryiko		  and instruction sections, .BTF and .BTF.ext (if present in
37d80b2fcbSAndrii Nakryiko		  any of the input files) data are combined together. .BTF
38d80b2fcbSAndrii Nakryiko		  data is deduplicated, so all the common types across
39d80b2fcbSAndrii Nakryiko		  *INPUT_FILE*'s will only be represented once in the resulting
40d80b2fcbSAndrii Nakryiko		  BTF information.
41d80b2fcbSAndrii Nakryiko
42d80b2fcbSAndrii Nakryiko		  BPF static linking allows to partition BPF source code into
43d80b2fcbSAndrii Nakryiko		  individually compiled files that are then linked into
44d80b2fcbSAndrii Nakryiko		  a single resulting BPF object file, which can be used to
45d80b2fcbSAndrii Nakryiko		  generated BPF skeleton (with **gen skeleton** command) or
46d80b2fcbSAndrii Nakryiko		  passed directly into **libbpf** (using **bpf_object__open()**
47d80b2fcbSAndrii Nakryiko		  family of APIs).
48d80b2fcbSAndrii Nakryiko
49cb21ac58SAndrii Nakryiko	**bpftool gen skeleton** *FILE*
50cb21ac58SAndrii Nakryiko		  Generate BPF skeleton C header file for a given *FILE*.
51cb21ac58SAndrii Nakryiko
52cb21ac58SAndrii Nakryiko		  BPF skeleton is an alternative interface to existing libbpf
53cb21ac58SAndrii Nakryiko		  APIs for working with BPF objects. Skeleton code is intended
54cb21ac58SAndrii Nakryiko		  to significantly shorten and simplify code to load and work
55cb21ac58SAndrii Nakryiko		  with BPF programs from userspace side. Generated code is
56cb21ac58SAndrii Nakryiko		  tailored to specific input BPF object *FILE*, reflecting its
57cb21ac58SAndrii Nakryiko		  structure by listing out available maps, program, variables,
58cb21ac58SAndrii Nakryiko		  etc. Skeleton eliminates the need to lookup mentioned
59cb21ac58SAndrii Nakryiko		  components by name. Instead, if skeleton instantiation
60cb21ac58SAndrii Nakryiko		  succeeds, they are populated in skeleton structure as valid
61c8caa0bbSQuentin Monnet		  libbpf types (e.g., **struct bpf_map** pointer) and can be
62cb21ac58SAndrii Nakryiko		  passed to existing generic libbpf APIs.
63cb21ac58SAndrii Nakryiko
64cb21ac58SAndrii Nakryiko		  In addition to simple and reliable access to maps and
65c8caa0bbSQuentin Monnet		  programs, skeleton provides a storage for BPF links (**struct
66c8caa0bbSQuentin Monnet		  bpf_link**) for each BPF program within BPF object. When
67cb21ac58SAndrii Nakryiko		  requested, supported BPF programs will be automatically
68cb21ac58SAndrii Nakryiko		  attached and resulting BPF links stored for further use by
69cb21ac58SAndrii Nakryiko		  user in pre-allocated fields in skeleton struct. For BPF
70cb21ac58SAndrii Nakryiko		  programs that can't be automatically attached by libbpf,
71cb21ac58SAndrii Nakryiko		  user can attach them manually, but store resulting BPF link
72cb21ac58SAndrii Nakryiko		  in per-program link field. All such set up links will be
73cb21ac58SAndrii Nakryiko		  automatically destroyed on BPF skeleton destruction. This
74cb21ac58SAndrii Nakryiko		  eliminates the need for users to manage links manually and
75cb21ac58SAndrii Nakryiko		  rely on libbpf support to detach programs and free up
76cb21ac58SAndrii Nakryiko		  resources.
77cb21ac58SAndrii Nakryiko
78cb21ac58SAndrii Nakryiko		  Another facility provided by BPF skeleton is an interface to
79cb21ac58SAndrii Nakryiko		  global variables of all supported kinds: mutable, read-only,
80cb21ac58SAndrii Nakryiko		  as well as extern ones. This interface allows to pre-setup
81cb21ac58SAndrii Nakryiko		  initial values of variables before BPF object is loaded and
82cb21ac58SAndrii Nakryiko		  verified by kernel. For non-read-only variables, the same
83cb21ac58SAndrii Nakryiko		  interface can be used to fetch values of global variables on
84cb21ac58SAndrii Nakryiko		  userspace side, even if they are modified by BPF code.
85cb21ac58SAndrii Nakryiko
86cb21ac58SAndrii Nakryiko		  During skeleton generation, contents of source BPF object
87cb21ac58SAndrii Nakryiko		  *FILE* is embedded within generated code and is thus not
88cb21ac58SAndrii Nakryiko		  necessary to keep around. This ensures skeleton and BPF
89cb21ac58SAndrii Nakryiko		  object file are matching 1-to-1 and always stay in sync.
90cb21ac58SAndrii Nakryiko		  Generated code is dual-licensed under LGPL-2.1 and
91cb21ac58SAndrii Nakryiko		  BSD-2-Clause licenses.
92cb21ac58SAndrii Nakryiko
93cb21ac58SAndrii Nakryiko		  It is a design goal and guarantee that skeleton interfaces
94cb21ac58SAndrii Nakryiko		  are interoperable with generic libbpf APIs. User should
95cb21ac58SAndrii Nakryiko		  always be able to use skeleton API to create and load BPF
96cb21ac58SAndrii Nakryiko		  object, and later use libbpf APIs to keep working with
97cb21ac58SAndrii Nakryiko		  specific maps, programs, etc.
98cb21ac58SAndrii Nakryiko
99cb21ac58SAndrii Nakryiko		  As part of skeleton, few custom functions are generated.
100c4122665SAndrii Nakryiko		  Each of them is prefixed with object name. Object name can
101c4122665SAndrii Nakryiko		  either be derived from object file name, i.e., if BPF object
102c4122665SAndrii Nakryiko		  file name is **example.o**, BPF object name will be
103c4122665SAndrii Nakryiko		  **example**. Object name can be also specified explicitly
104c4122665SAndrii Nakryiko		  through **name** *OBJECT_NAME* parameter. The following
105c4122665SAndrii Nakryiko		  custom functions are provided (assuming **example** as
106c4122665SAndrii Nakryiko		  the object name):
107cb21ac58SAndrii Nakryiko
108cb21ac58SAndrii Nakryiko		  - **example__open** and **example__open_opts**.
109cb21ac58SAndrii Nakryiko		    These functions are used to instantiate skeleton. It
110c8caa0bbSQuentin Monnet		    corresponds to libbpf's **bpf_object__open**\ () API.
111cb21ac58SAndrii Nakryiko		    **_opts** variants accepts extra **bpf_object_open_opts**
112cb21ac58SAndrii Nakryiko		    options.
113cb21ac58SAndrii Nakryiko
114cb21ac58SAndrii Nakryiko		  - **example__load**.
115cb21ac58SAndrii Nakryiko		    This function creates maps, loads and verifies BPF
116cb21ac58SAndrii Nakryiko		    programs, initializes global data maps. It corresponds to
117c8caa0bbSQuentin Monnet		    libppf's **bpf_object__load**\ () API.
118cb21ac58SAndrii Nakryiko
119cb21ac58SAndrii Nakryiko		  - **example__open_and_load** combines **example__open** and
120cb21ac58SAndrii Nakryiko		    **example__load** invocations in one commonly used
121cb21ac58SAndrii Nakryiko		    operation.
122cb21ac58SAndrii Nakryiko
123cb21ac58SAndrii Nakryiko		  - **example__attach** and **example__detach**
124cb21ac58SAndrii Nakryiko		    This pair of functions allow to attach and detach,
125cb21ac58SAndrii Nakryiko		    correspondingly, already loaded BPF object. Only BPF
126cb21ac58SAndrii Nakryiko		    programs of types supported by libbpf for auto-attachment
127cb21ac58SAndrii Nakryiko		    will be auto-attached and their corresponding BPF links
128cb21ac58SAndrii Nakryiko		    instantiated. For other BPF programs, user can manually
129cb21ac58SAndrii Nakryiko		    create a BPF link and assign it to corresponding fields in
130cb21ac58SAndrii Nakryiko		    skeleton struct. **example__detach** will detach both
131cb21ac58SAndrii Nakryiko		    links created automatically, as well as those populated by
132cb21ac58SAndrii Nakryiko		    user manually.
133cb21ac58SAndrii Nakryiko
134cb21ac58SAndrii Nakryiko		  - **example__destroy**
135cb21ac58SAndrii Nakryiko		    Detach and unload BPF programs, free up all the resources
136cb21ac58SAndrii Nakryiko		    used by skeleton and BPF object.
137cb21ac58SAndrii Nakryiko
138cb21ac58SAndrii Nakryiko		  If BPF object has global variables, corresponding structs
139cb21ac58SAndrii Nakryiko		  with memory layout corresponding to global data data section
140dacce641SAndrii Nakryiko		  layout will be created. Currently supported ones are: *.data*,
141dacce641SAndrii Nakryiko		  *.bss*, *.rodata*, and *.kconfig* structs/data sections.
142dacce641SAndrii Nakryiko		  These data sections/structs can be used to set up initial
143dacce641SAndrii Nakryiko		  values of variables, if set before **example__load**.
144dacce641SAndrii Nakryiko		  Afterwards, if target kernel supports memory-mapped BPF
145dacce641SAndrii Nakryiko		  arrays, same structs can be used to fetch and update
146dacce641SAndrii Nakryiko		  (non-read-only) data from userspace, with same simplicity
147dacce641SAndrii Nakryiko		  as for BPF side.
148cb21ac58SAndrii Nakryiko
149cb21ac58SAndrii Nakryiko	**bpftool gen help**
150cb21ac58SAndrii Nakryiko		  Print short help message.
151cb21ac58SAndrii Nakryiko
152cb21ac58SAndrii NakryikoOPTIONS
153cb21ac58SAndrii Nakryiko=======
154f28ef96dSQuentin Monnet	.. include:: common_options.rst
155cb21ac58SAndrii Nakryiko
156*8cc8c635SQuentin Monnet	-L, --use-loader
157*8cc8c635SQuentin Monnet		  For skeletons, generate a "light" skeleton (also known as "loader"
158*8cc8c635SQuentin Monnet		  skeleton). A light skeleton contains a loader eBPF program. It does
159*8cc8c635SQuentin Monnet		  not use the majority of the libbpf infrastructure, and does not need
160*8cc8c635SQuentin Monnet		  libelf.
161*8cc8c635SQuentin Monnet
162cb21ac58SAndrii NakryikoEXAMPLES
163cb21ac58SAndrii Nakryiko========
164d80b2fcbSAndrii Nakryiko**$ cat example1.bpf.c**
16516f3ddfbSQuentin Monnet
166cb21ac58SAndrii Nakryiko::
167cb21ac58SAndrii Nakryiko
168cb21ac58SAndrii Nakryiko  #include <stdbool.h>
169cb21ac58SAndrii Nakryiko  #include <linux/ptrace.h>
170cb21ac58SAndrii Nakryiko  #include <linux/bpf.h>
171d80b2fcbSAndrii Nakryiko  #include <bpf/bpf_helpers.h>
172cb21ac58SAndrii Nakryiko
173cb21ac58SAndrii Nakryiko  const volatile int param1 = 42;
174cb21ac58SAndrii Nakryiko  bool global_flag = true;
175cb21ac58SAndrii Nakryiko  struct { int x; } data = {};
176cb21ac58SAndrii Nakryiko
177cb21ac58SAndrii Nakryiko  SEC("raw_tp/sys_enter")
178cb21ac58SAndrii Nakryiko  int handle_sys_enter(struct pt_regs *ctx)
179cb21ac58SAndrii Nakryiko  {
180cb21ac58SAndrii Nakryiko  	static long my_static_var;
181cb21ac58SAndrii Nakryiko  	if (global_flag)
182cb21ac58SAndrii Nakryiko  		my_static_var++;
183cb21ac58SAndrii Nakryiko  	else
184cb21ac58SAndrii Nakryiko  		data.x += param1;
185cb21ac58SAndrii Nakryiko  	return 0;
186cb21ac58SAndrii Nakryiko  }
187cb21ac58SAndrii Nakryiko
188d80b2fcbSAndrii Nakryiko**$ cat example2.bpf.c**
189d80b2fcbSAndrii Nakryiko
190d80b2fcbSAndrii Nakryiko::
191d80b2fcbSAndrii Nakryiko
192d80b2fcbSAndrii Nakryiko  #include <linux/ptrace.h>
193d80b2fcbSAndrii Nakryiko  #include <linux/bpf.h>
194d80b2fcbSAndrii Nakryiko  #include <bpf/bpf_helpers.h>
195d80b2fcbSAndrii Nakryiko
196d80b2fcbSAndrii Nakryiko  struct {
197d80b2fcbSAndrii Nakryiko  	__uint(type, BPF_MAP_TYPE_HASH);
198d80b2fcbSAndrii Nakryiko  	__uint(max_entries, 128);
199d80b2fcbSAndrii Nakryiko  	__type(key, int);
200d80b2fcbSAndrii Nakryiko  	__type(value, long);
201d80b2fcbSAndrii Nakryiko  } my_map SEC(".maps");
202d80b2fcbSAndrii Nakryiko
203cb21ac58SAndrii Nakryiko  SEC("raw_tp/sys_exit")
204cb21ac58SAndrii Nakryiko  int handle_sys_exit(struct pt_regs *ctx)
205cb21ac58SAndrii Nakryiko  {
206cb21ac58SAndrii Nakryiko  	int zero = 0;
207cb21ac58SAndrii Nakryiko  	bpf_map_lookup_elem(&my_map, &zero);
208cb21ac58SAndrii Nakryiko  	return 0;
209cb21ac58SAndrii Nakryiko  }
210cb21ac58SAndrii Nakryiko
211cb21ac58SAndrii NakryikoThis is example BPF application with two BPF programs and a mix of BPF maps
212d80b2fcbSAndrii Nakryikoand global variables. Source code is split across two source code files.
213cb21ac58SAndrii Nakryiko
214d80b2fcbSAndrii Nakryiko**$ clang -target bpf -g example1.bpf.c -o example1.bpf.o**
215d80b2fcbSAndrii Nakryiko**$ clang -target bpf -g example2.bpf.c -o example2.bpf.o**
216d80b2fcbSAndrii Nakryiko**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o**
217d80b2fcbSAndrii Nakryiko
218d80b2fcbSAndrii NakryikoThis set of commands compiles *example1.bpf.c* and *example2.bpf.c*
219d80b2fcbSAndrii Nakryikoindividually and then statically links respective object files into the final
220d80b2fcbSAndrii NakryikoBPF ELF object file *example.bpf.o*.
221d80b2fcbSAndrii Nakryiko
222d80b2fcbSAndrii Nakryiko**$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**
22316f3ddfbSQuentin Monnet
224cb21ac58SAndrii Nakryiko::
225cb21ac58SAndrii Nakryiko
226cb21ac58SAndrii Nakryiko  /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
227cb21ac58SAndrii Nakryiko
228cb21ac58SAndrii Nakryiko  /* THIS FILE IS AUTOGENERATED! */
229cb21ac58SAndrii Nakryiko  #ifndef __EXAMPLE_SKEL_H__
230cb21ac58SAndrii Nakryiko  #define __EXAMPLE_SKEL_H__
231cb21ac58SAndrii Nakryiko
232cb21ac58SAndrii Nakryiko  #include <stdlib.h>
233229c3b47SToke Høiland-Jørgensen  #include <bpf/libbpf.h>
234cb21ac58SAndrii Nakryiko
235cb21ac58SAndrii Nakryiko  struct example {
236cb21ac58SAndrii Nakryiko  	struct bpf_object_skeleton *skeleton;
237cb21ac58SAndrii Nakryiko  	struct bpf_object *obj;
238cb21ac58SAndrii Nakryiko  	struct {
239cb21ac58SAndrii Nakryiko  		struct bpf_map *rodata;
240cb21ac58SAndrii Nakryiko  		struct bpf_map *data;
241cb21ac58SAndrii Nakryiko  		struct bpf_map *bss;
242cb21ac58SAndrii Nakryiko  		struct bpf_map *my_map;
243cb21ac58SAndrii Nakryiko  	} maps;
244cb21ac58SAndrii Nakryiko  	struct {
245cb21ac58SAndrii Nakryiko  		struct bpf_program *handle_sys_enter;
246cb21ac58SAndrii Nakryiko  		struct bpf_program *handle_sys_exit;
247cb21ac58SAndrii Nakryiko  	} progs;
248cb21ac58SAndrii Nakryiko  	struct {
249cb21ac58SAndrii Nakryiko  		struct bpf_link *handle_sys_enter;
250cb21ac58SAndrii Nakryiko  		struct bpf_link *handle_sys_exit;
251cb21ac58SAndrii Nakryiko  	} links;
252cb21ac58SAndrii Nakryiko  	struct example__bss {
253cb21ac58SAndrii Nakryiko  		struct {
254cb21ac58SAndrii Nakryiko  			int x;
255cb21ac58SAndrii Nakryiko  		} data;
256cb21ac58SAndrii Nakryiko  	} *bss;
257cb21ac58SAndrii Nakryiko  	struct example__data {
258cb21ac58SAndrii Nakryiko  		_Bool global_flag;
259cb21ac58SAndrii Nakryiko  		long int handle_sys_enter_my_static_var;
260cb21ac58SAndrii Nakryiko  	} *data;
261cb21ac58SAndrii Nakryiko  	struct example__rodata {
262cb21ac58SAndrii Nakryiko  		int param1;
263cb21ac58SAndrii Nakryiko  	} *rodata;
264cb21ac58SAndrii Nakryiko  };
265cb21ac58SAndrii Nakryiko
266cb21ac58SAndrii Nakryiko  static void example__destroy(struct example *obj);
267cb21ac58SAndrii Nakryiko  static inline struct example *example__open_opts(
268cb21ac58SAndrii Nakryiko                const struct bpf_object_open_opts *opts);
269cb21ac58SAndrii Nakryiko  static inline struct example *example__open();
270cb21ac58SAndrii Nakryiko  static inline int example__load(struct example *obj);
271cb21ac58SAndrii Nakryiko  static inline struct example *example__open_and_load();
272cb21ac58SAndrii Nakryiko  static inline int example__attach(struct example *obj);
273cb21ac58SAndrii Nakryiko  static inline void example__detach(struct example *obj);
274cb21ac58SAndrii Nakryiko
275cb21ac58SAndrii Nakryiko  #endif /* __EXAMPLE_SKEL_H__ */
276cb21ac58SAndrii Nakryiko
277d80b2fcbSAndrii Nakryiko**$ cat example.c**
27816f3ddfbSQuentin Monnet
279cb21ac58SAndrii Nakryiko::
280cb21ac58SAndrii Nakryiko
281cb21ac58SAndrii Nakryiko  #include "example.skel.h"
282cb21ac58SAndrii Nakryiko
283cb21ac58SAndrii Nakryiko  int main()
284cb21ac58SAndrii Nakryiko  {
285cb21ac58SAndrii Nakryiko  	struct example *skel;
286cb21ac58SAndrii Nakryiko  	int err = 0;
287cb21ac58SAndrii Nakryiko
288cb21ac58SAndrii Nakryiko  	skel = example__open();
289cb21ac58SAndrii Nakryiko  	if (!skel)
290cb21ac58SAndrii Nakryiko  		goto cleanup;
291cb21ac58SAndrii Nakryiko
292cb21ac58SAndrii Nakryiko  	skel->rodata->param1 = 128;
293cb21ac58SAndrii Nakryiko
294cb21ac58SAndrii Nakryiko  	err = example__load(skel);
295cb21ac58SAndrii Nakryiko  	if (err)
296cb21ac58SAndrii Nakryiko  		goto cleanup;
297cb21ac58SAndrii Nakryiko
298cb21ac58SAndrii Nakryiko  	err = example__attach(skel);
299cb21ac58SAndrii Nakryiko  	if (err)
300cb21ac58SAndrii Nakryiko  		goto cleanup;
301cb21ac58SAndrii Nakryiko
302cb21ac58SAndrii Nakryiko  	/* all libbpf APIs are usable */
303cb21ac58SAndrii Nakryiko  	printf("my_map name: %s\n", bpf_map__name(skel->maps.my_map));
304cb21ac58SAndrii Nakryiko  	printf("sys_enter prog FD: %d\n",
305cb21ac58SAndrii Nakryiko  	       bpf_program__fd(skel->progs.handle_sys_enter));
306cb21ac58SAndrii Nakryiko
307cb21ac58SAndrii Nakryiko  	/* detach and re-attach sys_exit program */
308cb21ac58SAndrii Nakryiko  	bpf_link__destroy(skel->links.handle_sys_exit);
309cb21ac58SAndrii Nakryiko  	skel->links.handle_sys_exit =
310cb21ac58SAndrii Nakryiko  		bpf_program__attach(skel->progs.handle_sys_exit);
311cb21ac58SAndrii Nakryiko
312cb21ac58SAndrii Nakryiko  	printf("my_static_var: %ld\n",
313cb21ac58SAndrii Nakryiko  	       skel->bss->handle_sys_enter_my_static_var);
314cb21ac58SAndrii Nakryiko
315cb21ac58SAndrii Nakryiko  cleanup:
316cb21ac58SAndrii Nakryiko  	example__destroy(skel);
317cb21ac58SAndrii Nakryiko  	return err;
318cb21ac58SAndrii Nakryiko  }
319cb21ac58SAndrii Nakryiko
320d80b2fcbSAndrii Nakryiko**# ./example**
32116f3ddfbSQuentin Monnet
322cb21ac58SAndrii Nakryiko::
323cb21ac58SAndrii Nakryiko
324cb21ac58SAndrii Nakryiko  my_map name: my_map
325cb21ac58SAndrii Nakryiko  sys_enter prog FD: 8
326cb21ac58SAndrii Nakryiko  my_static_var: 7
327cb21ac58SAndrii Nakryiko
328cb21ac58SAndrii NakryikoThis is a stripped-out version of skeleton generated for above example code.
329