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