It seems to me that trace-pc-guard is simply a newer alternative to trace-pc. Whereas the trace-pc callback is called with no further information, the trace-pc-guard receives the guard value which uniquely identifies the current function / basic block / edge as reflected in their signatures and can be used to disable tracking of the respective caller (by setting *guard=0):
If you look at the source code for the instrumentation, you can see that the passing of the guard variable seems to be their only difference:
// llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
void ModuleSanitizerCoverage::InjectCoverageAtBlock(
Function &F,
BasicBlock &BB,
size_t Idx,
bool IsLeafFunc) {
/* ... */
if (Options.TracePC) {
IRB.CreateCall(SanCovTracePC)
->setCannotMerge(); // gets the PC using GET_CALLER_PC.
}
if (Options.TracePCGuard) {
auto GuardPtr = IRB.CreateIntToPtr(
IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy),
ConstantInt::get(IntptrTy, Idx * 4)),
Int32PtrTy);
IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
}
/* ... */
}
The commits that introduced the options also add a bit of explanation:
Unfortunately, I do not know why trace-pc is used for kernel fuzzing (as mentioned by the CoverageSanitizer documentation) instead of trace-pc-guard - maybe it's just for compatibility of 'legacy' code instrumentation?