@@ -91,19 +91,6 @@ static void writeEOBMetadata();
91
91
// / TSC Wrap records are written when a TSC delta encoding scheme overflows.
92
92
static void writeTSCWrapMetadata (uint64_t TSC);
93
93
94
- // / Here's where the meat of the processing happens. The writer captures
95
- // / function entry, exit and tail exit points with a time and will create
96
- // / TSCWrap, NewCPUId and Function records as necessary. The writer might
97
- // / walk backward through its buffer and erase trivial functions to avoid
98
- // / polluting the log and may use the buffer queue to obtain or release a
99
- // / buffer.
100
- static void processFunctionHook (int32_t FuncId, XRayEntryType Entry,
101
- uint64_t TSC, unsigned char CPU,
102
- int (*wall_clock_reader)(clockid_t ,
103
- struct timespec *),
104
- __sanitizer::atomic_sint32_t &LoggingStatus,
105
- const std::shared_ptr<BufferQueue> &BQ);
106
-
107
94
// Group together thread-local-data in a struct, then hide it behind a function
108
95
// call so that it can be initialized on first use instead of as a global.
109
96
struct ThreadLocalData {
@@ -331,9 +318,22 @@ inline void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT {
331
318
writeTSCWrapMetadata (TSC, getThreadLocalData ().RecordPtr );
332
319
}
333
320
334
- inline void writeFunctionRecord (int FuncId, uint32_t TSCDelta,
335
- XRayEntryType EntryType,
336
- char *&MemPtr) XRAY_NEVER_INSTRUMENT {
321
+ // Call Argument metadata records store the arguments to a function in the
322
+ // order of their appearance; holes are not supported by the buffer format.
323
+ static inline void writeCallArgumentMetadata (uint64_t A) XRAY_NEVER_INSTRUMENT {
324
+ auto &TLD = getThreadLocalData ();
325
+ MetadataRecord CallArg;
326
+ CallArg.Type = uint8_t (RecordType::Metadata);
327
+ CallArg.RecordKind = uint8_t (MetadataRecord::RecordKinds::CallArgument);
328
+
329
+ std::memcpy (CallArg.Data , &A, sizeof (A));
330
+ std::memcpy (TLD.RecordPtr , &CallArg, sizeof (MetadataRecord));
331
+ TLD.RecordPtr += sizeof (MetadataRecord);
332
+ }
333
+
334
+ static inline void writeFunctionRecord (int FuncId, uint32_t TSCDelta,
335
+ XRayEntryType EntryType,
336
+ char *&MemPtr) XRAY_NEVER_INSTRUMENT {
337
337
std::aligned_storage<sizeof (FunctionRecord), alignof (FunctionRecord)>::type
338
338
AlignedFuncRecordBuffer;
339
339
auto &FuncRecord =
@@ -560,6 +560,41 @@ inline bool isLogInitializedAndReady(
560
560
return true ;
561
561
} // namespace __xray_fdr_internal
562
562
563
+ // Compute the TSC difference between the time of measurement and the previous
564
+ // event. There are a few interesting situations we need to account for:
565
+ //
566
+ // - The thread has migrated to a different CPU. If this is the case, then
567
+ // we write down the following records:
568
+ //
569
+ // 1. A 'NewCPUId' Metadata record.
570
+ // 2. A FunctionRecord with a 0 for the TSCDelta field.
571
+ //
572
+ // - The TSC delta is greater than the 32 bits we can store in a
573
+ // FunctionRecord. In this case we write down the following records:
574
+ //
575
+ // 1. A 'TSCWrap' Metadata record.
576
+ // 2. A FunctionRecord with a 0 for the TSCDelta field.
577
+ //
578
+ // - The TSC delta is representable within the 32 bits we can store in a
579
+ // FunctionRecord. In this case we write down just a FunctionRecord with
580
+ // the correct TSC delta.
581
+ inline uint32_t writeCurrentCPUTSC (ThreadLocalData &TLD, uint64_t TSC, uint8_t CPU) {
582
+ if (CPU != TLD.CurrentCPU ) {
583
+ // We've moved to a new CPU.
584
+ writeNewCPUIdMetadata (CPU, TSC);
585
+ return 0 ;
586
+ }
587
+ // If the delta is greater than the range for a uint32_t, then we write out
588
+ // the TSC wrap metadata entry with the full TSC, and the TSC for the
589
+ // function record be 0.
590
+ uint64_t Delta = TSC - TLD.LastTSC ;
591
+ if (Delta <= std::numeric_limits<uint32_t >::max ())
592
+ return Delta;
593
+
594
+ writeTSCWrapMetadata (TSC);
595
+ return 0 ;
596
+ }
597
+
563
598
inline void endBufferIfFull () XRAY_NEVER_INSTRUMENT {
564
599
auto &TLD = getThreadLocalData ();
565
600
auto BufferStart = static_cast <char *>(TLD.Buffer .Buffer );
@@ -573,10 +608,15 @@ inline void endBufferIfFull() XRAY_NEVER_INSTRUMENT {
573
608
574
609
thread_local volatile bool Running = false ;
575
610
611
+ // / Here's where the meat of the processing happens. The writer captures
612
+ // / function entry, exit and tail exit points with a time and will create
613
+ // / TSCWrap, NewCPUId and Function records as necessary. The writer might
614
+ // / walk backward through its buffer and erase trivial functions to avoid
615
+ // / polluting the log and may use the buffer queue to obtain or release a
616
+ // / buffer.
576
617
inline void processFunctionHook (
577
618
int32_t FuncId, XRayEntryType Entry, uint64_t TSC, unsigned char CPU,
578
- int (*wall_clock_reader)(clockid_t , struct timespec *),
579
- __sanitizer::atomic_sint32_t &LoggingStatus,
619
+ uint64_t Arg1, int (*wall_clock_reader)(clockid_t , struct timespec *),
580
620
const std::shared_ptr<BufferQueue> &BQ) XRAY_NEVER_INSTRUMENT {
581
621
// Prevent signal handler recursion, so in case we're already in a log writing
582
622
// mode and the signal handler comes in (and is also instrumented) then we
@@ -609,10 +649,10 @@ inline void processFunctionHook(
609
649
// - The least number of bytes we will ever write is 8
610
650
// (sizeof(FunctionRecord)) only if the delta between the previous entry
611
651
// and this entry is within 32 bits.
612
- // - The most number of bytes we will ever write is 8 + 16 = 24. This is
613
- // computed by:
652
+ // - The most number of bytes we will ever write is 8 + 16 + 16 = 40.
653
+ // This is computed by:
614
654
//
615
- // sizeof(FunctionRecord) + sizeof(MetadataRecord)
655
+ // MaxSize = sizeof(FunctionRecord) + 2 * sizeof(MetadataRecord)
616
656
//
617
657
// These arise in the following cases:
618
658
//
@@ -626,6 +666,7 @@ inline void processFunctionHook(
626
666
// FunctionRecord.
627
667
// 3. When we learn about a new CPU ID, we need to write down a "new cpu
628
668
// id" MetadataRecord before writing out the actual FunctionRecord.
669
+ // 4. The second MetadataRecord is the optional function call argument.
629
670
//
630
671
// - An End-of-Buffer (EOB) MetadataRecord is 16 bytes.
631
672
//
@@ -634,53 +675,18 @@ inline void processFunctionHook(
634
675
// MetadataRecord. If we don't have enough space after writing as much as 24
635
676
// bytes in the end of the buffer, we need to write out the EOB, get a new
636
677
// Buffer, set it up properly before doing any further writing.
637
- //
638
- if (!prepareBuffer (wall_clock_reader, FunctionRecSize + MetadataRecSize )) {
678
+ size_t MaxSize = FunctionRecSize + 2 * MetadataRecSize;
679
+ if (!prepareBuffer (wall_clock_reader, MaxSize )) {
639
680
TLD.LocalBQ = nullptr ;
640
681
return ;
641
682
}
642
683
643
- // By this point, we are now ready to write at most 24 bytes (one metadata
644
- // record and one function record).
645
- assert ((TLD.RecordPtr + (MetadataRecSize + FunctionRecSize)) -
646
- static_cast <char *>(TLD.Buffer .Buffer ) >=
684
+ // By this point, we are now ready to write up to 40 bytes (explained above).
685
+ assert ((TLD.RecordPtr + MaxSize) - static_cast <char *>(TLD.Buffer .Buffer ) >=
647
686
static_cast <ptrdiff_t >(MetadataRecSize) &&
648
687
" Misconfigured BufferQueue provided; Buffer size not large enough." );
649
688
650
- // Here we compute the TSC Delta. There are a few interesting situations we
651
- // need to account for:
652
- //
653
- // - The thread has migrated to a different CPU. If this is the case, then
654
- // we write down the following records:
655
- //
656
- // 1. A 'NewCPUId' Metadata record.
657
- // 2. A FunctionRecord with a 0 for the TSCDelta field.
658
- //
659
- // - The TSC delta is greater than the 32 bits we can store in a
660
- // FunctionRecord. In this case we write down the following records:
661
- //
662
- // 1. A 'TSCWrap' Metadata record.
663
- // 2. A FunctionRecord with a 0 for the TSCDelta field.
664
- //
665
- // - The TSC delta is representable within the 32 bits we can store in a
666
- // FunctionRecord. In this case we write down just a FunctionRecord with
667
- // the correct TSC delta.
668
- //
669
- uint32_t RecordTSCDelta = 0 ;
670
- if (CPU != TLD.CurrentCPU ) {
671
- // We've moved to a new CPU.
672
- writeNewCPUIdMetadata (CPU, TSC);
673
- } else {
674
- // If the delta is greater than the range for a uint32_t, then we write out
675
- // the TSC wrap metadata entry with the full TSC, and the TSC for the
676
- // function record be 0.
677
- auto Delta = TSC - TLD.LastTSC ;
678
- if (Delta > (1ULL << 32 ) - 1 )
679
- writeTSCWrapMetadata (TSC);
680
- else
681
- RecordTSCDelta = Delta;
682
- }
683
-
689
+ auto RecordTSCDelta = writeCurrentCPUTSC (TLD, TSC, CPU);
684
690
TLD.LastTSC = TSC;
685
691
TLD.CurrentCPU = CPU;
686
692
switch (Entry) {
@@ -711,6 +717,8 @@ inline void processFunctionHook(
711
717
}
712
718
713
719
writeFunctionRecord (FuncId, RecordTSCDelta, Entry, TLD.RecordPtr );
720
+ if (Entry == XRayEntryType::LOG_ARGS_ENTRY)
721
+ writeCallArgumentMetadata (Arg1);
714
722
715
723
// If we've exhausted the buffer by this time, we then release the buffer to
716
724
// make sure that other threads may start using this buffer.
0 commit comments