summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/xray/xray_interface.h19
-rw-r--r--lib/xray/xray_AArch64.cc9
-rw-r--r--lib/xray/xray_arm.cc9
-rw-r--r--lib/xray/xray_fdr_logging.cc1
-rw-r--r--lib/xray/xray_interface.cc21
-rw-r--r--lib/xray/xray_interface_internal.h3
-rw-r--r--lib/xray/xray_trampoline_x86_64.S43
-rw-r--r--lib/xray/xray_x86_64.cc10
-rw-r--r--test/xray/TestCases/Linux/arg1-logger.cc41
9 files changed, 144 insertions, 12 deletions
diff --git a/include/xray/xray_interface.h b/include/xray/xray_interface.h
index 9e712b1fa..52a7e1d9e 100644
--- a/include/xray/xray_interface.h
+++ b/include/xray/xray_interface.h
@@ -18,7 +18,13 @@
extern "C" {
-enum XRayEntryType { ENTRY = 0, EXIT = 1, TAIL = 2 };
+// Synchronize this with AsmPrinter::SledKind in LLVM.
+enum XRayEntryType {
+ ENTRY = 0,
+ EXIT = 1,
+ TAIL = 2,
+ LOG_ARGS_ENTRY = 3,
+};
// Provide a function to invoke for when instrumentation points are hit. This is
// a user-visible control surface that overrides the default implementation. The
@@ -60,6 +66,17 @@ extern XRayPatchingStatus __xray_patch();
// Reverses the effect of __xray_patch(). See XRayPatchingStatus for possible
// result values.
extern XRayPatchingStatus __xray_unpatch();
+
+// Use XRay to log the first argument of each (instrumented) function call.
+// When this function exits, all threads will have observed the effect and
+// start logging their subsequent affected function calls (if patched).
+//
+// Returns 1 on success, 0 on error.
+extern int __xray_set_handler_arg1(void (*)(int32_t, XRayEntryType, uint64_t));
+
+// Disables the XRay handler used to log first arguments of function calls.
+// Returns 1 on success, 0 on error.
+extern int __xray_remove_handler_arg1();
}
#endif
diff --git a/lib/xray/xray_AArch64.cc b/lib/xray/xray_AArch64.cc
index 43c097d11..8d1c7c5d8 100644
--- a/lib/xray/xray_AArch64.cc
+++ b/lib/xray/xray_AArch64.cc
@@ -92,8 +92,9 @@ inline static bool patchSled(const bool Enable, const uint32_t FuncId,
}
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
- const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
- return patchSled(Enable, FuncId, Sled, __xray_FunctionEntry);
+ const XRaySledEntry &Sled,
+ void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
+ return patchSled(Enable, FuncId, Sled, Trampoline);
}
bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
@@ -110,3 +111,7 @@ bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }
} // namespace __xray
+
+extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
+ // FIXME: this will have to be implemented in the trampoline assembly file
+}
diff --git a/lib/xray/xray_arm.cc b/lib/xray/xray_arm.cc
index fb78eb6e8..26d673ec2 100644
--- a/lib/xray/xray_arm.cc
+++ b/lib/xray/xray_arm.cc
@@ -128,8 +128,9 @@ inline static bool patchSled(const bool Enable, const uint32_t FuncId,
}
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
- const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
- return patchSled(Enable, FuncId, Sled, __xray_FunctionEntry);
+ const XRaySledEntry &Sled,
+ void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
+ return patchSled(Enable, FuncId, Sled, Trampoline);
}
bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
@@ -146,3 +147,7 @@ bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }
} // namespace __xray
+
+extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
+ // FIXME: this will have to be implemented in the trampoline assembly file
+}
diff --git a/lib/xray/xray_fdr_logging.cc b/lib/xray/xray_fdr_logging.cc
index 6db384519..bae7d4c4d 100644
--- a/lib/xray/xray_fdr_logging.cc
+++ b/lib/xray/xray_fdr_logging.cc
@@ -494,6 +494,7 @@ void fdrLoggingHandleArg0(int32_t FuncId,
switch (Entry) {
case XRayEntryType::ENTRY:
+ case XRayEntryType::LOG_ARGS_ENTRY:
FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter);
break;
case XRayEntryType::EXIT:
diff --git a/lib/xray/xray_interface.cc b/lib/xray/xray_interface.cc
index 306dbcae7..39cf8efb3 100644
--- a/lib/xray/xray_interface.cc
+++ b/lib/xray/xray_interface.cc
@@ -48,6 +48,9 @@ static const int16_t cSledLength = 8;
// This is the function to call when we encounter the entry or exit sleds.
std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr};
+// This is the function to call from the arg1-enabled sleds/trampolines.
+std::atomic<void (*)(int32_t, XRayEntryType, uint64_t)> XRayArgLogger{nullptr};
+
// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
// any successful mprotect(...) changes. This is used to make a page writeable
// and executable, and upon destruction if it was successful in doing so returns
@@ -185,7 +188,7 @@ XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
bool Success = false;
switch (Sled.Kind) {
case XRayEntryType::ENTRY:
- Success = patchFunctionEntry(Enable, FuncId, Sled);
+ Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);
break;
case XRayEntryType::EXIT:
Success = patchFunctionExit(Enable, FuncId, Sled);
@@ -193,6 +196,9 @@ XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
case XRayEntryType::TAIL:
Success = patchFunctionTailExit(Enable, FuncId, Sled);
break;
+ case XRayEntryType::LOG_ARGS_ENTRY:
+ Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);
+ break;
default:
Report("Unsupported sled kind: %d\n", int(Sled.Kind));
continue;
@@ -211,3 +217,16 @@ XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
return controlPatching(false);
}
+
+int __xray_set_handler_arg1(void (*Handler)(int32_t, XRayEntryType, uint64_t))
+{
+ if (!XRayInitialized.load(std::memory_order_acquire)) {
+ return 0;
+ }
+ // A relaxed write might not be visible even if the current thread gets
+ // scheduled on a different CPU/NUMA node. We need to wait for everyone to
+ // have this handler installed for consistency of collected data across CPUs.
+ XRayArgLogger.store(Handler, std::memory_order_release);
+ return 1;
+}
+int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }
diff --git a/lib/xray/xray_interface_internal.h b/lib/xray/xray_interface_internal.h
index c4f72433d..0e3a251f3 100644
--- a/lib/xray/xray_interface_internal.h
+++ b/lib/xray/xray_interface_internal.h
@@ -49,7 +49,7 @@ struct XRaySledMap {
};
bool patchFunctionEntry(bool Enable, uint32_t FuncId,
- const XRaySledEntry &Sled);
+ const XRaySledEntry &Sled, void (*Trampoline)());
bool patchFunctionExit(bool Enable, uint32_t FuncId, const XRaySledEntry &Sled);
bool patchFunctionTailExit(bool Enable, uint32_t FuncId,
const XRaySledEntry &Sled);
@@ -62,6 +62,7 @@ extern "C" {
extern void __xray_FunctionEntry();
extern void __xray_FunctionExit();
extern void __xray_FunctionTailExit();
+extern void __xray_ArgLoggerEntry();
}
#endif
diff --git a/lib/xray/xray_trampoline_x86_64.S b/lib/xray/xray_trampoline_x86_64.S
index d90c30cd9..b9fef6dad 100644
--- a/lib/xray/xray_trampoline_x86_64.S
+++ b/lib/xray/xray_trampoline_x86_64.S
@@ -53,6 +53,9 @@
.text
.file "xray_trampoline_x86.S"
+
+//===----------------------------------------------------------------------===//
+
.globl __xray_FunctionEntry
.align 16, 0x90
.type __xray_FunctionEntry,@function
@@ -81,6 +84,8 @@ __xray_FunctionEntry:
.size __xray_FunctionEntry, .Ltmp1-__xray_FunctionEntry
.cfi_endproc
+//===----------------------------------------------------------------------===//
+
.globl __xray_FunctionExit
.align 16, 0x90
.type __xray_FunctionExit,@function
@@ -117,6 +122,8 @@ __xray_FunctionExit:
.size __xray_FunctionExit, .Ltmp3-__xray_FunctionExit
.cfi_endproc
+//===----------------------------------------------------------------------===//
+
.global __xray_FunctionTailExit
.align 16, 0x90
.type __xray_FunctionTailExit,@function
@@ -145,3 +152,39 @@ __xray_FunctionTailExit:
.Ltmp5:
.size __xray_FunctionTailExit, .Ltmp5-__xray_FunctionTailExit
.cfi_endproc
+
+//===----------------------------------------------------------------------===//
+
+ .globl __xray_ArgLoggerEntry
+ .align 16, 0x90
+ .type __xray_ArgLoggerEntry,@function
+__xray_ArgLoggerEntry:
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ SAVE_REGISTERS
+
+ // Again, these function pointer loads must be atomic; MOV is fine.
+ movq _ZN6__xray13XRayArgLoggerE(%rip), %rax
+ testq %rax, %rax
+ jne .Larg1entryLog
+
+ // If [arg1 logging handler] not set, defer to no-arg logging.
+ movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax
+ testq %rax, %rax
+ je .Larg1entryFail
+
+.Larg1entryLog:
+ movq %rdi, %rdx // first argument will become the third
+ xorq %rsi, %rsi // XRayEntryType::ENTRY into the second
+ movl %r10d, %edi // 32-bit function ID becomes the first
+ callq *%rax
+
+.Larg1entryFail:
+ RESTORE_REGISTERS
+ popq %rbp
+ retq
+
+.Larg1entryEnd:
+ .size __xray_ArgLoggerEntry, .Larg1entryEnd-__xray_ArgLoggerEntry
+ .cfi_endproc
diff --git a/lib/xray/xray_x86_64.cc b/lib/xray/xray_x86_64.cc
index dd56e4214..1b9131316 100644
--- a/lib/xray/xray_x86_64.cc
+++ b/lib/xray/xray_x86_64.cc
@@ -82,7 +82,8 @@ static constexpr int64_t MinOffset{std::numeric_limits<int32_t>::min()};
static constexpr int64_t MaxOffset{std::numeric_limits<int32_t>::max()};
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
- const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
+ const XRaySledEntry &Sled,
+ void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
// Here we do the dance of replacing the following sled:
//
// xray_sled_n:
@@ -103,13 +104,12 @@ bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
// 4. Do an atomic write over the jmp instruction for the "mov r10d"
// opcode and first operand.
//
- // Prerequisite is to compute the relative offset to the
- // __xray_FunctionEntry function's address.
- int64_t TrampolineOffset = reinterpret_cast<int64_t>(__xray_FunctionEntry) -
+ // Prerequisite is to compute the relative offset to the trampoline's address.
+ int64_t TrampolineOffset = reinterpret_cast<int64_t>(Trampoline) -
(static_cast<int64_t>(Sled.Address) + 11);
if (TrampolineOffset < MinOffset || TrampolineOffset > MaxOffset) {
Report("XRay Entry trampoline (%p) too far from sled (%p)\n",
- __xray_FunctionEntry, reinterpret_cast<void *>(Sled.Address));
+ Trampoline, reinterpret_cast<void *>(Sled.Address));
return false;
}
if (Enable) {
diff --git a/test/xray/TestCases/Linux/arg1-logger.cc b/test/xray/TestCases/Linux/arg1-logger.cc
new file mode 100644
index 000000000..3140f4c88
--- /dev/null
+++ b/test/xray/TestCases/Linux/arg1-logger.cc
@@ -0,0 +1,41 @@
+// Check that we can get the first function argument logged
+// using a custom logging function.
+//
+// RUN: %clangxx_xray -std=c++11 %s -o %t
+// RUN: XRAY_OPTIONS="patch_premain=true verbosity=1 xray_logfile_base=arg1-logger-" %run %t 2>&1 | FileCheck %s
+//
+// After all that, clean up the XRay log file.
+//
+// RUN: rm arg1-logger-*
+//
+// At the time of writing, the ARM trampolines weren't written yet.
+// XFAIL: arm || aarch64
+
+#include "xray/xray_interface.h"
+
+#include <cinttypes>
+#include <cstdio>
+
+void arg1logger(int32_t fn, XRayEntryType t, uint64_t a1) {
+ printf("Arg1: %" PRIx64 ", XRayEntryType %u\n", a1, t);
+}
+
+[[clang::xray_always_instrument, clang::xray_log_args(1)]] void foo(void *) {}
+
+int main() {
+ // CHECK: XRay: Log file in 'arg1-logger-{{.*}}'
+
+ __xray_set_handler_arg1(arg1logger);
+ foo(nullptr);
+ // CHECK: Arg1: 0, XRayEntryType 0
+
+ __xray_remove_handler_arg1();
+ foo((void *) 0xBADC0DE);
+ // nothing expected to see here
+
+ __xray_set_handler_arg1(arg1logger);
+ foo((void *) 0xDEADBEEFCAFE);
+ // CHECK-NEXT: Arg1: deadbeefcafe, XRayEntryType 0
+ foo((void *) -1);
+ // CHECK-NEXT: Arg1: ffffffffffffffff, XRayEntryType 0
+}