LLVM 18.0.0git
InstrProfCorrelator.cpp
Go to the documentation of this file.
1//===-- InstrProfCorrelator.cpp -------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
17#include "llvm/Object/MachO.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/Format.h"
21#include <optional>
22
23#define DEBUG_TYPE "correlator"
24
25using namespace llvm;
26
27/// Get the __llvm_prf_cnts section.
29 for (auto &Section : Obj.sections())
30 if (auto SectionName = Section.getName())
31 if (SectionName.get() == INSTR_PROF_CNTS_SECT_NAME)
32 return Section;
33 return make_error<InstrProfError>(
34 instrprof_error::unable_to_correlate_profile,
35 "could not find counter section (" INSTR_PROF_CNTS_SECT_NAME ")");
36}
37
38const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name";
39const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash";
40const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
42 "Coverage Function Name";
43
45InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
46 const object::ObjectFile &Obj) {
47 auto CountersSection = getCountersSection(Obj);
48 if (auto Err = CountersSection.takeError())
49 return std::move(Err);
50 auto C = std::make_unique<Context>();
51 C->Buffer = std::move(Buffer);
52 C->CountersSectionStart = CountersSection->getAddress();
53 C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize();
54 C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost;
55 return Expected<std::unique_ptr<Context>>(std::move(C));
56}
57
60 auto DsymObjectsOrErr =
62 if (auto Err = DsymObjectsOrErr.takeError())
63 return std::move(Err);
64 if (!DsymObjectsOrErr->empty()) {
65 // TODO: Enable profile correlation when there are multiple objects in a
66 // dSYM bundle.
67 if (DsymObjectsOrErr->size() > 1)
68 return make_error<InstrProfError>(
70 "using multiple objects is not yet supported");
71 DebugInfoFilename = *DsymObjectsOrErr->begin();
72 }
73 auto BufferOrErr =
74 errorOrToExpected(MemoryBuffer::getFile(DebugInfoFilename));
75 if (auto Err = BufferOrErr.takeError())
76 return std::move(Err);
77
78 return get(std::move(*BufferOrErr));
79}
80
82InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer) {
83 auto BinOrErr = object::createBinary(*Buffer);
84 if (auto Err = BinOrErr.takeError())
85 return std::move(Err);
86
87 if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) {
88 auto CtxOrErr = Context::get(std::move(Buffer), *Obj);
89 if (auto Err = CtxOrErr.takeError())
90 return std::move(Err);
91 auto T = Obj->makeTriple();
92 if (T.isArch64Bit())
93 return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj);
94 if (T.isArch32Bit())
95 return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj);
96 }
97 return make_error<InstrProfError>(
99}
100
101std::optional<size_t> InstrProfCorrelator::getDataSize() const {
102 if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint32_t>>(this)) {
103 return C->getDataSize();
104 } else if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint64_t>>(this)) {
105 return C->getDataSize();
106 }
107 return {};
108}
109
110namespace llvm {
111
112template <>
114 std::unique_ptr<InstrProfCorrelator::Context> Ctx)
116 std::move(Ctx)) {}
117template <>
119 std::unique_ptr<InstrProfCorrelator::Context> Ctx)
121 std::move(Ctx)) {}
122template <>
124 return C->getKind() == InstrProfCorrelatorKind::CK_32Bit;
125}
126template <>
128 return C->getKind() == InstrProfCorrelatorKind::CK_64Bit;
129}
130
131} // end namespace llvm
132
133template <class IntPtrT>
136 std::unique_ptr<InstrProfCorrelator::Context> Ctx,
137 const object::ObjectFile &Obj) {
138 if (Obj.isELF() || Obj.isMachO()) {
139 auto DICtx = DWARFContext::create(Obj);
140 return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(std::move(DICtx),
141 std::move(Ctx));
142 }
143 return make_error<InstrProfError>(
144 instrprof_error::unable_to_correlate_profile,
145 "unsupported debug info format (only DWARF is supported)");
146}
147
148template <class IntPtrT>
150 assert(Data.empty() && Names.empty() && NamesVec.empty());
151 correlateProfileDataImpl(MaxWarnings);
152 if (Data.empty() || NamesVec.empty())
153 return make_error<InstrProfError>(
154 instrprof_error::unable_to_correlate_profile,
155 "could not find any profile metadata in debug info");
156 auto Result =
157 collectPGOFuncNameStrings(NamesVec, /*doCompression=*/false, Names);
158 CounterOffsets.clear();
159 NamesVec.clear();
160 return Result;
161}
162
163template <> struct yaml::MappingTraits<InstrProfCorrelator::CorrelationData> {
164 static void mapping(yaml::IO &io,
166 io.mapRequired("Probes", Data.Probes);
167 }
168};
169
170template <> struct yaml::MappingTraits<InstrProfCorrelator::Probe> {
171 static void mapping(yaml::IO &io, InstrProfCorrelator::Probe &P) {
172 io.mapRequired("Function Name", P.FunctionName);
173 io.mapOptional("Linkage Name", P.LinkageName);
174 io.mapRequired("CFG Hash", P.CFGHash);
175 io.mapRequired("Counter Offset", P.CounterOffset);
176 io.mapRequired("Num Counters", P.NumCounters);
177 io.mapOptional("File", P.FilePath);
178 io.mapOptional("Line", P.LineNumber);
179 }
180};
181
182template <> struct yaml::SequenceElementTraits<InstrProfCorrelator::Probe> {
183 static const bool flow = false;
184};
185
186template <class IntPtrT>
188 raw_ostream &OS) {
190 correlateProfileDataImpl(MaxWarnings, &Data);
191 if (Data.Probes.empty())
192 return make_error<InstrProfError>(
193 instrprof_error::unable_to_correlate_profile,
194 "could not find any profile metadata in debug info");
195 yaml::Output YamlOS(OS);
196 YamlOS << Data;
197 return Error::success();
198}
199
200template <class IntPtrT>
202 uint64_t CFGHash,
203 IntPtrT CounterOffset,
204 IntPtrT FunctionPtr,
205 uint32_t NumCounters) {
206 // Check if a probe was already added for this counter offset.
207 if (!CounterOffsets.insert(CounterOffset).second)
208 return;
209 Data.push_back({
210 maybeSwap<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName)),
211 maybeSwap<uint64_t>(CFGHash),
212 // In this mode, CounterPtr actually stores the section relative address
213 // of the counter.
214 maybeSwap<IntPtrT>(CounterOffset),
215 maybeSwap<IntPtrT>(FunctionPtr),
216 // TODO: Value profiling is not yet supported.
217 /*ValuesPtr=*/maybeSwap<IntPtrT>(0),
218 maybeSwap<uint32_t>(NumCounters),
219 /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)},
220 });
221 NamesVec.push_back(FunctionName.str());
222}
223
224template <class IntPtrT>
225std::optional<uint64_t>
227 auto Locations = Die.getLocations(dwarf::DW_AT_location);
228 if (!Locations) {
229 consumeError(Locations.takeError());
230 return {};
231 }
232 auto &DU = *Die.getDwarfUnit();
233 auto AddressSize = DU.getAddressByteSize();
234 for (auto &Location : *Locations) {
235 DataExtractor Data(Location.Expr, DICtx->isLittleEndian(), AddressSize);
236 DWARFExpression Expr(Data, AddressSize);
237 for (auto &Op : Expr) {
238 if (Op.getCode() == dwarf::DW_OP_addr) {
239 return Op.getRawOperand(0);
240 } else if (Op.getCode() == dwarf::DW_OP_addrx) {
242 if (auto SA = DU.getAddrOffsetSectionItem(Index))
243 return SA->Address;
244 }
245 }
246 }
247 return {};
248}
249
250template <class IntPtrT>
252 const auto &ParentDie = Die.getParent();
253 if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL())
254 return false;
255 if (Die.getTag() != dwarf::DW_TAG_variable)
256 return false;
257 if (!ParentDie.isSubprogramDIE())
258 return false;
259 if (!Die.hasChildren())
260 return false;
261 if (const char *Name = Die.getName(DINameKind::ShortName))
263 return false;
264}
265
266template <class IntPtrT>
268 int MaxWarnings, InstrProfCorrelator::CorrelationData *Data) {
269 bool UnlimitedWarnings = (MaxWarnings == 0);
270 // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
271 int NumSuppressedWarnings = -MaxWarnings;
272 auto maybeAddProbe = [&](DWARFDie Die) {
273 if (!isDIEOfProbe(Die))
274 return;
275 std::optional<const char *> FunctionName;
276 std::optional<uint64_t> CFGHash;
277 std::optional<uint64_t> CounterPtr = getLocation(Die);
278 auto FnDie = Die.getParent();
279 auto FunctionPtr = dwarf::toAddress(FnDie.find(dwarf::DW_AT_low_pc));
280 std::optional<uint64_t> NumCounters;
281 for (const DWARFDie &Child : Die.children()) {
282 if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation)
283 continue;
284 auto AnnotationFormName = Child.find(dwarf::DW_AT_name);
285 auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value);
286 if (!AnnotationFormName || !AnnotationFormValue)
287 continue;
288 auto AnnotationNameOrErr = AnnotationFormName->getAsCString();
289 if (auto Err = AnnotationNameOrErr.takeError()) {
290 consumeError(std::move(Err));
291 continue;
292 }
293 StringRef AnnotationName = *AnnotationNameOrErr;
294 if (AnnotationName.compare(
296 if (auto EC =
297 AnnotationFormValue->getAsCString().moveInto(FunctionName))
298 consumeError(std::move(EC));
299 } else if (AnnotationName.compare(
301 CFGHash = AnnotationFormValue->getAsUnsignedConstant();
302 } else if (AnnotationName.compare(
304 NumCounters = AnnotationFormValue->getAsUnsignedConstant();
305 }
306 }
307 if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) {
308 if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
310 << "Incomplete DIE for function " << FunctionName
311 << ": CFGHash=" << CFGHash << " CounterPtr=" << CounterPtr
312 << " NumCounters=" << NumCounters << "\n";
313 LLVM_DEBUG(Die.dump(dbgs()));
314 }
315 return;
316 }
317 uint64_t CountersStart = this->Ctx->CountersSectionStart;
318 uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
319 if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) {
320 if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
322 << format("CounterPtr out of range for function %s: Actual=0x%x "
323 "Expected=[0x%x, 0x%x)\n",
324 *FunctionName, *CounterPtr, CountersStart, CountersEnd);
325 LLVM_DEBUG(Die.dump(dbgs()));
326 }
327 return;
328 }
329 if (!FunctionPtr && (UnlimitedWarnings || ++NumSuppressedWarnings < 1)) {
330 WithColor::warning() << format("Could not find address of function %s\n",
331 *FunctionName);
332 LLVM_DEBUG(Die.dump(dbgs()));
333 }
334 IntPtrT CounterOffset = *CounterPtr - CountersStart;
335 if (Data) {
337 P.FunctionName = *FunctionName;
338 if (auto Name = FnDie.getName(DINameKind::LinkageName))
339 P.LinkageName = Name;
340 P.CFGHash = *CFGHash;
341 P.CounterOffset = CounterOffset;
342 P.NumCounters = *NumCounters;
343 auto FilePath = FnDie.getDeclFile(
344 DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath);
345 if (!FilePath.empty())
346 P.FilePath = FilePath;
347 if (auto LineNumber = FnDie.getDeclLine())
348 P.LineNumber = LineNumber;
349 Data->Probes.push_back(P);
350 } else {
351 this->addProbe(*FunctionName, *CFGHash, CounterOffset,
352 FunctionPtr.value_or(0), *NumCounters);
353 }
354 };
355 for (auto &CU : DICtx->normal_units())
356 for (const auto &Entry : CU->dies())
357 maybeAddProbe(DWARFDie(CU.get(), &Entry));
358 for (auto &CU : DICtx->dwo_units())
359 for (const auto &Entry : CU->dies())
360 maybeAddProbe(DWARFDie(CU.get(), &Entry));
361
362 if (!UnlimitedWarnings && NumSuppressedWarnings > 0)
363 WithColor::warning() << format("Suppressed %d additional warnings\n",
364 NumSuppressedWarnings);
365}
366
367template <class IntPtrT>
369 int MaxWarnings) {
370 bool UnlimitedWarnings = (MaxWarnings == 0);
371 // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
372 int NumSuppressedWarnings = -MaxWarnings;
373 std::vector<std::string> UnusedFuncNames;
374 auto IsDIEOfCovName = [](const DWARFDie &Die) {
375 const auto &ParentDie = Die.getParent();
376 if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL())
377 return false;
378 if (Die.getTag() != dwarf::DW_TAG_variable)
379 return false;
380 if (ParentDie.getParent().isValid())
381 return false;
382 if (!Die.hasChildren())
383 return false;
384 if (const char *Name = Die.getName(DINameKind::ShortName))
386 return false;
387 };
388 auto MaybeAddCovFuncName = [&](DWARFDie Die) {
389 if (!IsDIEOfCovName(Die))
390 return;
391 for (const DWARFDie &Child : Die.children()) {
392 if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation)
393 continue;
394 auto AnnotationFormName = Child.find(dwarf::DW_AT_name);
395 auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value);
396 if (!AnnotationFormName || !AnnotationFormValue)
397 continue;
398 auto AnnotationNameOrErr = AnnotationFormName->getAsCString();
399 if (auto Err = AnnotationNameOrErr.takeError()) {
400 consumeError(std::move(Err));
401 continue;
402 }
403 std::optional<const char *> FunctionName;
404 StringRef AnnotationName = *AnnotationNameOrErr;
405 if (AnnotationName.compare(
407 if (auto EC =
408 AnnotationFormValue->getAsCString().moveInto(FunctionName))
409 consumeError(std::move(EC));
410 }
411 if (!FunctionName) {
412 if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
414 "Missing coverage function name value at DIE 0x%08" PRIx64,
415 Child.getOffset());
416 }
417 return;
418 }
419 UnusedFuncNames.push_back(*FunctionName);
420 }
421 };
422 for (auto &CU : DICtx->normal_units())
423 for (const auto &Entry : CU->dies())
424 MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry));
425 for (auto &CU : DICtx->dwo_units())
426 for (const auto &Entry : CU->dies())
427 MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry));
428
429 if (!UnlimitedWarnings && NumSuppressedWarnings > 0)
430 WithColor::warning() << format("Suppressed %d additional warnings\n",
431 NumSuppressedWarnings);
432 if (!UnusedFuncNames.empty()) {
434 UnusedFuncNames, /*doCompression=*/false, this->CovUnusedFuncNames);
435 return Result;
436 }
437 return Error::success();
438}
#define LLVM_DEBUG(X)
Definition: Debug.h:101
std::string Name
Expected< object::SectionRef > getCountersSection(const object::ObjectFile &Obj)
Get the __llvm_prf_cnts section.
#define P(N)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static MemoryLocation getLocation(Instruction *I)
raw_pwrite_stream & OS
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
Utility class that carries the DWARF compile/type unit and the debug info entry in an object.
Definition: DWARFDie.h:42
iterator_range< iterator > children() const
Definition: DWARFDie.h:395
DWARFDie getParent() const
Get the parent of this DIE object.
Definition: DWARFDie.cpp:622
DWARFUnit * getDwarfUnit() const
Definition: DWARFDie.h:53
bool hasChildren() const
Definition: DWARFDie.h:78
const char * getName(DINameKind Kind) const
Return the DIE name resolving DW_AT_specification or DW_AT_abstract_origin references if necessary.
Definition: DWARFDie.cpp:442
dwarf::Tag getTag() const
Definition: DWARFDie.h:71
Expected< DWARFLocationExpressionsVector > getLocations(dwarf::Attribute Attr) const
Definition: DWARFDie.cpp:406
bool isNULL() const
Returns true for a valid DIE that terminates a sibling chain.
Definition: DWARFDie.h:84
bool isValid() const
Definition: DWARFDie.h:50
void dump(raw_ostream &OS, unsigned indent=0, DIDumpOptions DumpOpts=DIDumpOptions()) const
Dump the DIE and all of its attributes to the supplied stream.
Definition: DWARFDie.cpp:562
This class represents an Operation in the Expression.
uint64_t getRawOperand(unsigned Idx) const
uint8_t getAddressByteSize() const
Definition: DWARFUnit.h:324
DwarfInstrProfCorrelator - A child of InstrProfCorrelatorImpl that takes DWARF debug info as input to...
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
InstrProfCorrelatorImpl - A child of InstrProfCorrelator with a template pointer type so that the Pro...
Error correlateProfileData(int MaxWarnings) override
Construct a ProfileData vector used to correlate raw instrumentation data to their functions.
static bool classof(const InstrProfCorrelator *C)
InstrProfCorrelatorImpl(std::unique_ptr< InstrProfCorrelator::Context > Ctx)
void addProbe(StringRef FunctionName, uint64_t CFGHash, IntPtrT CounterOffset, IntPtrT FunctionPtr, uint32_t NumCounters)
Error dumpYaml(int MaxWarnings, raw_ostream &OS) override
Process debug info and dump the correlation data.
static llvm::Expected< std::unique_ptr< InstrProfCorrelatorImpl< IntPtrT > > > get(std::unique_ptr< InstrProfCorrelator::Context > Ctx, const object::ObjectFile &Obj)
InstrProfCorrelator - A base class used to create raw instrumentation data to their functions.
static const char * FunctionNameAttributeName
static const char * CFGHashAttributeName
static llvm::Expected< std::unique_ptr< InstrProfCorrelator > > get(StringRef DebugInfoFilename)
static const char * CovFunctionNameAttributeName
static const char * NumCountersAttributeName
std::optional< size_t > getDataSize() const
Return the number of ProfileData elements.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:222
iterator begin() const
Definition: StringRef.h:111
bool startswith(StringRef Prefix) const
Definition: StringRef.h:261
int compare(StringRef RHS) const
compare - Compare two strings; the result is negative, zero, or positive if this string is lexicograp...
Definition: StringRef.h:177
static raw_ostream & warning()
Convenience method for printing "warning: " to stderr.
Definition: WithColor.cpp:85
bool isLittleEndian() const
Definition: Binary.h:155
bool isMachO() const
Definition: Binary.h:127
bool isELF() const
Definition: Binary.h:123
static Expected< std::vector< std::string > > findDsymObjectMembers(StringRef Path)
If the input path is a .dSYM bundle (as created by the dsymutil tool), return the paths to the object...
This class is the base class for all object file types.
Definition: ObjectFile.h:229
section_iterator_range sections() const
Definition: ObjectFile.h:328
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
uint64_t ComputeHash(StringRef K)
Definition: InstrProf.h:1025
std::optional< uint64_t > toAddress(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract an address.
Expected< std::unique_ptr< Binary > > createBinary(MemoryBufferRef Source, LLVMContext *Context=nullptr, bool InitContent=true)
Create a Binary from Source, autodetecting the file type.
Definition: Binary.cpp:45
static const bool IsLittleEndianHost
Definition: SwapByteOrder.h:57
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition: Casting.h:649
StringRef getCoverageUnusedNamesVarName()
Return the name of the internal variable recording the array of PGO name vars referenced by the cover...
Definition: InstrProf.h:122
StringRef getInstrProfCountersVarPrefix()
Return the name prefix of profile counter variables.
Definition: InstrProf.h:97
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:125
Expected< T > errorOrToExpected(ErrorOr< T > &&EO)
Convert an ErrorOr<T> to an Expected<T>.
Definition: Error.h:1184
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1854
Error collectPGOFuncNameStrings(ArrayRef< std::string > NameStrs, bool doCompression, std::string &Result)
Given a vector of strings (function PGO names) NameStrs, the method generates a combined string Resul...
Definition: InstrProf.cpp:497
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1041
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
static llvm::Expected< std::unique_ptr< Context > > get(std::unique_ptr< MemoryBuffer > Buffer, const object::ObjectFile &Obj)
std::unique_ptr< MemoryBuffer > Buffer
static void mapping(yaml::IO &io, InstrProfCorrelator::CorrelationData &Data)
static void mapping(yaml::IO &io, InstrProfCorrelator::Probe &P)