<div dir="auto"><div dir="auto">I&#39;m really sorry, I&#39;ve just found a bug in my criu patch: the crash takes place if log filename is not set via commandline options. The fix is trivial, &quot;flog_ctx.readonly=1; /* don&#39;t try to write into it */&quot; inserted into the criu/log.c, line 245, fixes it. It turns flog off if there is no log file name. I checked other cases, but not this one...</div><div dir="auto"><br></div><div dir="auto">If you run criu with —log-file ... parameter, this bug is not activated.</div><div dir="auto"><br></div><div dir="auto">I&#39;ve updated it in <a href="https://github.com/anastasiamarkina/criu/tree/nastya-flog2">https://github.com/anastasiamarkina/criu/tree/nastya-flog2</a>, and I will be happy to follow with any updates needed. Should I make updated patch into the mailing list?</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">пт, 30 авг. 2019 г., 12:13 Andrei Vagin &lt;<a href="mailto:avagin@gmail.com">avagin@gmail.com</a>&gt;:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Mon, Aug 26, 2019 at 05:49:24PM +0300, Anastasia Markina wrote:<br>
&gt; Hello! I&#39;ve finalized my work for the GSoC project as a patch, attached to<br>
&gt; this message. As before, it is also available in my github repo,<br>
&gt; <a href="https://github.com/anastasiamarkina/criu/tree/nastya-flog2" rel="noreferrer noreferrer" target="_blank">https://github.com/anastasiamarkina/criu/tree/nastya-flog2</a><br>
&gt; <br>
&gt; The next part is a fragment of binlog.txt file included into the patch,<br>
&gt; which explains how to use it:<br>
&gt; <br>
&gt; USAGE: TURNING ON BINARY LOGGING<br>
&gt; <br>
&gt; Classical text logging is done by default.<br>
&gt; Binary logging is turned on by the --binlog command line parameter.<br>
&gt; It should be combined with --log-file filename parameter to specify the log<br>
&gt; file name (this second parameter is used with both text and binary logging).<br>
&gt; <br>
&gt; The example:<br>
&gt; <br>
&gt; criu --log-file /tmp/sdf.bin --binlog check<br>
&gt; <br>
&gt; USAGE: READING BINARY LOG FILE<br>
&gt; <br>
&gt; There is a --print-log parameter which turnes criy into a log reader.<br>
&gt; It should be combined with --log-file filename parameter, which specifies<br>
&gt; the log file name. When these parameters are used, CRIU prints all messages<br>
&gt; from the binary log file on the screen and exits. This looks as follows:<br>
&gt; <br>
&gt; criu --log-file /tmp/sdf.bin --binlog --print-log<br>
<br>
On my laptop, criu segfaulted with binlog:<br>
<br>
$ ./criu/criu check --binlog -o /tmp/test.log<br>
Segmentation fault (core dumped)<br>
<br>
(gdb) bt<br>
#0  0x00007fb629e45120 in __memchr_avx2 () from /lib64/libc.so.6<br>
#1  0x00007fb629d77d4f in memccpy () from /lib64/libc.so.6<br>
#2  0x00000000004c754c in flog_encode_msg (ctx=0x676ca0 &lt;flog_ctx&gt;,<br>
nargs=nargs@entry=7, mask=mask@entry=65, <br>
    format=format@entry=0x534888 &quot;\t%s: ino %d peer_ino %d family %4d<br>
type %4d state %2d name %s\n&quot;) at flog/src/flog.c:202<br>
#3  0x00000000004a7b62 in show_one_unix (act=act@entry=0x510c69<br>
&quot;Collected&quot;, sk=sk@entry=0xf7ab90) at criu/sk-unix.c:133<br>
#4  0x00000000004ac664 in unix_collect_one (ns=0x7ffe4151e830,<br>
tb=0x7ffe4151e660, m=0x56b490 &lt;buf+1968&gt;) at criu/sk-unix.c:764<br>
#5  unix_receive_one (h=h@entry=0x56b480 &lt;buf+1952&gt;,<br>
ns=ns@entry=0x7ffe4151e830, arg=arg@entry=0x7ffe4151e7c0) at<br>
criu/sk-unix.c:783<br>
#6  0x000000000046bc4f in nlmsg_receive (buf=&lt;optimized out&gt;,<br>
arg=&lt;optimized out&gt;, ns=&lt;optimized out&gt;, err_cb=&lt;optimized out&gt;,<br>
cb=&lt;optimized out&gt;, len=&lt;optimized out&gt;)<br>
    at criu/libnetlink.c:41<br>
#7  do_rtnl_req (nl=nl@entry=7, req=req@entry=0x7ffe4151e7b0,<br>
size=size@entry=72, receive_callback=0x4ac3e0 &lt;unix_receive_one&gt;,<br>
error_callback=error_callback@entry=0x4ad810 &lt;collect_err&gt;, <br>
    ns=ns@entry=0x7ffe4151e830, arg=0x7ffe4151e7c0) at<br>
criu/libnetlink.c:120<br>
#8  0x00000000004af6dc in do_collect_req (arg=0x7ffe4151e7c0,<br>
ns=&lt;optimized out&gt;, error_callback=0x4ad810 &lt;collect_err&gt;,<br>
receive_callback=&lt;optimized out&gt;, size=72, req=0x7ffe4151e7b0, nl=7)<br>
    at criu/sockets.c:817<br>
#9  collect_sockets (ns=ns@entry=0x7ffe4151e830) at criu/sockets.c:817<br>
#10 0x000000000043d3fd in check_sock_diag () at criu/cr-check.c:127<br>
#11 0x000000000043f29c in cr_check () at criu/cr-check.c:1331<br>
#12 0x0000000000424b37 in main (argc=&lt;optimized out&gt;,<br>
argv=0x7ffe4151eba8, envp=&lt;optimized out&gt;) at criu/crtools.c:257<br>
<br>
Have you tried to run zdtm tests with this binary logging?<br>
<br>
How do you handle log messages from parasite and restorer?<br>
<br>
Did you run any perfomance tests to compare the text and bin logging<br>
engines?<br>
<br>
&gt; <br>
&gt; P.S. Thank you for the possibility to work with the project!<br>
<br>
&gt; From 515a0edf4ffdbfc173e237553e29eae993e8aab6 Mon Sep 17 00:00:00 2001<br>
&gt; From: Anastasia Markina &lt;<a href="mailto:asyamarkina2@gmail.com" target="_blank" rel="noreferrer">asyamarkina2@gmail.com</a>&gt;<br>
&gt; Date: Thu, 25 Jul 2019 15:51:04 +0300<br>
&gt; Subject: [PATCH] criu binary logging based on slightly modified flog engine<br>
&gt;  merging flog code into criu and making the first working version with binlog<br>
&gt;  log file name is specified after the --log-file key as usual; added --binlog<br>
&gt;  option to save binary log instead of text; added --print-log option to print<br>
&gt;  messages from binary log file and exit added documentation about binlog<br>
&gt;<br>
<br>
SOB is required.<br>
<a href="https://criu.org/How_to_submit_patches" rel="noreferrer noreferrer" target="_blank">https://criu.org/How_to_submit_patches</a><br>
<br>
&gt; ---<br>
&gt;  Documentation/binlog.txt  |  52 +++++++++<br>
&gt;  Makefile                  |  13 +++<br>
&gt;  criu/Makefile             |   1 +<br>
&gt;  criu/Makefile.packages    |   2 +-<br>
&gt;  criu/config.c             |   2 +<br>
&gt;  criu/crtools.c            | 111 ++++++++++++-------<br>
&gt;  criu/include/cr_options.h |   2 +<br>
&gt;  criu/include/criu-log.h   |   1 +<br>
&gt;  criu/include/log.h        |  36 +++++-<br>
&gt;  criu/log.c                |  40 +++++--<br>
&gt;  flog/Makefile             |   8 ++<br>
&gt;  flog/include/flog.h       |   9 ++<br>
&gt;  flog/include/uapi/flog.h  | 168 ++++++++++++++++++++++++++++<br>
&gt;  flog/src/flog.c           | 227 ++++++++++++++++++++++++++++++++++++++<br>
<br>
This patch has to be splitted on five patches or maybe even more:<br>
1. Merge flog<br>
2. Makefile changes<br>
3. Introduce the binlog option<br>
4. Integrate flog with CRIU<br>
5. Documentations<br>
<br>
In addition, we need to add a test for this binary logging.<br>
<br>
&gt;  14 files changed, 620 insertions(+), 52 deletions(-)<br>
&gt;  create mode 100644 Documentation/binlog.txt<br>
&gt;  create mode 100644 flog/Makefile<br>
&gt;  create mode 100644 flog/include/flog.h<br>
&gt;  create mode 100644 flog/include/uapi/flog.h<br>
&gt;  create mode 100644 flog/src/flog.c<br>
&gt; <br>
&gt; diff --git a/Documentation/binlog.txt b/Documentation/binlog.txt<br>
&gt; new file mode 100644<br>
&gt; index 00000000..1a5c80c1<br>
&gt; --- /dev/null<br>
&gt; +++ b/Documentation/binlog.txt<br>
&gt; @@ -0,0 +1,52 @@<br>
&gt; +CRIU binary logging<br>
&gt; +===================<br>
&gt; +<br>
&gt; +Usage: turning on binary logging<br>
&gt; +--------------------------------<br>
&gt; +<br>
&gt; +Classical text logging is done by default.<br>
&gt; +Binary logging is turned on by the `--binlog` command line parameter.<br>
&gt; +It should be combined with `--log-file filename` parameter to specify<br>
&gt; +the log file name (this second parameter is used with both text and binary logging).<br>
&gt; +<br>
&gt; +The example:<br>
&gt; +<br>
&gt; +`criu --log-file /tmp/sdf.bin --binlog check`<br>
&gt; +<br>
&gt; +Usage: reading binary log file<br>
&gt; +------------------------------<br>
&gt; +<br>
&gt; +There is a `--print-log` parameter which turnes criy into a log reader.<br>
&gt; +It should be combined with `--log-file filename` parameter, which specifies<br>
&gt; +the log file name. When these parameters are used, CRIU prints all messages<br>
&gt; +from the binary log file on the screen and exits. This looks as follows:<br>
&gt; +<br>
&gt; +`criu --log-file /tmp/sdf.bin --binlog --print-log`<br>
&gt; +<br>
&gt; +Binary log file format<br>
&gt; +----------------------<br>
&gt; +<br>
&gt; +Text logging in CRIU is based on the printf functions family, with format string<br>
&gt; +followed by arguments. To make logging in binary form faster, CRIU just stores<br>
&gt; +format string and arguments in binary log without parsing. When binary log file<br>
&gt; +is read with `--print-log` parameter, these data are taken from the binary log<br>
&gt; +records and passed to a printf function.<br>
&gt; +<br>
&gt; +Binary log file format is highly inspired by the [flog experimental project](<a href="https://github.com/cyrillos/flog" rel="noreferrer noreferrer" target="_blank">https://github.com/cyrillos/flog</a>).<br>
&gt; +Each log message is stored as a format string aned a variable number of<br>
&gt; +arguments. The variable size record has following structure:<br>
&gt; +<br>
&gt; +<br>
&gt; +| field   | datatype     |  explanation                    |<br>
&gt; +|---------|--------------|---------------------------------|<br>
&gt; +| magic   | unsigned int | magic code, equal to `0x676f6c66` |<br>
&gt; +| version | unsigned int | binlog file version, equal to `1` |<br>
&gt; +| size    | unsigned int | actual size of the record (is needed because of variable arguments|<br>
&gt; +| nargs   | unsigned int | number of arguments |<br>
&gt; +| mask    | unsigned int | mask, which encodes the argument type, e.g. long integer or string|<br>
&gt; +| fmt     | long         | offset to the format string, starting from the beginning of the record|<br>
&gt; +| args[0] | long         | 1st argument: the value, if it is a number, or the offset to the string, starting from the beginning of the record |<br>
&gt; +| .....   |              | |<br>
&gt; +| args[nargs-1] | long   | last argument: the value, if it is a number, or the offset to the string, starting from the beginning of the record |<br>
&gt; +| .....   |              | format string and all other strings passed as arguments, if any |<br>
&gt; +<br>
&gt; diff --git a/Makefile b/Makefile<br>
&gt; index 0140330e..c2091a88 100644<br>
&gt; --- a/Makefile<br>
&gt; +++ b/Makefile<br>
&gt; @@ -127,6 +127,8 @@ ifeq ($(GMON),1)<br>
&gt;  export GMON GMONLDOPT<br>
&gt;  endif<br>
&gt;  <br>
&gt; +CFLAGS                       += -iquote flog/include/ -iquote flog/include/uapi<br>
&gt; +<br>
&gt;  AFLAGS                       += -D__ASSEMBLY__<br>
&gt;  CFLAGS                       += $(USERCFLAGS) $(WARNINGS) $(DEFINES) -iquote include/<br>
&gt;  HOSTCFLAGS           += $(WARNINGS) $(DEFINES) -iquote include/<br>
&gt; @@ -218,6 +220,17 @@ soccr/built-in.o: $(CONFIG_HEADER) .FORCE<br>
&gt;  $(SOCCR_A): |soccr/built-in.o<br>
&gt;  criu-deps    += $(SOCCR_A)<br>
&gt;  <br>
&gt; +#<br>
&gt; +# Fast logging library<br>
&gt; +FLOG_A := flog/libflog.a<br>
&gt; +flog/Makefile: ;<br>
&gt; +flog/%: $(CONFIG_HEADER) .FORCE<br>
&gt; +     $(Q) $(MAKE) $(build)=flog $@<br>
&gt; +flog/built-in.o: $(CONFIG_HEADER) .FORCE<br>
&gt; +     $(Q) $(MAKE) $(build)=flog all<br>
&gt; +$(FLOG_A): | flog/built-in.o<br>
&gt; +criu-deps    += $(FLOG_A)<br>
&gt; +<br>
&gt;  #<br>
&gt;  # CRIU building done in own directory<br>
&gt;  # with slightly different rules so we<br>
&gt; diff --git a/criu/Makefile b/criu/Makefile<br>
&gt; index 4134e505..ee0aa291 100644<br>
&gt; --- a/criu/Makefile<br>
&gt; +++ b/criu/Makefile<br>
&gt; @@ -70,6 +70,7 @@ PROGRAM-BUILTINS    += images/built-in.o<br>
&gt;  PROGRAM-BUILTINS     += $(obj)/built-in.o<br>
&gt;  PROGRAM-BUILTINS     += $(ARCH-LIB)<br>
&gt;  PROGRAM-BUILTINS     += soccr/libsoccr.a<br>
&gt; +PROGRAM-BUILTINS     += flog/libflog.a<br>
&gt;  PROGRAM-BUILTINS     += $(COMPEL_LIBS)<br>
&gt;  <br>
&gt;  $(obj)/built-in.o: pie<br>
&gt; diff --git a/criu/Makefile.packages b/criu/Makefile.packages<br>
&gt; index f380fa2f..65111f9c 100644<br>
&gt; --- a/criu/Makefile.packages<br>
&gt; +++ b/criu/Makefile.packages<br>
&gt; @@ -32,7 +32,7 @@ REQ-DEB-PKG-NAMES   += python-future<br>
&gt;  REQ-RPM-PKG-TEST-NAMES       += $(PYTHON)-pyyaml<br>
&gt;  endif<br>
&gt;  <br>
&gt; -export LIBS          += -lprotobuf-c -ldl -lnl-3 -lsoccr -Lsoccr/ -lnet<br>
&gt; +export LIBS          += -lprotobuf-c -ldl -lnl-3 -lsoccr -Lsoccr/ -lnet -lffi<br>
&gt;  <br>
&gt;  check-packages-failed:<br>
&gt;       $(warning Can not find some of the required libraries)<br>
&gt; diff --git a/criu/config.c b/criu/config.c<br>
&gt; index 3a54afd4..7d79c638 100644<br>
&gt; --- a/criu/config.c<br>
&gt; +++ b/criu/config.c<br>
&gt; @@ -458,6 +458,8 @@ int parse_options(int argc, char **argv, bool *usage_error,<br>
&gt;               BOOL_OPT(SK_EST_PARAM, &amp;opts.tcp_established_ok),<br>
&gt;               { &quot;close&quot;,                      required_argument,      0, 1043 },<br>
&gt;               BOOL_OPT(&quot;log-pid&quot;, &amp;opts.log_file_per_pid),<br>
&gt; +             BOOL_OPT(&quot;binlog&quot;, &amp;opts.binlog),<br>
&gt; +             BOOL_OPT(&quot;print-log&quot;, &amp;opts.printlog),<br>
&gt;               { &quot;version&quot;,                    no_argument,            0, &#39;V&#39;  },<br>
&gt;               BOOL_OPT(&quot;evasive-devices&quot;, &amp;opts.evasive_devices),<br>
&gt;               { &quot;pidfile&quot;,                    required_argument,      0, 1046 },<br>
&gt; diff --git a/criu/crtools.c b/criu/crtools.c<br>
&gt; index 97a6d6d6..010d8d11 100644<br>
&gt; --- a/criu/crtools.c<br>
&gt; +++ b/criu/crtools.c<br>
&gt; @@ -48,6 +48,14 @@<br>
&gt;  #include &quot;sysctl.h&quot;<br>
&gt;  #include &quot;img-remote.h&quot;<br>
&gt;  <br>
&gt; +flog_ctx_t flog_ctx;<br>
&gt; +<br>
&gt; +<br>
&gt; +int criu_quit(int ret_val) {<br>
&gt; +     log_fini();     <br>
&gt; +     exit(ret_val);<br>
&gt; +}<br>
&gt; +<br>
&gt;  int main(int argc, char *argv[], char *envp[])<br>
&gt;  {<br>
&gt;       int ret = -1;<br>
&gt; @@ -55,7 +63,14 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;       bool has_exec_cmd = false;<br>
&gt;       bool has_sub_command;<br>
&gt;       int state = PARSING_GLOBAL_CONF;<br>
&gt; +     <br>
&gt; +     flog_ctx.readonly=1; <br>
&gt; +     /* It is not known yet if we will have binlog, but let&#39;s not try to <br>
&gt; +      * write into it for safety reasons <br>
&gt; +      */<br>
&gt; +      <br>
&gt;  <br>
&gt; +     /* FIXME: where to put flog_fini? */<br>
&gt;       BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32);<br>
&gt;       BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR);<br>
&gt;       /* We use it for fd overlap handling in clone_service_fd() */<br>
&gt; @@ -67,20 +82,34 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;  <br>
&gt;       cr_pb_init();<br>
&gt;       setproctitle_init(argc, argv, envp);<br>
&gt; -<br>
&gt; -     if (argc &lt; 2)<br>
&gt; +     <br>
&gt; +     if (argc &lt; 2) {<br>
&gt;               goto usage;<br>
&gt; -<br>
&gt; +     }<br>
&gt; +     <br>
&gt;       init_opts();<br>
&gt;  <br>
&gt; -<br>
&gt;       ret = parse_options(argc, argv, &amp;usage_error, &amp;has_exec_cmd, state);<br>
&gt;  <br>
&gt; +     if (opts.printlog) {<br>
&gt; +                             <br>
&gt; +             if (log_init(opts.output))<br>
&gt; +                     return 1;       <br>
&gt; +             /* print and exit */<br>
&gt; +             printf(&quot;--- printing log from %s file ---\n&quot;, opts.output);<br>
&gt; +             struct stat st;                 <br>
&gt; +             stat(opts.output, &amp;st);                 <br>
&gt; +             if(st.st_size) <br>
&gt; +                     flog_decode_all(&amp;flog_ctx, STDOUT_FILENO);<br>
&gt; +             printf(&quot;--- end of the log ---\n&quot;);<br>
&gt; +             criu_quit(0);<br>
&gt; +     }<br>
&gt; +     <br>
&gt;       if (ret == 1)<br>
&gt; -             return 1;<br>
&gt; +             return 1;       <br>
&gt;       if (ret == 2)<br>
&gt; -             goto usage;<br>
&gt; -<br>
&gt; +             goto usage;     <br>
&gt; +     <br>
&gt;       log_set_loglevel(opts.log_level);<br>
&gt;  <br>
&gt;       if (!strcmp(argv[1], &quot;swrk&quot;)) {<br>
&gt; @@ -98,6 +127,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;  <br>
&gt;       if (check_options()) {<br>
&gt;               flush_early_log_buffer(STDERR_FILENO);<br>
&gt; +             flog_fini(&amp;flog_ctx);<br>
&gt;               return 1;<br>
&gt;       }<br>
&gt;  <br>
&gt; @@ -106,7 +136,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;  <br>
&gt;       if (opts.work_dir == NULL)<br>
&gt;               SET_CHAR_OPTS(work_dir, opts.imgs_dir);<br>
&gt; -<br>
&gt; +     <br>
&gt;       if (optind &gt;= argc) {<br>
&gt;               pr_msg(&quot;Error: command is required\n&quot;);<br>
&gt;               goto usage;<br>
&gt; @@ -131,8 +161,8 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;               }<br>
&gt;  <br>
&gt;               opts.exec_cmd = xmalloc((argc - optind) * sizeof(char *));<br>
&gt; -             if (!opts.exec_cmd)<br>
&gt; -                     return 1;<br>
&gt; +             if (!opts.exec_cmd) <br>
&gt; +                     criu_quit(1);<br>
&gt;               memcpy(opts.exec_cmd, &amp;argv[optind + 1], (argc - optind - 1) * sizeof(char *));<br>
&gt;               opts.exec_cmd[argc - optind - 1] = NULL;<br>
&gt;       } else {<br>
&gt; @@ -148,7 +178,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;       if (strcmp(argv[optind], &quot;service&quot;)) {<br>
&gt;               ret = open_image_dir(opts.imgs_dir);<br>
&gt;               if (ret &lt; 0)<br>
&gt; -                     return 1;<br>
&gt; +                     criu_quit(1);<br>
&gt;       }<br>
&gt;  <br>
&gt;       /*<br>
&gt; @@ -163,14 +193,14 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;  <br>
&gt;       if (chdir(opts.work_dir)) {<br>
&gt;               pr_perror(&quot;Can&#39;t change directory to %s&quot;, opts.work_dir);<br>
&gt; -             return 1;<br>
&gt; +             criu_quit(1);<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (log_init(opts.output))<br>
&gt; -             return 1;<br>
&gt; -<br>
&gt; +             return 1;               <br>
&gt; +     <br>
&gt;       if (kerndat_init())<br>
&gt; -             return 1;<br>
&gt; +             criu_quit(1);<br>
&gt;  <br>
&gt;       if (opts.deprecated_ok)<br>
&gt;               pr_debug(&quot;DEPRECATED ON\n&quot;);<br>
&gt; @@ -178,7 +208,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;       if (!list_empty(&amp;opts.inherit_fds)) {<br>
&gt;               if (strcmp(argv[optind], &quot;restore&quot;)) {<br>
&gt;                       pr_err(&quot;--inherit-fd is restore-only option\n&quot;);<br>
&gt; -                     return 1;<br>
&gt; +                     criu_quit(1);<br>
&gt;               }<br>
&gt;               /* now that log file is set up, print inherit fd list */<br>
&gt;               inherit_fd_log();<br>
&gt; @@ -190,7 +220,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;       if (!strcmp(argv[optind], &quot;dump&quot;)) {<br>
&gt;               if (!opts.tree_id)<br>
&gt;                       goto opt_pid_missing;<br>
&gt; -             return cr_dump_tasks(opts.tree_id);<br>
&gt; +             criu_quit(cr_dump_tasks(opts.tree_id));<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;pre-dump&quot;)) {<br>
&gt; @@ -199,10 +229,10 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;  <br>
&gt;               if (opts.lazy_pages) {<br>
&gt;                       pr_err(&quot;Cannot pre-dump with --lazy-pages\n&quot;);<br>
&gt; -                     return 1;<br>
&gt; +                     criu_quit(1);<br>
&gt;               }<br>
&gt;  <br>
&gt; -             return cr_pre_dump_tasks(opts.tree_id) != 0;<br>
&gt; +             criu_quit(cr_pre_dump_tasks(opts.tree_id) != 0);<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;restore&quot;)) {<br>
&gt; @@ -217,39 +247,41 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;                       ret = 1;<br>
&gt;               }<br>
&gt;  <br>
&gt; -             return ret != 0;<br>
&gt; +             criu_quit(ret != 0);<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;lazy-pages&quot;))<br>
&gt; -             return cr_lazy_pages(opts.daemon_mode) != 0;<br>
&gt; +             criu_quit(cr_lazy_pages(opts.daemon_mode) != 0);<br>
&gt;  <br>
&gt; -     if (!strcmp(argv[optind], &quot;check&quot;))<br>
&gt; -             return cr_check() != 0;<br>
&gt; +     if (!strcmp(argv[optind], &quot;check&quot;)) {<br>
&gt; +             ret = cr_check();               <br>
&gt; +             criu_quit(ret != 0);<br>
&gt; +     }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;page-server&quot;))<br>
&gt; -             return cr_page_server(opts.daemon_mode, false, -1) != 0;<br>
&gt; +             criu_quit(cr_page_server(opts.daemon_mode, false, -1) != 0);<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;image-cache&quot;)) {<br>
&gt;               if (!opts.port)<br>
&gt;                       goto opt_port_missing;<br>
&gt; -             return image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET);<br>
&gt; +             criu_quit(image_cache(opts.daemon_mode, DEFAULT_CACHE_SOCKET));<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;image-proxy&quot;)) {<br>
&gt;               if (!opts.addr) {<br>
&gt;                       pr_msg(&quot;Error: address not specified\n&quot;);<br>
&gt; -                     return 1;<br>
&gt; +                     criu_quit(1);<br>
&gt;               }<br>
&gt;               if (!opts.port)<br>
&gt;                       goto opt_port_missing;<br>
&gt; -             return image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET);<br>
&gt; +             criu_quit(image_proxy(opts.daemon_mode, DEFAULT_PROXY_SOCKET));<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;service&quot;))<br>
&gt; -             return cr_service(opts.daemon_mode);<br>
&gt; +             criu_quit(cr_service(opts.daemon_mode));<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;dedup&quot;))<br>
&gt; -             return cr_dedup() != 0;<br>
&gt; +             criu_quit(cr_dedup() != 0);<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;cpuinfo&quot;)) {<br>
&gt;               if (!argv[optind + 1]) {<br>
&gt; @@ -259,22 +291,23 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;               if (!strcmp(argv[optind + 1], &quot;dump&quot;))<br>
&gt;                       return cpuinfo_dump();<br>
&gt;               else if (!strcmp(argv[optind + 1], &quot;check&quot;))<br>
&gt; -                     return cpuinfo_check();<br>
&gt; +                     criu_quit(cpuinfo_check());<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;exec&quot;)) {<br>
&gt;               pr_msg(&quot;The \&quot;exec\&quot; action is deprecated by the Compel library.\n&quot;);<br>
&gt; -             return -1;<br>
&gt; +             criu_quit(-1);<br>
&gt;       }<br>
&gt;  <br>
&gt;       if (!strcmp(argv[optind], &quot;show&quot;)) {<br>
&gt;               pr_msg(&quot;The \&quot;show\&quot; action is deprecated by the CRIT utility.\n&quot;);<br>
&gt;               pr_msg(&quot;To view an image use the \&quot;crit decode -i $name --pretty\&quot; command.\n&quot;);<br>
&gt; -             return -1;<br>
&gt; +             criu_quit(-1);<br>
&gt;       }<br>
&gt; -<br>
&gt; +     <br>
&gt;       pr_msg(&quot;Error: unknown command: %s\n&quot;, argv[optind]);<br>
&gt;  usage:<br>
&gt; +     printf(&quot;usage\n&quot;);<br>
&gt;       pr_msg(&quot;\n&quot;<br>
&gt;  &quot;Usage:\n&quot;<br>
&gt;  &quot;  criu dump|pre-dump -t PID [&lt;options&gt;]\n&quot;<br>
&gt; @@ -302,8 +335,8 @@ usage:<br>
&gt;       );<br>
&gt;  <br>
&gt;       if (usage_error) {<br>
&gt; -             pr_msg(&quot;\nTry -h|--help for more info\n&quot;);<br>
&gt; -             return 1;<br>
&gt; +             pr_msg(&quot;\nTry -h|--help for more info\n&quot;);                              <br>
&gt; +             criu_quit(1);<br>
&gt;       }<br>
&gt;  <br>
&gt;       pr_msg(&quot;\n&quot;<br>
&gt; @@ -425,7 +458,9 @@ usage:<br>
&gt;  &quot;\n&quot;<br>
&gt;  &quot;* Logging:\n&quot;<br>
&gt;  &quot;  -o|--log-file FILE    log file name\n&quot;<br>
&gt; +&quot;     --binlog           use faster binary log instead of slower text log\n&quot;<br>
&gt;  &quot;     --log-pid          enable per-process logging to separate FILE.pid files\n&quot;<br>
&gt; +&quot;     --print-log        print log from the binlog file and exit\n&quot;<br>
&gt;  &quot;  -v[v...]|--verbosity  increase verbosity (can use multiple v)\n&quot;<br>
&gt;  &quot;  -vNUM|--verbosity=NUM set verbosity to NUM (higher level means more output):\n&quot;<br>
&gt;  &quot;                          -v1 - only errors and messages\n&quot;<br>
&gt; @@ -466,13 +501,13 @@ usage:<br>
&gt;  &quot;  -V|--version          show version\n&quot;<br>
&gt;       );<br>
&gt;  <br>
&gt; -     return 0;<br>
&gt; +     criu_quit(0);<br>
&gt;  <br>
&gt;  opt_port_missing:<br>
&gt;       pr_msg(&quot;Error: port not specified\n&quot;);<br>
&gt; -     return 1;<br>
&gt; +     criu_quit(1);<br>
&gt;  <br>
&gt;  opt_pid_missing:<br>
&gt;       pr_msg(&quot;Error: pid not specified\n&quot;);<br>
&gt; -     return 1;<br>
&gt; +     criu_quit(1);<br>
&gt;  }<br>
&gt; diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h<br>
&gt; index c519c740..a79bdebc 100644<br>
&gt; --- a/criu/include/cr_options.h<br>
&gt; +++ b/criu/include/cr_options.h<br>
&gt; @@ -81,6 +81,8 @@ struct cr_options {<br>
&gt;       int                     evasive_devices;<br>
&gt;       int                     link_remap_ok;<br>
&gt;       int                     log_file_per_pid;<br>
&gt; +     int                     binlog;<br>
&gt; +     int                     printlog;<br>
&gt;       bool                    swrk_restore;<br>
&gt;       char                    *output;<br>
&gt;       char                    *root;<br>
&gt; diff --git a/criu/include/criu-log.h b/criu/include/criu-log.h<br>
&gt; index 21ef5430..57792a44 100644<br>
&gt; --- a/criu/include/criu-log.h<br>
&gt; +++ b/criu/include/criu-log.h<br>
&gt; @@ -25,6 +25,7 @@<br>
&gt;  struct timeval;<br>
&gt;  <br>
&gt;  extern int log_init(const char *output);<br>
&gt; +<br>
&gt;  extern void log_fini(void);<br>
&gt;  extern int log_init_by_pid(pid_t pid);<br>
&gt;  extern void log_closedir(void);<br>
&gt; diff --git a/criu/include/log.h b/criu/include/log.h<br>
&gt; index 15787b09..08c57381 100644<br>
&gt; --- a/criu/include/log.h<br>
&gt; +++ b/criu/include/log.h<br>
&gt; @@ -9,6 +9,11 @@<br>
&gt;  #include &lt;errno.h&gt;<br>
&gt;  #include &lt;stdarg.h&gt;<br>
&gt;  <br>
&gt; +#include &quot;flog.h&quot;<br>
&gt; +<br>
&gt; +/* FIXME: Should be in options? */<br>
&gt; +extern flog_ctx_t flog_ctx;<br>
&gt; +<br>
&gt;  extern void vprint_on_level(unsigned int loglevel, const char *format,<br>
&gt;               va_list params);<br>
&gt;  <br>
&gt; @@ -32,35 +37,53 @@ extern void print_on_level(unsigned int loglevel, const char *format, ...)<br>
&gt;  <br>
&gt;  void flush_early_log_buffer(int fd);<br>
&gt;  <br>
&gt; +#ifdef CR_NOGLIBC<br>
&gt; +#undef flog_encode<br>
&gt; +#define flog_encode(ctx, fmt, ...)<br>
&gt; +#endif<br>
&gt; +<br>
&gt;  #define print_once(loglevel, fmt, ...)                                       \<br>
&gt;       do {                                                            \<br>
&gt;               static bool __printed;                                  \<br>
&gt;               if (!__printed) {                                       \<br>
&gt; +                     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);     \<br>
&gt;                       print_on_level(loglevel, fmt, ##__VA_ARGS__);   \<br>
&gt;                       __printed = 1;                                  \<br>
&gt;               }                                                       \<br>
&gt;       } while (0)<br>
&gt;  <br>
&gt;  #define pr_msg(fmt, ...)                                             \<br>
&gt; +     do {                                                            \<br>
&gt; +     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);                     \<br>
&gt;       print_on_level(LOG_MSG,                                         \<br>
&gt; -                    fmt, ##__VA_ARGS__)<br>
&gt; +                    fmt, ##__VA_ARGS__);                             \<br>
&gt; +     } while (0)<br>
&gt;  <br>
&gt;  #define pr_info(fmt, ...)                                            \<br>
&gt; +     do {                                                            \<br>
&gt; +     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);                     \<br>
&gt;       print_on_level(LOG_INFO,                                        \<br>
&gt; -                    LOG_PREFIX fmt, ##__VA_ARGS__)<br>
&gt; +                    LOG_PREFIX fmt, ##__VA_ARGS__);                  \<br>
&gt; +     } while (0)<br>
&gt;  <br>
&gt;  #define pr_err(fmt, ...)                                             \<br>
&gt; +     do {                                                            \<br>
&gt; +     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);                     \<br>
&gt;       print_on_level(LOG_ERROR,                                       \<br>
&gt;                      &quot;Error (%s:%d): &quot; LOG_PREFIX fmt,                \<br>
&gt; -                    __FILE__, __LINE__, ##__VA_ARGS__)<br>
&gt; +                    __FILE__, __LINE__, ##__VA_ARGS__);              \<br>
&gt; +     } while (0)<br>
&gt;  <br>
&gt;  #define pr_err_once(fmt, ...)                                                \<br>
&gt;       print_once(LOG_ERROR, fmt, ##__VA_ARGS__)<br>
&gt;  <br>
&gt;  #define pr_warn(fmt, ...)                                            \<br>
&gt; +     do {                                                            \<br>
&gt; +     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);                     \<br>
&gt;       print_on_level(LOG_WARN,                                        \<br>
&gt;                      &quot;Warn  (%s:%d): &quot; LOG_PREFIX fmt,                \<br>
&gt; -                    __FILE__, __LINE__, ##__VA_ARGS__)<br>
&gt; +                    __FILE__, __LINE__, ##__VA_ARGS__);              \<br>
&gt; +     } while (0)<br>
&gt;  <br>
&gt;  #define pr_warn_once(fmt, ...)                                               \<br>
&gt;         print_once(LOG_WARN,                                          \<br>
&gt; @@ -68,8 +91,11 @@ void flush_early_log_buffer(int fd);<br>
&gt;                       __FILE__, __LINE__, ##__VA_ARGS__)<br>
&gt;  <br>
&gt;  #define pr_debug(fmt, ...)                                           \<br>
&gt; +     do {                                                            \<br>
&gt; +     flog_encode(&amp;flog_ctx, fmt, ##__VA_ARGS__);                     \<br>
&gt;       print_on_level(LOG_DEBUG,                                       \<br>
&gt; -                    LOG_PREFIX fmt, ##__VA_ARGS__)<br>
&gt; +                    LOG_PREFIX fmt, ##__VA_ARGS__);                  \<br>
&gt; +     } while (0)<br>
&gt;  <br>
&gt;  #ifndef CR_NOGLIBC<br>
&gt;  <br>
&gt; diff --git a/criu/log.c b/criu/log.c<br>
&gt; index 8bdf8353..723fe72d 100644<br>
&gt; --- a/criu/log.c<br>
&gt; +++ b/criu/log.c<br>
&gt; @@ -10,6 +10,7 @@<br>
&gt;  #include &lt;sys/time.h&gt;<br>
&gt;  #include &lt;sys/resource.h&gt;<br>
&gt;  #include &lt;sys/utsname.h&gt;<br>
&gt; +#include &lt;sys/stat.h&gt;<br>
&gt;  <br>
&gt;  #include &lt;fcntl.h&gt;<br>
&gt;  <br>
&gt; @@ -207,10 +208,11 @@ void flush_early_log_buffer(int fd)<br>
&gt;  int log_init(const char *output)<br>
&gt;  {<br>
&gt;       int new_logfd, fd;<br>
&gt; +     int log_file_open_mode, log_file_perm;<br>
&gt;  <br>
&gt;       gettimeofday(&amp;start, NULL);<br>
&gt;       reset_buf_off();<br>
&gt; -<br>
&gt; +     memset(&amp;flog_ctx, 0, sizeof(flog_ctx));<br>
&gt;       if (output &amp;&amp; !strncmp(output, &quot;-&quot;, 2)) {<br>
&gt;               new_logfd = dup(STDOUT_FILENO);<br>
&gt;               if (new_logfd &lt; 0) {<br>
&gt; @@ -218,7 +220,23 @@ int log_init(const char *output)<br>
&gt;                       return -1;<br>
&gt;               }<br>
&gt;       } else if (output) {<br>
&gt; -             new_logfd = open(output, O_CREAT|O_TRUNC|O_WRONLY|O_APPEND, 0600);<br>
&gt; +             if (opts.printlog) {<br>
&gt; +                     /* read old binlog */<br>
&gt; +                     log_file_open_mode = O_RDONLY;<br>
&gt; +                     flog_ctx.readonly=1; /* don&#39;t try to write into it */<br>
&gt; +                     struct stat st;                 <br>
&gt; +                     stat(opts.output, &amp;st);                 <br>
&gt; +                     flog_ctx.size=st.st_size;<br>
&gt; +             }<br>
&gt; +             else if (opts.binlog) {<br>
&gt; +                     log_file_open_mode = O_RDWR | O_CREAT | O_TRUNC;<br>
&gt; +             }<br>
&gt; +             else {<br>
&gt; +                     flog_ctx.readonly=1; /* no binlog, don&#39;t try to write into it */<br>
&gt; +                     log_file_open_mode = O_CREAT|O_TRUNC|O_WRONLY|O_APPEND;<br>
&gt; +             }<br>
&gt; +             log_file_perm = opts.binlog?0644:0600;<br>
&gt; +             new_logfd = open(output, log_file_open_mode, log_file_perm);<br>
&gt;               if (new_logfd &lt; 0) {<br>
&gt;                       pr_perror(&quot;Can&#39;t create log file %s&quot;, output);<br>
&gt;                       return -1;<br>
&gt; @@ -234,15 +252,19 @@ int log_init(const char *output)<br>
&gt;       fd = install_service_fd(LOG_FD_OFF, new_logfd);<br>
&gt;       if (fd &lt; 0)<br>
&gt;               goto err;<br>
&gt; -<br>
&gt; +                     <br>
&gt; +     if (opts.binlog||opts.printlog) {<br>
&gt; +             <br>
&gt; +             flog_map_buf(fd, &amp;flog_ctx);<br>
&gt; +     }<br>
&gt; +     <br>
&gt; +     <br>
&gt;       init_done = 1;<br>
&gt;  <br>
&gt;       /*<br>
&gt;        * Once logging is setup this write out all early log messages.<br>
&gt;        * Only those messages which have to correct log level are printed.<br>
&gt;        */<br>
&gt; -     flush_early_log_buffer(fd);<br>
&gt; -<br>
&gt;       print_versions();<br>
&gt;  <br>
&gt;       return 0;<br>
&gt; @@ -277,7 +299,9 @@ int log_init_by_pid(pid_t pid)<br>
&gt;  <br>
&gt;  void log_fini(void)<br>
&gt;  {<br>
&gt; -     close_service_fd(LOG_FD_OFF);<br>
&gt; +     flog_fini(&amp;flog_ctx);<br>
&gt; +     <br>
&gt; +     <br>
&gt;  }<br>
&gt;  <br>
&gt;  static void soccr_print_on_level(unsigned int loglevel, const char *format, ...)<br>
&gt; @@ -392,9 +416,9 @@ void vprint_on_level(unsigned int loglevel, const char *format, va_list params)<br>
&gt;  }<br>
&gt;  <br>
&gt;  void print_on_level(unsigned int loglevel, const char *format, ...)<br>
&gt; -{<br>
&gt; +{    <br>
&gt;       va_list params;<br>
&gt; -<br>
&gt; +     if (opts.binlog) return;<br>
&gt;       va_start(params, format);<br>
&gt;       vprint_on_level(loglevel, format, params);<br>
&gt;       va_end(params);<br>
&gt; diff --git a/flog/Makefile b/flog/Makefile<br>
&gt; new file mode 100644<br>
&gt; index 00000000..71e398ed<br>
&gt; --- /dev/null<br>
&gt; +++ b/flog/Makefile<br>
&gt; @@ -0,0 +1,8 @@<br>
&gt; +ccflags-y    += -iquote flog/include<br>
&gt; +ccflags-y    += -iquote include<br>
&gt; +ccflags-y    += -fno-strict-aliasing<br>
&gt; +ldflags-y    += -r<br>
&gt; +<br>
&gt; +lib-name := libflog.a<br>
&gt; +<br>
&gt; +lib-y += src/flog.o<br>
&gt; diff --git a/flog/include/flog.h b/flog/include/flog.h<br>
&gt; new file mode 100644<br>
&gt; index 00000000..f00c2054<br>
&gt; --- /dev/null<br>
&gt; +++ b/flog/include/flog.h<br>
&gt; @@ -0,0 +1,9 @@<br>
&gt; +#ifndef __FLOG_H__<br>
&gt; +#define __FLOG_H__<br>
&gt; +<br>
&gt; +#include &lt;string.h&gt;<br>
&gt; +#include &lt;errno.h&gt;<br>
&gt; +<br>
&gt; +#include &quot;uapi/flog.h&quot;<br>
&gt; +<br>
&gt; +#endif /* __FLOG_H__ */<br>
&gt; diff --git a/flog/include/uapi/flog.h b/flog/include/uapi/flog.h<br>
&gt; new file mode 100644<br>
&gt; index 00000000..8ab3566c<br>
&gt; --- /dev/null<br>
&gt; +++ b/flog/include/uapi/flog.h<br>
&gt; @@ -0,0 +1,168 @@<br>
&gt; +#ifndef __UAPI_FLOG_H__<br>
&gt; +#define __UAPI_FLOG_H__<br>
&gt; +<br>
&gt; +#include &lt;stdbool.h&gt;<br>
&gt; +#include &lt;string.h&gt;<br>
&gt; +#include &lt;errno.h&gt;<br>
&gt; +<br>
&gt; +/*<br>
&gt; + * We work with up to 32 arguments in macros here.<br>
&gt; + * If more provided -- behaviour is undefined.<br>
&gt; + */<br>
&gt; +<br>
&gt; +/*<br>
&gt; + * By Laurent Deniau at <a href="https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s" rel="noreferrer noreferrer" target="_blank">https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s</a><br>
&gt; + */<br>
&gt; +#define FLOG_PP_NARG_(...)                   FLOG_PP_ARG_N(__VA_ARGS__)<br>
&gt; +#define FLOG_PP_NARG(...)                    FLOG_PP_NARG_(1, ##__VA_ARGS__, FLOG_PP_RSEQ_N())<br>
&gt; +<br>
&gt; +#define FLOG_PP_ARG_N( _0, _1, _2, _3, _4,   \<br>
&gt; +                    _5, _6, _7, _8, _9,      \<br>
&gt; +                   _10,_11,_12,_13,_14,      \<br>
&gt; +                   _15,_16,_17,_18,_19,      \<br>
&gt; +                   _20,_21,_22,_23,_24,      \<br>
&gt; +                   _25,_26,_27,_28,_29,      \<br>
&gt; +                   _30,_31,  N, ...)         N<br>
&gt; +<br>
&gt; +#define FLOG_PP_RSEQ_N()                     \<br>
&gt; +                    31, 30, 29, 28, 27,      \<br>
&gt; +                    26, 25, 24, 23, 22,      \<br>
&gt; +                    21, 20, 19, 18, 17,      \<br>
&gt; +                    16, 15, 14, 13, 12,      \<br>
&gt; +                    11, 10,  9,  8,  7,      \<br>
&gt; +                     6,  5,  4,  3,  2,      \<br>
&gt; +                     1,  0<br>
&gt; +<br>
&gt; +#define FLOG_GENMASK_0(N, x)         0<br>
&gt; +#define FLOG_GENMASK_1(N,  op, x, ...)        (op(N,  0, x))<br>
&gt; +#define FLOG_GENMASK_2(N,  op, x, ...)       ((op(N,  1, x)) | FLOG_GENMASK_1(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_3(N,  op, x, ...)       ((op(N,  2, x)) | FLOG_GENMASK_2(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_4(N,  op, x, ...)       ((op(N,  3, x)) | FLOG_GENMASK_3(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_5(N,  op, x, ...)       ((op(N,  4, x)) | FLOG_GENMASK_4(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_6(N,  op, x, ...)       ((op(N,  5, x)) | FLOG_GENMASK_5(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_7(N,  op, x, ...)       ((op(N,  6, x)) | FLOG_GENMASK_6(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_8(N,  op, x, ...)       ((op(N,  7, x)) | FLOG_GENMASK_7(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_9(N,  op, x, ...)       ((op(N,  8, x)) | FLOG_GENMASK_8(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_10(N, op, x, ...)       ((op(N,  9, x)) | FLOG_GENMASK_9(N,  op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_11(N, op, x, ...)       ((op(N, 10, x)) | FLOG_GENMASK_10(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_12(N, op, x, ...)       ((op(N, 11, x)) | FLOG_GENMASK_11(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_13(N, op, x, ...)       ((op(N, 12, x)) | FLOG_GENMASK_12(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_14(N, op, x, ...)       ((op(N, 13, x)) | FLOG_GENMASK_13(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_15(N, op, x, ...)       ((op(N, 14, x)) | FLOG_GENMASK_14(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_16(N, op, x, ...)       ((op(N, 15, x)) | FLOG_GENMASK_15(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_17(N, op, x, ...)       ((op(N, 16, x)) | FLOG_GENMASK_16(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_18(N, op, x, ...)       ((op(N, 17, x)) | FLOG_GENMASK_17(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_19(N, op, x, ...)       ((op(N, 18, x)) | FLOG_GENMASK_18(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_20(N, op, x, ...)       ((op(N, 19, x)) | FLOG_GENMASK_19(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_21(N, op, x, ...)       ((op(N, 20, x)) | FLOG_GENMASK_20(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_22(N, op, x, ...)       ((op(N, 21, x)) | FLOG_GENMASK_21(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_23(N, op, x, ...)       ((op(N, 22, x)) | FLOG_GENMASK_22(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_24(N, op, x, ...)       ((op(N, 23, x)) | FLOG_GENMASK_23(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_25(N, op, x, ...)       ((op(N, 24, x)) | FLOG_GENMASK_24(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_26(N, op, x, ...)       ((op(N, 25, x)) | FLOG_GENMASK_25(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_27(N, op, x, ...)       ((op(N, 26, x)) | FLOG_GENMASK_26(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_28(N, op, x, ...)       ((op(N, 27, x)) | FLOG_GENMASK_27(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_29(N, op, x, ...)       ((op(N, 28, x)) | FLOG_GENMASK_28(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_30(N, op, x, ...)       ((op(N, 29, x)) | FLOG_GENMASK_29(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_31(N, op, x, ...)       ((op(N, 30, x)) | FLOG_GENMASK_30(N, op,  __VA_ARGS__))<br>
&gt; +#define FLOG_GENMASK_32(N, op, x, ...)       ((op(N, 31, x)) | FLOG_GENMASK_31(N, op,  __VA_ARGS__))<br>
&gt; +<br>
&gt; +#define FLOG_CONCAT(arg1, arg2)              FLOG_CONCAT1(arg1, arg2)<br>
&gt; +#define FLOG_CONCAT1(arg1, arg2)     FLOG_CONCAT2(arg1, arg2)<br>
&gt; +#define FLOG_CONCAT2(arg1, arg2)     arg1##arg2<br>
&gt; +<br>
&gt; +#define FLOG_GENMASK_(N, op, ...)    FLOG_CONCAT(FLOG_GENMASK_, N)(N, op, ##__VA_ARGS__)<br>
&gt; +#define FLOG_GENMASK(op, ...)                FLOG_GENMASK_(FLOG_PP_NARG(__VA_ARGS__), op, ##__VA_ARGS__)<br>
&gt; +<br>
&gt; +#define flog_genbit(ord, n, v, ...)                                  \<br>
&gt; +     _Generic((v),                                                   \<br>
&gt; +                                                                     \<br>
&gt; +              /* Basic types */                                      \<br>
&gt; +              char:                          0,                      \<br>
&gt; +              signed char:                   0,                      \<br>
&gt; +              unsigned char:                 0,                      \<br>
&gt; +              signed short int:              0,                      \<br>
&gt; +              unsigned short int:            0,                      \<br>
&gt; +              signed int:                    0,                      \<br>
&gt; +              unsigned int:                  0,                      \<br>
&gt; +              signed long:                   0,                      \<br>
&gt; +              unsigned long:                 0,                      \<br>
&gt; +              signed long long:              0,                      \<br>
&gt; +              unsigned long long:            0,                      \<br>
&gt; +                                                                     \<br>
&gt; +              /* Not used for a while */                             \<br>
&gt; +              /* float:                      12, */                  \<br>
&gt; +              /* double:                     13, */                  \<br>
&gt; +              /* long double:                14, */                  \<br>
&gt; +                                                                     \<br>
&gt; +              /* Basic poniters */                                   \<br>
&gt; +              char *:                        (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              signed char *:                 (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              unsigned char *:               (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              signed short int *:            0,                      \<br>
&gt; +              unsigned short int *:          0,                      \<br>
&gt; +              signed int *:                  0,                      \<br>
&gt; +              unsigned int *:                0,                      \<br>
&gt; +              signed long *:                 0,                      \<br>
&gt; +              unsigned long *:               0,                      \<br>
&gt; +              signed long long *:            0,                      \<br>
&gt; +              unsigned long long *:          0,                      \<br>
&gt; +              void *:                        0,                      \<br>
&gt; +                                                                     \<br>
&gt; +              /* Const basic pointers */                             \<br>
&gt; +              const char *:                  (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              const signed char *:           (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              const unsigned char *:         (1u &lt;&lt; (ord - n - 1)),  \<br>
&gt; +              const signed short int *:      0,                      \<br>
&gt; +              const unsigned short int *:    0,                      \<br>
&gt; +              const signed int *:            0,                      \<br>
&gt; +              const unsigned int *:          0,                      \<br>
&gt; +              const signed long *:           0,                      \<br>
&gt; +              const unsigned long *:         0,                      \<br>
&gt; +              const signed long long *:      0,                      \<br>
&gt; +              const unsigned long long *:    0,                      \<br>
&gt; +              const void *:                  0,                      \<br>
&gt; +                                                                     \<br>
&gt; +              /* Systypes and pointers */                            \<br>
&gt; +              default:                       -1)<br>
&gt; +<br>
&gt; +#define FLOG_MAGIC   0x676f6c66<br>
&gt; +#define FLOG_VERSION 1<br>
&gt; +<br>
&gt; +typedef struct {<br>
&gt; +     unsigned int    magic;<br>
&gt; +     unsigned int    version;<br>
&gt; +     unsigned int    size;<br>
&gt; +     unsigned int    nargs;<br>
&gt; +     unsigned int    mask;<br>
&gt; +     long            fmt;<br>
&gt; +     long            args[0];<br>
&gt; +} flog_msg_t;<br>
&gt; +<br>
&gt; +typedef struct {<br>
&gt; +     char            *buf;<br>
&gt; +     char            *pos;<br>
&gt; +     size_t          size;<br>
&gt; +     size_t          left;<br>
&gt; +     int             readonly;<br>
&gt; +} flog_ctx_t;<br>
&gt; +<br>
&gt; +extern int flog_init(flog_ctx_t *ctx);<br>
&gt; +extern void flog_fini(flog_ctx_t *ctx);<br>
&gt; +<br>
&gt; +extern void flog_decode_all(flog_ctx_t *ctx, int fdout);<br>
&gt; +<br>
&gt; +extern int flog_encode_msg(flog_ctx_t *ctx,<br>
&gt; +                        unsigned int nargs, unsigned int mask,<br>
&gt; +                        const char *format, ...);<br>
&gt; +extern int flog_decode_msg(flog_msg_t *m, int fdout);<br>
&gt; +<br>
&gt; +extern int flog_map_buf(int fdout, flog_ctx_t *flog_ctx);<br>
&gt; +<br>
&gt; +#define flog_encode(ctx, fmt, ...)                                           \<br>
&gt; +     flog_encode_msg(ctx,                                                    \<br>
&gt; +                     FLOG_PP_NARG(__VA_ARGS__),                              \<br>
&gt; +                     FLOG_GENMASK(flog_genbit, ##__VA_ARGS__),               \<br>
&gt; +                     fmt, ##__VA_ARGS__)<br>
&gt; +<br>
&gt; +#endif /* __UAPI_FLOG_H__ */<br>
&gt; diff --git a/flog/src/flog.c b/flog/src/flog.c<br>
&gt; new file mode 100644<br>
&gt; index 00000000..9a538795<br>
&gt; --- /dev/null<br>
&gt; +++ b/flog/src/flog.c<br>
&gt; @@ -0,0 +1,227 @@<br>
&gt; +#include &lt;stdio.h&gt;<br>
&gt; +#include &lt;stdlib.h&gt;<br>
&gt; +#include &lt;stdarg.h&gt;<br>
&gt; +#include &lt;unistd.h&gt;<br>
&gt; +#include &lt;stdint.h&gt;<br>
&gt; +<br>
&gt; +#include &lt;sys/param.h&gt;<br>
&gt; +#include &lt;sys/mman.h&gt;<br>
&gt; +<br>
&gt; +#include &lt;ffi.h&gt;<br>
&gt; +<br>
&gt; +#include &quot;common/compiler.h&quot;<br>
&gt; +<br>
&gt; +#include &quot;flog.h&quot;<br>
&gt; +<br>
&gt; +#define BUF_SIZE (1&lt;&lt;20)<br>
&gt; +<br>
&gt; +static char _mbuf[BUF_SIZE];<br>
&gt; +static char *mbuf = _mbuf;<br>
&gt; +static char *fbuf;<br>
&gt; +static uint64_t fsize;<br>
&gt; +static uint64_t mbuf_size = sizeof(_mbuf);<br>
&gt; +static int binlog_fd;<br>
&gt; +<br>
&gt; +int flog_map_buf(int fdout, flog_ctx_t *flog_ctx)<br>
&gt; +{<br>
&gt; +     uint64_t off = 0;<br>
&gt; +     void *addr;<br>
&gt; +     <br>
&gt; +     mbuf_size = 2 * BUF_SIZE;<br>
&gt; +     <br>
&gt; +     if (flog_ctx-&gt;size==0)  flog_ctx-&gt;size=BUF_SIZE;<br>
&gt; +<br>
&gt; +     /*<br>
&gt; +      * Two buffers are mmaped into memory. A new one is mapped when a first<br>
&gt; +      * one is completly filled.<br>
&gt; +      */<br>
&gt; +     if (fbuf &amp;&amp; (mbuf - fbuf &lt; BUF_SIZE))<br>
&gt; +             return 0;<br>
&gt; +<br>
&gt; +     if (fbuf) {<br>
&gt; +             if (munmap(fbuf, BUF_SIZE * 2)) {<br>
&gt; +                     fprintf(stderr, &quot;Unable to unmap a buffer: %m&quot;);<br>
&gt; +                     return 1;<br>
&gt; +             }<br>
&gt; +             off = mbuf - fbuf - BUF_SIZE;<br>
&gt; +             fbuf = NULL;<br>
&gt; +     }<br>
&gt; +<br>
&gt; +     if (fsize == 0)<br>
&gt; +             fsize += BUF_SIZE;<br>
&gt; +     fsize += BUF_SIZE;<br>
&gt; +<br>
&gt; +     if (!flog_ctx-&gt;readonly) {<br>
&gt; +             if (ftruncate(fdout, fsize)) {<br>
&gt; +                     fprintf(stderr, &quot;Unable to truncate a file: %m&quot;);<br>
&gt; +                     return -1;<br>
&gt; +             }<br>
&gt; +     }       <br>
&gt; +     if (!fbuf) {<br>
&gt; +             if (!flog_ctx-&gt;readonly) {                      <br>
&gt; +                     addr = mmap(NULL, BUF_SIZE * 2, PROT_WRITE | PROT_READ,<br>
&gt; +                         MAP_FILE | MAP_SHARED, fdout, fsize - 2 * BUF_SIZE);<br>
&gt; +             }<br>
&gt; +             else {                  <br>
&gt; +                     addr = mmap(NULL, flog_ctx-&gt;size, PROT_READ,<br>
&gt; +                         MAP_FILE | MAP_SHARED, fdout, 0);<br>
&gt; +                     mbuf_size = flog_ctx-&gt;size;<br>
&gt; +             }<br>
&gt; +     }<br>
&gt; +     else {<br>
&gt; +             addr = mremap(fbuf + BUF_SIZE, BUF_SIZE,<br>
&gt; +                             BUF_SIZE * 2, MREMAP_FIXED, fbuf);<br>
&gt; +     }<br>
&gt; +     if (addr == MAP_FAILED) {<br>
&gt; +             fprintf(stderr, &quot;Unable to map a buffer: %m&quot;);<br>
&gt; +             return -1;<br>
&gt; +     }<br>
&gt; +     <br>
&gt; +     fbuf = addr;<br>
&gt; +     mbuf = fbuf + off;<br>
&gt; +<br>
&gt; +     binlog_fd=fdout;<br>
&gt; +     flog_init(flog_ctx);<br>
&gt; +     return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +int flog_init(flog_ctx_t *ctx)<br>
&gt; +{<br>
&gt; +     ctx-&gt;size = ctx-&gt;left = mbuf_size; <br>
&gt; +     ctx-&gt;pos = ctx-&gt;buf = mbuf;<br>
&gt; +     if (!ctx-&gt;buf)<br>
&gt; +             return -ENOMEM;<br>
&gt; +     <br>
&gt; +     return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +void flog_fini(flog_ctx_t *ctx)<br>
&gt; +{<br>
&gt; +     if (mbuf == _mbuf)<br>
&gt; +             return;<br>
&gt; +     munmap(ctx-&gt;buf, BUF_SIZE * 2);<br>
&gt; +     ctx-&gt;size=(size_t) (ctx-&gt;pos - ctx-&gt;buf);<br>
&gt; +     if (!ctx-&gt;readonly) {<br>
&gt; +             if (ftruncate(binlog_fd, ctx-&gt;size)) {<br>
&gt; +                     fprintf(stderr, &quot;Unable to truncate a file: %m&quot;);<br>
&gt; +             }<br>
&gt; +     }<br>
&gt; +     <br>
&gt; +}<br>
&gt; +<br>
&gt; +int flog_decode_msg(flog_msg_t *ro_m, int fdout)<br>
&gt; +{<br>
&gt; +     ffi_type *args[34] = {<br>
&gt; +             [0]             = &amp;ffi_type_sint,<br>
&gt; +             [1]             = &amp;ffi_type_pointer,<br>
&gt; +             [2 ... 33]      = &amp;ffi_type_slong<br>
&gt; +     };<br>
&gt; +     void *values[34];<br>
&gt; +     ffi_cif cif;<br>
&gt; +     ffi_arg rc;<br>
&gt; +     flog_msg_t *m;<br>
&gt; +     size_t i, ret = 0;<br>
&gt; +     char *fmt;<br>
&gt; +<br>
&gt; +     m=malloc(ro_m-&gt;size);<br>
&gt; +     memcpy(m, ro_m, ro_m-&gt;size);<br>
&gt; +     values[0] = (void *)&amp;fdout;<br>
&gt; +     if (m-&gt;magic != FLOG_MAGIC) {<br>
&gt; +             return -EINVAL;<br>
&gt; +     }<br>
&gt; +     if (m-&gt;version != FLOG_VERSION) {<br>
&gt; +             return -EINVAL;<br>
&gt; +     }<br>
&gt; +<br>
&gt; +     fmt = (void *)m + m-&gt;fmt;<br>
&gt; +     values[1] = &amp;fmt;<br>
&gt; +     <br>
&gt; +     for (i = 0; i &lt; m-&gt;nargs; i++) {                <br>
&gt; +             values[i + 2] = (void *)&amp;m-&gt;args[i];<br>
&gt; +             if (m-&gt;mask &amp; (1u &lt;&lt; i)) {<br>
&gt; +                     m-&gt;args[i] = (long)((void *)m + m-&gt;args[i]);<br>
&gt; +             }       <br>
&gt; +             <br>
&gt; +     }<br>
&gt; +     <br>
&gt; +     int sdf=ffi_prep_cif(&amp;cif, FFI_DEFAULT_ABI, m-&gt;nargs + 2,<br>
&gt; +                      &amp;ffi_type_sint, args);<br>
&gt; +     if ( sdf == FFI_OK) {<br>
&gt; +             ffi_call(&amp;cif, FFI_FN(dprintf), &amp;rc, values);<br>
&gt; +     } else<br>
&gt; +             ret = -1;<br>
&gt; +     <br>
&gt; +     free(m);<br>
&gt; +     return ret;<br>
&gt; +}<br>
&gt; +<br>
&gt; +void flog_decode_all(flog_ctx_t *ctx, int fdout)<br>
&gt; +{<br>
&gt; +     flog_msg_t *m;<br>
&gt; +     char *pos;<br>
&gt; +     printf(&quot;log size is %ld\n&quot;, ctx-&gt;size);<br>
&gt; +     if (ctx-&gt;size == 0)<br>
&gt; +             return;<br>
&gt; +     if (ctx-&gt;readonly) ctx-&gt;pos=ctx-&gt;buf + ctx-&gt;size;<br>
&gt; +     for (pos = ctx-&gt;buf; pos &lt; ctx-&gt;pos; ) {<br>
&gt; +             m = (void *)pos;<br>
&gt; +             flog_decode_msg(m ,fdout);<br>
&gt; +             pos += m-&gt;size;<br>
&gt; +     }<br>
&gt; +}<br>
&gt; +<br>
&gt; +int flog_encode_msg(flog_ctx_t *ctx, unsigned int nargs, unsigned int mask, const char *format, ...)<br>
&gt; +{    <br>
&gt; +     if (ctx-&gt;readonly) {            <br>
&gt; +             return 0;<br>
&gt; +     }<br>
&gt; +     flog_msg_t *m = (void *)ctx-&gt;pos;<br>
&gt; +     char *str_start, *p;<br>
&gt; +     va_list argptr;<br>
&gt; +     size_t i;<br>
&gt; +<br>
&gt; +     m-&gt;nargs = nargs;<br>
&gt; +     m-&gt;mask = mask;<br>
&gt; +<br>
&gt; +     str_start = (void *)m-&gt;args + sizeof(m-&gt;args[0]) * nargs;<br>
&gt; +     p = memccpy(str_start, format, 0, ctx-&gt;left - (str_start - ctx-&gt;pos));<br>
&gt; +     if (!p)<br>
&gt; +             return -ENOMEM;<br>
&gt; +<br>
&gt; +     m-&gt;fmt = str_start - ctx-&gt;pos;<br>
&gt; +     str_start = p;<br>
&gt; +     va_start(argptr, format);<br>
&gt; +     for (i = 0; i &lt; nargs; i++) {<br>
&gt; +             m-&gt;args[i] = (long)va_arg(argptr, long);<br>
&gt; +             /*<br>
&gt; +              * If we got a string, we should either<br>
&gt; +              * reference it when in rodata, or make<br>
&gt; +              * a copy (FIXME implement rodata refs).<br>
&gt; +              */<br>
&gt; +             if (mask &amp; (1u &lt;&lt; i)) {<br>
&gt; +                     p = memccpy(str_start, (void *)m-&gt;args[i], 0, ctx-&gt;left - (str_start - ctx-&gt;pos));<br>
&gt; +                     if (!p)<br>
&gt; +                             return -ENOMEM;<br>
&gt; +                     m-&gt;args[i] = str_start - ctx-&gt;pos;<br>
&gt; +                     str_start = p;<br>
&gt; +             }<br>
&gt; +     }<br>
&gt; +     va_end(argptr);<br>
&gt; +     m-&gt;size = str_start - ctx-&gt;pos;<br>
&gt; +<br>
&gt; +     /*<br>
&gt; +      * A magic is required to know where we stop writing into a log file,<br>
&gt; +      * if it was not properly closed.  The file is mapped into memory, so a<br>
&gt; +      * space in the file is allocated in advance and at the end it can have<br>
&gt; +      * some unused tail.<br>
&gt; +      */<br>
&gt; +     m-&gt;magic = FLOG_MAGIC;<br>
&gt; +     m-&gt;version = FLOG_VERSION;<br>
&gt; +<br>
&gt; +     m-&gt;size = round_up(m-&gt;size, 8);<br>
&gt; +<br>
&gt; +     /* Advance position and left bytes in context memory */<br>
&gt; +     ctx-&gt;left -= m-&gt;size;<br>
&gt; +     ctx-&gt;pos += m-&gt;size;<br>
&gt; +     return 0;<br>
&gt; +}<br>
&gt; -- <br>
&gt; 2.22.0<br>
&gt; <br>
<br>
&gt; _______________________________________________<br>
&gt; CRIU mailing list<br>
&gt; <a href="mailto:CRIU@openvz.org" target="_blank" rel="noreferrer">CRIU@openvz.org</a><br>
&gt; <a href="https://lists.openvz.org/mailman/listinfo/criu" rel="noreferrer noreferrer" target="_blank">https://lists.openvz.org/mailman/listinfo/criu</a><br>
<br>
</blockquote></div>