]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/xray/xray_fdr_controller.h
Merge llvm, clang, compiler-rt, libc++, lld, and lldb release_80 branch
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / xray / xray_fdr_controller.h
1 //===-- xray_fdr_controller.h ---------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of XRay, a function call tracing system.
11 //
12 //===----------------------------------------------------------------------===//
13 #ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
14 #define COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
15
16 #include <limits>
17 #include <time.h>
18
19 #include "xray/xray_interface.h"
20 #include "xray/xray_records.h"
21 #include "xray_buffer_queue.h"
22 #include "xray_fdr_log_writer.h"
23
24 namespace __xray {
25
26 template <size_t Version = 5> class FDRController {
27   BufferQueue *BQ;
28   BufferQueue::Buffer &B;
29   FDRLogWriter &W;
30   int (*WallClockReader)(clockid_t, struct timespec *) = 0;
31   uint64_t CycleThreshold = 0;
32
33   uint64_t LastFunctionEntryTSC = 0;
34   uint64_t LatestTSC = 0;
35   uint16_t LatestCPU = 0;
36   tid_t TId = 0;
37   pid_t PId = 0;
38   bool First = true;
39
40   uint32_t UndoableFunctionEnters = 0;
41   uint32_t UndoableTailExits = 0;
42
43   bool finalized() const XRAY_NEVER_INSTRUMENT {
44     return BQ == nullptr || BQ->finalizing();
45   }
46
47   bool hasSpace(size_t S) XRAY_NEVER_INSTRUMENT {
48     return B.Data != nullptr && B.Generation == BQ->generation() &&
49            W.getNextRecord() + S <= reinterpret_cast<char *>(B.Data) + B.Size;
50   }
51
52   constexpr int32_t mask(int32_t FuncId) const XRAY_NEVER_INSTRUMENT {
53     return FuncId & ((1 << 29) - 1);
54   }
55
56   bool getNewBuffer() XRAY_NEVER_INSTRUMENT {
57     if (BQ->getBuffer(B) != BufferQueue::ErrorCode::Ok)
58       return false;
59
60     W.resetRecord();
61     DCHECK_EQ(W.getNextRecord(), B.Data);
62     LatestTSC = 0;
63     LatestCPU = 0;
64     First = true;
65     UndoableFunctionEnters = 0;
66     UndoableTailExits = 0;
67     atomic_store(B.Extents, 0, memory_order_release);
68     return true;
69   }
70
71   bool setupNewBuffer() XRAY_NEVER_INSTRUMENT {
72     if (finalized())
73       return false;
74
75     DCHECK(hasSpace(sizeof(MetadataRecord) * 3));
76     TId = GetTid();
77     PId = internal_getpid();
78     struct timespec TS {
79       0, 0
80     };
81     WallClockReader(CLOCK_MONOTONIC, &TS);
82
83     MetadataRecord Metadata[] = {
84         // Write out a MetadataRecord to signify that this is the start of a new
85         // buffer, associated with a particular thread, with a new CPU. For the
86         // data, we have 15 bytes to squeeze as much information as we can. At
87         // this point we only write down the following bytes:
88         //   - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8
89         //   bytes)
90         createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(
91             static_cast<int32_t>(TId)),
92
93         // Also write the WalltimeMarker record. We only really need microsecond
94         // precision here, and enforce across platforms that we need 64-bit
95         // seconds and 32-bit microseconds encoded in the Metadata record.
96         createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
97             static_cast<int64_t>(TS.tv_sec),
98             static_cast<int32_t>(TS.tv_nsec / 1000)),
99
100         // Also write the Pid record.
101         createMetadataRecord<MetadataRecord::RecordKinds::Pid>(
102             static_cast<int32_t>(PId)),
103     };
104
105     if (finalized())
106       return false;
107     return W.writeMetadataRecords(Metadata);
108   }
109
110   bool prepareBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
111     if (finalized())
112       return returnBuffer();
113
114     if (UNLIKELY(!hasSpace(S))) {
115       if (!returnBuffer())
116         return false;
117       if (!getNewBuffer())
118         return false;
119       if (!setupNewBuffer())
120         return false;
121     }
122
123     if (First) {
124       First = false;
125       W.resetRecord();
126       atomic_store(B.Extents, 0, memory_order_release);
127       return setupNewBuffer();
128     }
129
130     return true;
131   }
132
133   bool returnBuffer() XRAY_NEVER_INSTRUMENT {
134     if (BQ == nullptr)
135       return false;
136
137     First = true;
138     if (finalized()) {
139       BQ->releaseBuffer(B); // ignore result.
140       return false;
141     }
142
143     return BQ->releaseBuffer(B) == BufferQueue::ErrorCode::Ok;
144   }
145
146   enum class PreambleResult { NoChange, WroteMetadata, InvalidBuffer };
147   PreambleResult recordPreamble(uint64_t TSC,
148                                 uint16_t CPU) XRAY_NEVER_INSTRUMENT {
149     if (UNLIKELY(LatestCPU != CPU || LatestTSC == 0)) {
150       // We update our internal tracking state for the Latest TSC and CPU we've
151       // seen, then write out the appropriate metadata and function records.
152       LatestTSC = TSC;
153       LatestCPU = CPU;
154
155       if (B.Generation != BQ->generation())
156         return PreambleResult::InvalidBuffer;
157
158       W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);
159       return PreambleResult::WroteMetadata;
160     }
161
162     DCHECK_EQ(LatestCPU, CPU);
163
164     if (UNLIKELY(LatestTSC > TSC ||
165                  TSC - LatestTSC >
166                      uint64_t{std::numeric_limits<int32_t>::max()})) {
167       // Either the TSC has wrapped around from the last TSC we've seen or the
168       // delta is too large to fit in a 32-bit signed integer, so we write a
169       // wrap-around record.
170       LatestTSC = TSC;
171
172       if (B.Generation != BQ->generation())
173         return PreambleResult::InvalidBuffer;
174
175       W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);
176       return PreambleResult::WroteMetadata;
177     }
178
179     return PreambleResult::NoChange;
180   }
181
182   bool rewindRecords(int32_t FuncId, uint64_t TSC,
183                      uint16_t CPU) XRAY_NEVER_INSTRUMENT {
184     // Undo one enter record, because at this point we are either at the state
185     // of:
186     // - We are exiting a function that we recently entered.
187     // - We are exiting a function that was the result of a sequence of tail
188     //   exits, and we can check whether the tail exits can be re-wound.
189     //
190     FunctionRecord F;
191     W.undoWrites(sizeof(FunctionRecord));
192     if (B.Generation != BQ->generation())
193       return false;
194     internal_memcpy(&F, W.getNextRecord(), sizeof(FunctionRecord));
195
196     DCHECK(F.RecordKind ==
197                uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&
198            "Expected to find function entry recording when rewinding.");
199     DCHECK_EQ(F.FuncId, FuncId & ~(0x0F << 28));
200
201     LatestTSC -= F.TSCDelta;
202     if (--UndoableFunctionEnters != 0) {
203       LastFunctionEntryTSC -= F.TSCDelta;
204       return true;
205     }
206
207     LastFunctionEntryTSC = 0;
208     auto RewindingTSC = LatestTSC;
209     auto RewindingRecordPtr = W.getNextRecord() - sizeof(FunctionRecord);
210     while (UndoableTailExits) {
211       if (B.Generation != BQ->generation())
212         return false;
213       internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
214       DCHECK_EQ(F.RecordKind,
215                 uint8_t(FunctionRecord::RecordKinds::FunctionTailExit));
216       RewindingTSC -= F.TSCDelta;
217       RewindingRecordPtr -= sizeof(FunctionRecord);
218       if (B.Generation != BQ->generation())
219         return false;
220       internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
221
222       // This tail call exceeded the threshold duration. It will not be erased.
223       if ((TSC - RewindingTSC) >= CycleThreshold) {
224         UndoableTailExits = 0;
225         return true;
226       }
227
228       --UndoableTailExits;
229       W.undoWrites(sizeof(FunctionRecord) * 2);
230       LatestTSC = RewindingTSC;
231     }
232     return true;
233   }
234
235 public:
236   template <class WallClockFunc>
237   FDRController(BufferQueue *BQ, BufferQueue::Buffer &B, FDRLogWriter &W,
238                 WallClockFunc R, uint64_t C) XRAY_NEVER_INSTRUMENT
239       : BQ(BQ),
240         B(B),
241         W(W),
242         WallClockReader(R),
243         CycleThreshold(C) {}
244
245   bool functionEnter(int32_t FuncId, uint64_t TSC,
246                      uint16_t CPU) XRAY_NEVER_INSTRUMENT {
247     if (finalized() ||
248         !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
249       return returnBuffer();
250
251     auto PreambleStatus = recordPreamble(TSC, CPU);
252     if (PreambleStatus == PreambleResult::InvalidBuffer)
253       return returnBuffer();
254
255     if (PreambleStatus == PreambleResult::WroteMetadata) {
256       UndoableFunctionEnters = 1;
257       UndoableTailExits = 0;
258     } else {
259       ++UndoableFunctionEnters;
260     }
261
262     auto Delta = TSC - LatestTSC;
263     LastFunctionEntryTSC = TSC;
264     LatestTSC = TSC;
265     return W.writeFunction(FDRLogWriter::FunctionRecordKind::Enter,
266                            mask(FuncId), Delta);
267   }
268
269   bool functionTailExit(int32_t FuncId, uint64_t TSC,
270                         uint16_t CPU) XRAY_NEVER_INSTRUMENT {
271     if (finalized())
272       return returnBuffer();
273
274     if (!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
275       return returnBuffer();
276
277     auto PreambleStatus = recordPreamble(TSC, CPU);
278     if (PreambleStatus == PreambleResult::InvalidBuffer)
279       return returnBuffer();
280
281     if (PreambleStatus == PreambleResult::NoChange &&
282         UndoableFunctionEnters != 0 &&
283         TSC - LastFunctionEntryTSC < CycleThreshold)
284       return rewindRecords(FuncId, TSC, CPU);
285
286     UndoableTailExits = UndoableFunctionEnters ? UndoableTailExits + 1 : 0;
287     UndoableFunctionEnters = 0;
288     auto Delta = TSC - LatestTSC;
289     LatestTSC = TSC;
290     return W.writeFunction(FDRLogWriter::FunctionRecordKind::TailExit,
291                            mask(FuncId), Delta);
292   }
293
294   bool functionEnterArg(int32_t FuncId, uint64_t TSC, uint16_t CPU,
295                         uint64_t Arg) XRAY_NEVER_INSTRUMENT {
296     if (finalized() ||
297         !prepareBuffer((2 * sizeof(MetadataRecord)) + sizeof(FunctionRecord)) ||
298         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
299       return returnBuffer();
300
301     auto Delta = TSC - LatestTSC;
302     LatestTSC = TSC;
303     LastFunctionEntryTSC = 0;
304     UndoableFunctionEnters = 0;
305     UndoableTailExits = 0;
306
307     return W.writeFunctionWithArg(FDRLogWriter::FunctionRecordKind::EnterArg,
308                                   mask(FuncId), Delta, Arg);
309   }
310
311   bool functionExit(int32_t FuncId, uint64_t TSC,
312                     uint16_t CPU) XRAY_NEVER_INSTRUMENT {
313     if (finalized() ||
314         !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
315       return returnBuffer();
316
317     auto PreambleStatus = recordPreamble(TSC, CPU);
318     if (PreambleStatus == PreambleResult::InvalidBuffer)
319       return returnBuffer();
320
321     if (PreambleStatus == PreambleResult::NoChange &&
322         UndoableFunctionEnters != 0 &&
323         TSC - LastFunctionEntryTSC < CycleThreshold)
324       return rewindRecords(FuncId, TSC, CPU);
325
326     auto Delta = TSC - LatestTSC;
327     LatestTSC = TSC;
328     UndoableFunctionEnters = 0;
329     UndoableTailExits = 0;
330     return W.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, mask(FuncId),
331                            Delta);
332   }
333
334   bool customEvent(uint64_t TSC, uint16_t CPU, const void *Event,
335                    int32_t EventSize) XRAY_NEVER_INSTRUMENT {
336     if (finalized() ||
337         !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
338         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
339       return returnBuffer();
340
341     auto Delta = TSC - LatestTSC;
342     LatestTSC = TSC;
343     UndoableFunctionEnters = 0;
344     UndoableTailExits = 0;
345     return W.writeCustomEvent(Delta, Event, EventSize);
346   }
347
348   bool typedEvent(uint64_t TSC, uint16_t CPU, uint16_t EventType,
349                   const void *Event, int32_t EventSize) XRAY_NEVER_INSTRUMENT {
350     if (finalized() ||
351         !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
352         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
353       return returnBuffer();
354
355     auto Delta = TSC - LatestTSC;
356     LatestTSC = TSC;
357     UndoableFunctionEnters = 0;
358     UndoableTailExits = 0;
359     return W.writeTypedEvent(Delta, EventType, Event, EventSize);
360   }
361
362   bool flush() XRAY_NEVER_INSTRUMENT {
363     if (finalized()) {
364       returnBuffer(); // ignore result.
365       return true;
366     }
367     return returnBuffer();
368   }
369 };
370
371 } // namespace __xray
372
373 #endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_