bionic: fix broken end atrace events
When calling write on an FD for trace_marker, it is expected that the
pointer passed will be paged into memory. If this isn't the case, the
kernel will ignore the string passed and instead write "<faulted>" to
the ring buffer.
For end events, we were passing a constant string which resides in
the rodata section of the ELF file. If this section is paged out, we
end up not closing atrace stacks correctly leading to very broken traces.
For even more context, see the associated bug.
Fix this issue by reading the constant string to the stack first
which should mean the string is always paged in.
Bug: 197620214
Change-Id: I6a444ac6fe83a6a9fb696c5621e392eca7e9437a
diff --git a/libc/bionic/bionic_systrace.cpp b/libc/bionic/bionic_systrace.cpp
index fd97712..cf5cd82 100644
--- a/libc/bionic/bionic_systrace.cpp
+++ b/libc/bionic/bionic_systrace.cpp
@@ -86,7 +86,21 @@
return;
}
- TEMP_FAILURE_RETRY(write(trace_marker_fd, "E|", 2));
+ // This code is intentionally "sub-optimal"; do not optimize this by inlining
+ // the E| string into the write.
+ //
+ // This is because if the const char* string passed to write(trace_marker) is not
+ // in resident memory (e.g. the page of the .rodata section that contains it has
+ // been paged out, or the anonymous page that contained a heap-based string is
+ // swapped in zram), the ftrace code will NOT page it in and instead report
+ // <faulted>.
+ //
+ // We "fix" this by putting the string on the stack, which is more unlikely
+ // to be paged out and pass the pointer to that instead.
+ //
+ // See b/197620214 for more context on this.
+ volatile char buf[2]{'E', '|'};
+ TEMP_FAILURE_RETRY(write(trace_marker_fd, const_cast<const char*>(buf), 2));
}
ScopedTrace::ScopedTrace(const char* message) : called_end_(false) {