<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.zmeiresearch.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.zmeiresearch.com/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2026-10-17T13:46:51+00:00</updated><id>https://blog.zmeiresearch.com/feed.xml</id><title type="html">Zmei’s blog</title><subtitle>A place for random thoughts and observations. Mostly embedded, cars and DevOps</subtitle><author><name>Ivan</name></author><entry><title type="html">Docker distribution/registry - Or: why version pinning is important</title><link href="https://blog.zmeiresearch.com/2025/04/04/Distribution-Docker-Registry-Keycloak-Otel/" rel="alternate" type="text/html" title="Docker distribution/registry - Or: why version pinning is important" /><published>2025-04-04T13:22:23+00:00</published><updated>2025-04-04T13:22:23+00:00</updated><id>https://blog.zmeiresearch.com/2025/04/04/Distribution-Docker-Registry-Keycloak-Otel</id><content type="html" xml:base="https://blog.zmeiresearch.com/2025/04/04/Distribution-Docker-Registry-Keycloak-Otel/"><![CDATA[<h2 id="re-urgent---i-get-this-error"><em>Re: Urgent - I get this error</em></h2>
<p>Thursday afternoon… last thing I’d like to see is a mail with similar subject from the CTO of the company.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker login <span class="nt">-u</span> <span class="nv">$CONTAINER_REGISTRY_USERNAME</span> <span class="nt">-p</span> <span class="nv">$CONTAINER_REGISTRY_PASSWORD</span> <span class="nv">$CONTAINER_REGISTRY</span>
Error response from daemon: login attempt to https://registry.XXXXXX/v2/ failed with status: 401 Unauthorized
</code></pre></div></div>
<p>For various reasons we run our own private container registry. Needless to say, without it all development is blocked. Worse, still if a running pod needs to be started on another node, the new node MAY not contain the image in it’s local cache so a pod migration(a normal event in kubernetes) can actually result in a service outage. I know it’s a single point of failure, have a ticket in the backlog about it.</p>

<p>The setup is quite straightforward, Registry is deployed in k8s behind an LB, uses s3 for storage and Keycloak for AA. It’s been worry-free for the past year or so, apart from some semi-periodic clean-up of old cruft.</p>

<p>Looking at the logs I saw a lot of errors:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>time="2025-04-04T09:32:55.822077511Z" level=error msg="traces export: Post \"https://localhost:4318/v1/traces\": dial tcp [::1]:4318: connect: connection refused" environment=development go.version=go1.23.7 instance.id=1df5abf0-2cc7-4268-9d28-b3f4a046ce87 service=registry version=3.0.0
</code></pre></div></div>

<p>So why the hell is registry all of a sudden trying to reach <em>localhost:4318</em>? I know, by accident, that such a request is actually the service trying to reach out to an <a href="https://opentelemetry.io">Otel(OpenTelemetry)</a> collector but why would that cause an outage?</p>

<p>Well, it didn’t. Hidden among the thousand similar <strong>errors</strong> there was also another line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>time="2025-04-04T09:30:53.436663295Z" level=info msg="failed to verify token: token signed by untrusted key with ID: \"FML4:7GSU:IBQ4:XXX\""
</code></pre></div></div>

<p>Let me sink that for a moment. I got 100+ <strong>errors</strong> that Otel is unable  to post it’s traces, but I get a single <strong>info</strong> message why my AA setup is not working!</p>

<p>Google led to this issue: <a href="https://github.com/distribution/distribution/issues/4299">INFO[0101] failed to verify token: token signed by untrusted key with ID</a>. It seems the logic inside Registry has changed and now the Keycloak setup that’s been working for some time and that is done exactly as per Keycloack documentation needs additional configuration.</p>

<p>But why did it break now? Well, in the deployment I had:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">registry</span>
  <span class="s">...</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">registry</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">registry:latest</span>
</code></pre></div></div>

<p>And guess what? <em>:latest</em> just got bumped to <em>3.0.0</em> yesterday. So it was just a matter of time until the pod was restarted for disaster to strike.
For now, I’ve just changed to <strong>image: registry:2</strong> and that seems to have fixed the issue on prod.</p>

<p>However, I feel kind of alienated by the registry guys because:</p>
<ul>
  <li>They knowingly broke token auth for <em>legacy</em> implementations. No migration path, not even a mention besides <em>“docker/libtrust has been replaced with go-jose/go-jose in #4096”</em>. I for sure don’t know what <em>go-jose</em> is and don’t really care as long as it does not make me do more work. When a <a href="https://github.com/distribution/distribution/pull/4417">fix/pull request was proposed</a> it was dismissed as pretty much <em>We used to do bad things, but now we do only good ones</em>.</li>
  <li>
    <p>OTel traces being enabled by default is a PITA. If it’s an opt-in - fine, I’ll just not use it. If there’s a way to disable it via config file option - also somewhat OK. Luckily it’s in k8s so I can just do:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  ```
  configMapGenerator:
  - name: registry-new-config
    literals:
    - REGISTRY_LOG_LEVEL="info"
    - OTEL_TRACES_EXPORTER="none"
  ```
</code></pre></div>    </div>

    <p>But what if it runs on real hardware/shared VM?</p>
  </li>
</ul>

<p>Both these issues shows to me that the maintainers of registry just don’t care about their users…</p>]]></content><author><name>Ivan</name></author><category term="Docker" /><category term="Registry" /><category term="Distribution" /><category term="OpenTelemetry" /><category term="Keycloak" /><summary type="html"><![CDATA[Re: Urgent - I get this error Thursday afternoon… last thing I’d like to see is a mail with similar subject from the CTO of the company. $ docker login -u $CONTAINER_REGISTRY_USERNAME -p $CONTAINER_REGISTRY_PASSWORD $CONTAINER_REGISTRY Error response from daemon: login attempt to https://registry.XXXXXX/v2/ failed with status: 401 Unauthorized For various reasons we run our own private container registry. Needless to say, without it all development is blocked. Worse, still if a running pod needs to be started on another node, the new node MAY not contain the image in it’s local cache so a pod migration(a normal event in kubernetes) can actually result in a service outage. I know it’s a single point of failure, have a ticket in the backlog about it.]]></summary></entry><entry><title type="html">Grafana Alloy/Mimir - Received a series with duplicate label name</title><link href="https://blog.zmeiresearch.com/2025/01/30/Grafana-Alloy-Mimir-series-with-duplicate-label-name/" rel="alternate" type="text/html" title="Grafana Alloy/Mimir - Received a series with duplicate label name" /><published>2025-01-30T08:37:00+00:00</published><updated>2025-01-30T08:37:00+00:00</updated><id>https://blog.zmeiresearch.com/2025/01/30/Grafana-Alloy-Mimir-series-with-duplicate-label-name</id><content type="html" xml:base="https://blog.zmeiresearch.com/2025/01/30/Grafana-Alloy-Mimir-series-with-duplicate-label-name/"><![CDATA[<h2 id="tldr">TL/DR</h2>
<p>Just sure to restart your Alloy instances from time to time</p>

<h2 id="a-monitoring-tale">A Monitoring tale</h2>
<p>I’ve been setting up a <a href="https://grafana.com/go/webinar/getting-started-with-grafana-lgtm-stack/">LG(T)M</a> stack to monitor a kubernetes cluster of mine. It’s a self-built setup running on <a href="https://www.hetzner.com/cloud">Hetzner Cloud</a> using the excellent <a href="https://github.com/kube-hetzner/terraform-hcloud-kube-hetzner">Kube-Hetzner</a> project to configure the cluster itself. I’ll probably do a write-up on that setup at a later point, suffice to say that it’s really hassle-free and works great.</p>

<p>I have a monitoring/alerting set-up consisting of Grafana itself for visualisation, <a href="https://grafana.com/oss/loki/">Grafana Loki</a> for log storage, <a href="https://grafana.com/oss/mimir/">Grafana Mimir</a> for metrics storage and <a href="https://grafana.com/oss/alloy-opentelemetry-collector/">Grafana Alloy</a> to scrape logs and metrics. It’s been running for a few months, delivering logs and metrics as requested - not much as it’s a dev/demo system that does not see much usage.</p>

<p>Lately I’ve looked into pushing more workloads to the cluster and decided I need more metrics at pod level. Lookin at what was scraped, I identified two sets of metrics that I wanted in - specifically <a href="https://github.com/kubernetes/kube-state-metrics">Kube-State-Metrics</a> and <a href="https://github.com/google/cadvisor/">cAdvisor</a>. It’s a bit of a mess to set that up in <strong>Alloy</strong> (another piece to describe in a future post) and it involved lots of edit to the Alloy configuration file. At one point in time I started receiving some errors in both <strong>Mimir</strong> and <strong>Alloy</strong> logs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ts=2025-01-29T14:12:10.555161198Z level=error msg="non-recoverable error" component_path=/ 
component_id=prometheus.remote_write.default_mimir subcomponent=rw remote_name=711a87 
url=http://mimir-nginx.monitoring.svc.cluster.local:80/api/v1/push count=707 exemplarCount=0 
err="server returned HTTP status 400 Bad Request: received a series with duplicate label name, 
label: 'node_kubernetes_io_instance_type' series: 'machine_scrape_error{beta_kubernetes_io_arch=\"amd64\",
beta_kubernetes_io_instance_type=\"cx42\", beta_kubernetes_io_os=\"linux\", csi_hetzner_cloud_location=\"hel1\",
 failure_domain_beta_kubernetes_io_regio' (err-mimir-duplicate-label-names)"
</code></pre></div></div>

<p>This is just a sample, I was getting lots of messages for different duplicate labels, not only <strong>node_kubernetes_io_instance_type</strong>.</p>

<p>Google showed up some results like <a href="https://github.com/grafana/mimir/discussions/8344">This</a> and <a href="https://github.com/grafana/alloy/issues/1006">This</a>. However, the <em>Quick Workaround</em> mentioned didn’t work for me… I dug a bit deeper and these two issues didn’t really seem to be the same as mine - in both cases an <strong>err-mimir-duplicate-label-names</strong> was generated, but I was not doing any relabelling on my metrics at all.</p>

<p>After spending a few hours fighting with this, including disabling almost all metrics collection and still getting errors I was out of ideas.</p>

<p>Now a step back. I’m using Alloy in a distributed way in a <em>daemonset</em>, meaning each node gets it’s own <strong>Alloy</strong> pod - in my case I had 6 of them. Also, I’ve configured <strong>Alloy</strong> to do live config reloads to minimize downtime. This all meant that my <strong>Alloy</strong> pods were up for 10+ days… In an act of desperation I followed the old Level-1 Tech support advice: <strong>“Have you tried turning it off and on again?”</strong>. In my case:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl rollout restart daemonset.apps/alloy <span class="nt">-n</span> monitoring
</code></pre></div></div>

<p>When the restart was complete and the Alloy WAL was flushed from previous data, there were no more errors!</p>

<p>What has probably happened is that during all the configuration file restarts, Alloy instances got out of sync or just had their internal state messed up. This will probably not happen during normal operation as either:</p>
<ul>
  <li>Config file(s) are rarely changed</li>
  <li>Pods get restarted from time to time</li>
  <li>Live reload is not used</li>
</ul>

<p>But it’s good practice to keep in mind that sometimes a good old restart is the proper solution.</p>

<h2 id="addendum-two-days-later">Addendum: two days later</h2>
<p>After some more experiments I have a pretty reliable way of triggering this. Configure <strong>Alloy</strong> to scrape more metrics than <strong>Mimir</strong> will accept, you’ll soon get a <em>HTTP 429</em> like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ts=2025-01-31T21:11:59.769007025Z level=warn msg="Failed to send batch, retrying" component_path=/ 
component_id=prometheus.remote_write.default_mimir subcomponent=rw remote_name=183919 
url=http://mimir-nginx.monitoring.svc.cluster.local:80/api/v1/push 
err="server returned HTTP status 429 Too Many Requests: the request has been rejected 
because the tenant exceeded the ingestion rate limit, set to 10000 items/s with a maximum 
allowed burst of 200000. This limit is applied on the total number of samples, exemplars 
and metadata received across all distributors (err-mimir-tenant-max-ingestion-rate). 
To adjust the related per-tenant limits, configure -distributor.ingestion-rate-limit 
and -distributor.ingestion-burst-size, or contact your service administrator."
</code></pre></div></div>

<p>Fair enough, Adjust the limits or reduce metrics volume and the error should go away, right? Yep, but more often than not <strong>err-mimir-duplicate-label-names</strong> will start popping up, prompting a restart of <strong>Alloy</strong>.</p>

<h2 id="bonus-singleline-query-kubernetes-api-from-within-a-pod-with-curl">Bonus: Singleline query Kubernetes API from within a pod with curl</h2>
<p>For my own easy reference:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="se">\</span>
  <span class="nt">--cacert</span> /var/run/secrets/kubernetes.io/serviceaccount/ca.crt   <span class="se">\</span>
  <span class="nt">--header</span> <span class="s2">"Authorization: Bearer </span><span class="si">$(</span><span class="nb">cat</span> /var/run/secrets/kubernetes.io/serviceaccount/token<span class="si">)</span><span class="s2">"</span> <span class="se">\</span>
  https://kubernetes.default.svc.cluster.local/api/v1/nodes/<span class="k">${</span><span class="nv">NODE</span><span class="k">}</span>/proxy/pods
</code></pre></div></div>]]></content><author><name>Ivan</name></author><category term="Grafana" /><category term="Alloy" /><category term="Mimir" /><category term="Prometheus" /><category term="Kubernetes" /><summary type="html"><![CDATA[TL/DR Just sure to restart your Alloy instances from time to time]]></summary></entry><entry><title type="html">AzDO Service Connection - failed to rotate secrets</title><link href="https://blog.zmeiresearch.com/2024/10/10/AzDO-service-connection-secret-rotation/" rel="alternate" type="text/html" title="AzDO Service Connection - failed to rotate secrets" /><published>2024-10-10T11:51:25+00:00</published><updated>2024-10-10T11:51:25+00:00</updated><id>https://blog.zmeiresearch.com/2024/10/10/AzDO-service-connection-secret-rotation</id><content type="html" xml:base="https://blog.zmeiresearch.com/2024/10/10/AzDO-service-connection-secret-rotation/"><![CDATA[<h2 id="tldr">TL/DR</h2>
<p>If you get a <strong>One or more issues occurred while trying to rotate service endpoint secrets…</strong> - create a new service connection with the same settings and do a clean pipeline run.</p>

<h2 id="just-another-monday-morning">Just another Monday morning</h2>
<p><em>- Hey Ivan, the pipeline started failing during the weekend and we urgently need it for a release</em></p>

<p>Sure I can. It’s a project I just recently inherited and only have a basic idea on the setup, all running on Azure with the code and pipelines on <a href="https://dev.azure.com/">Azure DevOps</a>.</p>

<p>Pipeline failed during <strong>Docker push</strong> stage like so:
<img src="/assets/img/2024-10-10-docker-push-failed.png" alt="Docker push failed" /></p>

<p>So where are the credentials stored? Quick reasearch and in Azure DevOps it’s common to define <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops">Service connections</a>. Opening the properties page of the SC that the pipeline used for the push showed an error message: <strong>One or more issues occurred while trying to rotate service endpoint secrets. More details: Insufficient privileges to complete the operation in Microsoft Graph.</strong>
<img src="/assets/img/2024-10-10-service-connection-secret-rotation-failed.png" alt="Service Connection Secret Rotation Failed" /></p>

<p>Great… so how do I fix that? The single thing one can do to a service connection is “Edit” it. According to <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/release/azure-rm-endpoint?view=azure-devops#service-principals-token-expired">this</a> article, there must be a “Verify” button - but there was none. I the same article there’s a mention to do a dummy edit (e.g. update the description field) - tested it, didn’t work.</p>

<p>After scratching my head for a few moments decided to replace the connection. Renamed the old one to <strong>xxx-old</strong> then created a new one using the exact same settings. Re-ran the failed stage… and it failed again.</p>

<p>At that point I had no other ideas how to proceed. So put in some additional logging in the pipeline and ran it again - which worked! Hurray!</p>

<h2 id="analysisspeculation">Analysis/speculation</h2>
<p>Why did the pipeline fail in the first place and why did the re-run fail as well even with the new connection in place?</p>

<p>I can only speculate, but probably the initial error message was due to the creator of the Service Connection (DL - the previous DevOps guy, you can see him in the first picture) not being part of the organization anymore. Probably the automation that works behind the scenes tries to use his permissions to create a Service Principal or similar - which fails.</p>

<p>But why did the pipeline rerun failed? Again a speculation: The service connection is referred to in the pipeline by name. During initial pipeline run setup, the runner fetches the UUID of the service connection and caches it for consecutive runs, effectively still using the old connection. A new run looked it up again and that’s why it worked.</p>]]></content><author><name>Ivan</name></author><category term="Azure" /><category term="AzDO" /><category term="Azure DevOps" /><summary type="html"><![CDATA[TL/DR If you get a One or more issues occurred while trying to rotate service endpoint secrets… - create a new service connection with the same settings and do a clean pipeline run.]]></summary></entry><entry><title type="html">GCC compiler performance</title><link href="https://blog.zmeiresearch.com/2023/09/05/gcc-compiler-performance/" rel="alternate" type="text/html" title="GCC compiler performance" /><published>2023-09-05T11:56:24+00:00</published><updated>2023-09-05T11:56:24+00:00</updated><id>https://blog.zmeiresearch.com/2023/09/05/gcc-compiler-performance</id><content type="html" xml:base="https://blog.zmeiresearch.com/2023/09/05/gcc-compiler-performance/"><![CDATA[<h2 id="what">What?</h2>
<p>A totally unscientific benchmark of GCC on a real project that I’m currently working.</p>

<h2 id="why">Why?</h2>
<p>Because switching from GCC 6 to GCC12 felt slow and I wanted to know exactly how much slower it really is.</p>

<h2 id="background">Background</h2>
<p>It’s a real project, a custom firmware for a Cortex-M4F MCU (Atmel AT-SAME51 to be specific). It’s developed mainly in plain old C, with a bit of C++ thrown in. Even when C++ is used, it’s mostly as C-with-classes. The specific compiler options in place are:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Os 
-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 
-ffunction-sections -mlong-calls 
-Waddress -Wformat-nonliteral -Wformat-security -Wformat -Winit-self -Wmissing-declarations 
-Wno-multichar -Wunreachable-code -Wwrite-strings -Wpointer-arith -Wfloat-equal -Wparentheses 
-Wunused-parameter -Wunused-variable -Wreturn-type -Wuninitialized -Wextra -Wall  
-std=gnu11 -MD -MT
</code></pre></div></div>

<h2 id="methodology">Methodology</h2>
<p>Execute the below script two times, changing <code class="language-plaintext highlighter-rouge">PATH</code> to point to different compiler versions.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test_compile <span class="o">()</span> <span class="o">{</span>
    <span class="nb">sleep </span>30
    make clean
    <span class="nb">time </span>make <span class="nt">-j4</span> | <span class="nb">grep </span>real
<span class="o">}</span>

<span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>1..10<span class="o">}</span><span class="p">;</span> <span class="k">do </span>test_compile<span class="p">;</span> <span class="k">done</span>
</code></pre></div></div>

<p>Basically compile the firmware 10 times in a loop, using up to 4 parallel compiler invokations - as this is the number of cores that I have on my setup.</p>

<h2 id="raw-results">Raw Results</h2>
<h3 id="gcc-631">GCC 6.3.1</h3>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>arm-none-eabi-gcc <span class="nt">--version</span> 
arm-none-eabi-gcc <span class="o">(</span>Atmel build: 508<span class="o">)</span> 6.3.1 20170620 <span class="o">(</span>release<span class="o">)</span> <span class="o">[</span>ARM/embedded-6-branch revision 249437]

<span class="nv">$ </span>./test_compile.sh | <span class="nb">grep </span>real
real	0m13,659s
real	0m14,090s
real	0m15,397s
real	0m14,367s
real	0m14,815s
real	0m15,611s
real	0m18,213s
real	0m15,756s
real	0m18,644s
real	0m17,026s
</code></pre></div></div>
<h3 id="gcc-122">GCC 12.2</h3>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>arm-none-eabi-gcc <span class="nt">--version</span> 
arm-none-eabi-gcc <span class="o">(</span>Arm GNU Toolchain 12.2.MPACBTI-Rel1 <span class="o">(</span>Build arm-12-mpacbti.34<span class="o">))</span> 12.2.1 20230214

<span class="nv">$ </span>./test_compile.sh | <span class="nb">grep </span>real
real	0m20,078s
real	0m22,832s
real	0m19,660s
real	0m19,067s
real	0m18,097s
real	0m18,159s
real	0m19,469s
real	0m19,113s
real	0m18,993s
real	0m20,311s
</code></pre></div></div>

<h2 id="results">Results:</h2>

<h3 id="build-time">Build time</h3>
<p>Build time in seconds:</p>

<table>
  <thead>
    <tr>
      <th>GCC version</th>
      <th>Avg. time</th>
      <th>StdDev</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>6.3.1</td>
      <td>15.757</td>
      <td>1.616</td>
    </tr>
    <tr>
      <td>12.2</td>
      <td>19.578</td>
      <td>1.281</td>
    </tr>
  </tbody>
</table>

<h3 id="binary-size">Binary size</h3>
<p>Sizes in bytes as obtained from <code class="language-plaintext highlighter-rouge">arm-none-eabi-size</code>:</p>

<table>
  <thead>
    <tr>
      <th>GCC version</th>
      <th>Text</th>
      <th>Data</th>
      <th>Bss</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>6.3.1</td>
      <td>54704</td>
      <td>228</td>
      <td>129344</td>
      <td>184276</td>
    </tr>
    <tr>
      <td>12.2</td>
      <td>53752</td>
      <td>220</td>
      <td>129656</td>
      <td>183628</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>
<p><strong>GCC 12</strong> is around 25% slower, which is noticeable even with this small project if doing a full build. I don’t have a statistic how much of that time is spent between the compiler and linker but subjectively even building and linking only a few files do seem slower.
On the plus side, <strong>GCC 12</strong> produces a binary that’s ~600 bytes smaller - which is rarely a deal-breaker but it may be under specific circumstances.</p>]]></content><author><name>Ivan</name></author><category term="gcc" /><category term="embedded" /><category term="benchmark" /><summary type="html"><![CDATA[What? A totally unscientific benchmark of GCC on a real project that I’m currently working.]]></summary></entry><entry><title type="html">Building Clang the Google way</title><link href="https://blog.zmeiresearch.com/2021/12/05/building-clang-the-google-way/" rel="alternate" type="text/html" title="Building Clang the Google way" /><published>2021-12-05T13:49:24+00:00</published><updated>2021-12-05T13:49:24+00:00</updated><id>https://blog.zmeiresearch.com/2021/12/05/building-clang-the-google-way</id><content type="html" xml:base="https://blog.zmeiresearch.com/2021/12/05/building-clang-the-google-way/"><![CDATA[<h2 id="what">What?</h2>
<p>Build an archive similar to the ones available from <a href="https://chrome-infra-packages.appspot.com/p/fuchsia/clang">CIPD</a></p>

<h2 id="why">Why?</h2>
<p>Try and get Fuchsia built on a new platform.</p>

<h2 id="how">How?</h2>

<h3 id="tldr">TL/DR</h3>
<p>This is an old post. Probably a lot hast changed. But it felt like a game of cat and mouse and I did not manage to see through it all.</p>

<h3 id="long-version">Long version</h3>

<h4 id="setting-the-stage">Setting the stage</h4>
<p>I’ve been playing around with Fuchsia since it went public a few years ago, periodically building the latest version and taking a look at how it evolved. Then my family grew bigger and I suddenly found myself with an unlikely source of spare time - 15 minutes now, 10 - in two hours, mixed in between changing nappies and taking care of the household. The highly irregular and ultimately quite short periods meant that no ‘serious’ work can be done. At the same time, I was preparing for a conference talk on Risc-V - slides available <a href="https://github.com/zmeiresearch/HackConf2021_Why-Go-Risc-V-y">here</a>. And one night probably around 3AM a tought occured - <em>Can I run Fuchsia, or at least Zircon, on Risc-V?</em></p>

<p>Google quickly turned up an answer - back when it was still called Magenta, Zircon has been ported to Risc-V - a good start, but outdated by several years. At present, Zircon is part of the Fuchsia checkout, let’s try building it using the native Fuchsia <em>way</em>. That’s the kind of task that perfectly matches my sketchy free time - make a change, kick off a build, come back later, fix new failures and retry. And I was down the rabbit hole, only to find myself trying to build Clang the way Google does.</p>

<h4 id="context">Context</h4>
<p>When building Fuchsia some packages are downloaded as prebuilt archives. This is done for convenience, to cut down on build resource requirements (both time and space) as well as to reuse what’s already done in another place. To do so, it relies on a tool called CIPD - short from Chrome Infrastructure Package Deployment. Although the tool is <a href="https://chromium.googlesource.com/infra/luci/luci-go/+/refs/heads/main/cipd">open-source</a>. CIPD officially supports only x64 and arm64 and obviously it cannot be of any help in my quest to Risc-V compilation. Which is fine, I’m used to cross-compilation ever since I was in the dev boards business - which is quite a long time ago.</p>

<p>What’s annoying, however, is that Fuchsia build systems makes it seemingly easy to specify a new target architecture, but it quickly escalates once you understand that quite a lot of packages are actually prebuilt and often packaged in a particular way. Maybe I’ll be doing a separate write-up with more details, but long story short - after a few days of building toolchains and messing around, I decided to tackle the problem at it’s root and try doing what Fuchsia expects - build the packages using CIPD.</p>

<h4 id="details">Details</h4>
<p>While basic usage (e.g. downloading) of CIPD packages is unrestricted, there is a profound lack of information on how actually CIPD works and, more importantly, how packages are built. There <em>is</em> documentation, but it is usually centered at specific tools, is outdated or assumes one is a <em>googler</em> - which I am not.</p>

<p>What intially led to some confusion was what exactly CIPD does and what it does not. And building a package CIPD does <strong>not</strong> do. Rather, there is a separate set of tools, living under the <a href="https://chromium.googlesource.com/infra/luci/">Chromium repository</a> that <em>seem</em> to be doing the building. Studiying the documentation, there should be a set of ‘recipes’ specifying how to build a package - which sounds reasonable. Now, where would one find the recipe for clang - and the other Fuchsia-required packages? Maybe they live in <a href="https://chromium.googlesource.com/infra/luci/recipes-py/">lucy-recipes</a>? Nah, that would’ve been too obvious. Back to reading documentation, duckduckgoing/googling and when that did not work, grep-ing the fuchsia and chromium source code. And there was a lead:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ivan@balha:~/fuchsia<span class="nv">$ </span>rg recipes
...
tools/integration/fint/README.md
244:into the <span class="o">[</span>recipes][fuchsia-recipes] repository so recipes can construct
275:[fuchsia-recipes]: https://fuchsia.googlesource.com/infra/recipes 
</code></pre></div></div>

<p>Aha! A quick <code class="language-plaintext highlighter-rouge">git clone https://fuchsia.googlesource.com/infra/recipes/ &amp;&amp; find ./ -name "*clang*</code> turned out a <code class="language-plaintext highlighter-rouge">./recipes/contrib/clang_toolchain.py</code>. Studying the contents I was sure I’m on the right track - there are options to turn on/off LTO, use LLD on OSX and a few other Clang/LLVM-specific ones. Time to kick off this build!</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ivan@balha:~/Work/fuchsia-infra/recipes<span class="nv">$ </span>./recipes.py run contrib/clang_toolchain <span class="nv">enable_lto</span><span class="o">=</span>thin
</code></pre></div></div>

<p>At first, I experienced a failure related to a missing git directory, fixed by running</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ivan@balha:~/Work/fuchsia-infra/recipes<span class="nv">$ </span><span class="nb">mkdir</span> ~/Work/fuchsia-infra/recipes/.recipe_deps/recipe_engine/workdir/cache/git
</code></pre></div></div>

<p>Next, the recipe assumes goma is available and complains with authentication/configuration failures. No command-line switch seems to exist, thus a quick manual patch got the job done:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ivan@balha:~/Work/fuchsia-infra/recipes<span class="nv">$ </span>git diff recipes/contrib/clang_toolchain.py
diff <span class="nt">--git</span> a/recipes/contrib/clang_toolchain.py b/recipes/contrib/clang_toolchain.py
index 7d68cf787..7df0d2fc4 100644
<span class="nt">---</span> a/recipes/contrib/clang_toolchain.py
+++ b/recipes/contrib/clang_toolchain.py
@@ <span class="nt">-268</span>,16 +268,17 @@ def RunSteps<span class="o">(</span>
     use_inliner_model,
     builders,
 <span class="o">)</span>:
-    use_goma <span class="o">=</span> <span class="o">(</span>
-        not api.platform.arch <span class="o">==</span> <span class="s2">"arm"</span> and api.platform.bits <span class="o">==</span> 64
-    <span class="o">)</span> and not api.platform.is_win
+    <span class="c">#use_goma = (</span>
+    <span class="c">#    not api.platform.arch == "arm" and api.platform.bits == 64</span>
+    <span class="c">#) and not api.platform.is_win</span>
+    use_goma <span class="o">=</span> False
     <span class="k">if </span>use_goma:
         api.goma.ensure<span class="o">()</span>
         ninja_jobs <span class="o">=</span> api.goma.jobs
         goma_context <span class="o">=</span> api.goma.build_with_goma<span class="o">()</span>
     <span class="k">else</span>:
         ninja_jobs <span class="o">=</span> api.platform.cpu_count
-        goma_context <span class="o">=</span> contextlib.nullcontext<span class="o">()</span>
+        goma_context <span class="o">=</span> None

     <span class="c"># TODO: builders would ideally set this explicitly</span>
</code></pre></div></div>

<p>Running the recipe command again resulted in 5 hours of compilation and testing (on a slow 4C/4T Ryzen 3 machine) which worked OK, only for the process to fail with a:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>E2021-11-09T01:33:50.197624+02:00 3124 0 annotate.go:273] original error: interactive login is required
<span class="c">#4 runtime/asm_amd64.s:1371 - runtime.goexit()</span>
cas: failed to create cas client: failed to get PerRPCCredentials: interactive login is required
</code></pre></div></div>

<p>Never heard of CAS before. Googling does not produce meaningful results at first. Running the executable gives:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ivan@balha:~/Work/zmeiresearch.github.io/blog<span class="nv">$ </span>/mnt/phys2/home/ivan/Work/fuchsia-infra/recipes/.recipe_deps/recipe_engine/workdir/cache/cipd/infra/tools/luci/cas/git_revision%3Acefd07c708bfd0bb37362a0c90e53fa31b0f8793/.cipd/pkgs/0/_current/cas
Client tool to access CAS.

Usage:  cas <span class="o">[</span><span class="nb">command</span><span class="o">]</span> <span class="o">[</span>arguments]

Commands:
  <span class="nb">help      </span>prints <span class="nb">help </span>about a <span class="nb">command
  </span>archive   archive <span class="nb">dirs</span>/files to CAS
  download  download directory tree from a CAS server.
  <span class="nb">whoami    </span>prints an email address associated with currently cached token
  login     performs interactive login flow
  <span class="nb">logout    </span>removes cached credentials
  version   prints the executable version


Use <span class="s2">"cas help [command]"</span> <span class="k">for </span>more information about a command.

ivan@balha:~/Work/zmeiresearch.github.io/blog<span class="nv">$ </span>/mnt/phys2/home/ivan/Work/fuchsia-infra/recipes/.recipe_deps/recipe_engine/workdir/cache/cipd/infra/tools/luci/cas/git_revision%3Acefd07c708bfd0bb37362a0c90e53fa31b0f8793/.cipd/pkgs/0/_current/cas version
0.1

CIPD package name: infra/tools/luci/cas/linux-amd64
CIPD instance ID:  xiOY69QEW4Td0_DKahtTNA30_SiIwg20ei1En_APEUQC
</code></pre></div></div>

<p>Lovely, just another undocumented tool :) :</p>]]></content><author><name>Ivan</name></author><category term="clang" /><category term="fuchsia" /><summary type="html"><![CDATA[What? Build an archive similar to the ones available from CIPD]]></summary></entry></feed>