Skip to content

Fix ipc_read tool offsets on 64-bit targets#17

Merged
johannesschrimpf merged 2 commits into
mainfrom
jp-pino/fix-ipc-read-offsets
Jun 11, 2026
Merged

Fix ipc_read tool offsets on 64-bit targets#17
johannesschrimpf merged 2 commits into
mainfrom
jp-pino/fix-ipc-read-offsets

Conversation

@jp-pino

@jp-pino jp-pino commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Fixes the tyndall_tool_ipc_read data offsets so the tool works on Jetson (aarch64) and x86_64, not just i.MX (armv7).

Problem

tools/ipc_read.cpp hardcoded the seq_lock layout, assuming the data buffer starts at CACHELINE_BYTES from the mapping base and that the size_t size field sits at offset sizeof(unsigned) == 4.

That only holds on 32-bit ARM (i.MX), where sizeof(size_t) == 4 and the header packs into exactly one 32-byte cacheline. On 64-bit targets size_t is 8 bytes, so the compiler inserts 4 bytes of padding between seq and size, shifting the real entry offset:

Platform sizeof(size_t) CACHELINE_BYTES real offsetof(entry) tool assumed
armv7 (i.MX) 4 32 32 32 ✅
aarch64 (Jetson) 8 32 36+ 32 ❌
x86_64 8 64 68+ 64 ❌

Symptom on Jetson:

$ ros2 topic echo /supervisor/light_state --once
data: 0.12090606987476349
$ tyndall_tool_ipc_read /dev/shm/...supervisor_light_state... ff
0(f): 0.000000      # phantom — 4 bytes of header padding read as float
1(f): 0.120906      # actual value, shifted by one float

Fix

Compute seq, size, and entry offsets with offsetof(seq_lock<char>, ...) so the tool always agrees with the real struct layout the writer/reader templates use.

The templated ipc_read() API was never affected because the writer and reader both use &this->entry via the same struct definition.

Follow-up

The underlying seq_lock padding[] calculation is also off on 64-bit (entry no longer starts on a cacheline boundary, defeating the false-sharing protection). Tracked separately in #16 so this PR stays minimal and unblocks the tool immediately.

Testing

Not yet built — please cross-compile for jetson/imx to confirm. Native build wasn't available on my macOS host.

jp-pino and others added 2 commits June 11, 2026 20:31
The tool hardcoded the seq_lock layout assuming the data buffer always
starts at CACHELINE_BYTES from the mapping base. That holds on 32-bit
ARM (i.MX) where size_t is 4 bytes and the header packs into exactly
one cacheline. On 64-bit targets (Jetson aarch64, x86_64) size_t is 8
bytes and the compiler inserts 4 bytes of padding between seq and
size, shifting the real entry offset to 36 (aarch64) or 68 (x86_64).
This caused all printed fields to be off by 4 bytes.

Compute the seq, size and entry offsets with offsetof(seq_lock<char>,
...) so they match the actual struct layout on every target.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous fix used offsetof(seq_lock<char>, entry), which gives the
offset right after padding[] assuming 1-byte alignment for STORAGE.
That works for std_msgs::Float32 (4-byte aligned, lands at offset 36
on aarch64) but not for geometry_msgs::Vector3 (8-byte aligned, entry
sits at offset 40 because the compiler rounds up).

Symptom: reading Vector3 with 'ddd' produced 0,0,0 because the tool
read at offset 36 — 4 zero bytes of inter-field padding plus 20 bytes
of real data, losing the last 4 bytes of z.

Fix: compute storage_align from the format string (max alignment of
any format char) and round offsetof(probe_lock, entry) up to it.

Refactor: pull format-string discovery (CLI flag or debug tailer)
above the read loop so the entry offset is known before mmap is
addressed. Drop the duplicate tailer parse inside the loop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jp-pino jp-pino self-assigned this Jun 11, 2026
@jp-pino jp-pino added the bug Something isn't working label Jun 11, 2026
@jp-pino jp-pino added this to the Blunux v5.0 milestone Jun 11, 2026
@johannesschrimpf johannesschrimpf merged commit 57e5970 into main Jun 11, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates tyndall_tool_ipc_read to compute the seq_lock<STORAGE> field offsets using offsetof(...) rather than hard-coded 32-bit assumptions, so the tool reads shared-memory payloads correctly on 64-bit targets (aarch64/x86_64) as well as armv7.

Changes:

  • Use offsetof(seq_lock<char>, seq/size/entry) to locate seq and size fields reliably across ABIs.
  • Compute the payload (entry) start offset using inferred STORAGE alignment (from the format string) instead of assuming it begins at CACHELINE_BYTES.
  • Refactor format-string recovery so the tool can determine entry alignment before reading/printing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/ipc_read.cpp
Comment on lines +174 to +192
auto format_char_align = [](char c) -> size_t {
switch (c) {
case 'd': // double
case 'l': // long
case 'm': // unsigned long
case 'x': // long long
case 'y': // unsigned long long
return 8;
case 'f': // float
case 'i': // int
case 'j': // unsigned int
return 4;
case 's': // short
case 't': // unsigned short
return 2;
default: // b(bool), c(char), h(unsigned char)
return 1;
}
};
Comment thread tools/ipc_read.cpp
Comment on lines +210 to +226
} else {
constexpr int type_info_hash_size = 4;
const size_t debug_tail_size = ipc_size - *data_size;
if (debug_tail_size > (size_t)type_info_hash_size) {
const size_t debug_format_size = debug_tail_size - type_info_hash_size;
char *const debug_format_loc = static_cast<char *>(mapped) + ipc_size -
debug_format_size + CACHELINE_BYTES + 24;
std::string candidate(debug_format_loc);
auto valid = !candidate.empty() &&
std::all_of(candidate.begin(), candidate.end(), [&](char c) {
return std::find(allowed.begin(), allowed.end(), c) !=
allowed.end();
});
if (valid)
fmt_string = candidate;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants