libSQL Self-hosting
You might have heard of Turso, a hosted SQLite solution. Their fork of SQLite, libSQL, is open source, so anyone can play with it for free.
Trouble is, their guide on self-hosting is somewhat lacking, so I figured I’d write down how I got everything up and running for some proper testing.
NOTE: This post is written for a Debian-based Linux system, but it’s probably not that far off for a Mac user.
Read moreLogs as metrics from embedded devices
Getting metrics out of embedded devices is often useful but also challenging due to resource constraints. What I’ve found to work quite well is attaching a debugger and just dumping metrics via printf
and then parsing the output.
I always dump the logs into Loki with something like this
command-to-get-logs | promtail \
--stdin \
--client.url http://localhost:3100/loki/api/v1/push \
--client.external-labels="app=my-embedded-device" \
--server.disable
Than I can just print lines in logfmt:
logfmt: rx_bytes=100 tx_bytes=100
logfmt: rx_bytes=150 tx_bytes=100
logfmt: rx_bytes=400 tx_bytes=120
and parse into a graph in Grafana via LogQL:
Read moreWhen using a vendor SDK or HAL, always check how callbacks are being run
At my job, I often work with embedded devices. Once, we had a particularly nasty problem with measurements from sensors sometimes returning unexpected values, debug logs getting garbled up, and then there was the occasional crash, too.
Most of our code was unit tested on a desktop. The tests were run with sanitizers, and they reported no issues.
Still, the problems kind of pointed to some kind of memory corruption. A threading issue, perhaps?
Read moreThere is more than one localhost
Well, technically I lied. Of course, localhost
maps to just 127.0.0.1
but that is not the only address that points to your local machine. There are actually 16.7 million (2^24) addresses, as the loopback network range is 127.0.0.0/8
.
I find it particularly useful to have different loopback addresses per project, while keeping their ports consistent. For example:
Project A development environment
127.0.0.2:3000
http server127.0.0.2:5432
postgres127.0.0.2:6379
redis
Project B development environment
Read moreComparing structs in C
What do you think this C code prints?
struct foo first = { 0 };
struct foo second = { 0 };
if (memcmp(&first, &second, sizeof(first)) == 0) {
printf("equal\n");
} else {
printf("not equal\n");
}
The corrent answer is: “I don’t know.”
The reason is that we don’t know if the struct contains padding bytes inserted by the compiler. Because if it does, those bytes might not be initialized. The only way to set them is with memset
.
Exhaustive matching for C enums
I like Rust. I like how it forces you to do exhaustive matching in match
statements (well, as long as you don’t use a catch-all like _ => {}
), so when I add a new enum type, the compiler helplfully reminds me to handle it.
I don’t get to work in Rust nearly as much as I’d like; I usually find myself swimming in the C, instead.
Thankfully, modern compilers like GCC and Clang have a lot of warnings you can enable to help you avoid mistakes. For example, you can get the same kind of exhaustive matching for switch
statements that you can get in Rust’s match
statements, provided that you don’t add a default
case.