214 lines
6.9 KiB
C++
214 lines
6.9 KiB
C++
|
//===- lib/MC/MCPseudoProbe.cpp - Pseudo probe encoding support ----------===//
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "llvm/MC/MCPseudoProbe.h"
|
||
|
#include "llvm/MC/MCAsmInfo.h"
|
||
|
#include "llvm/MC/MCContext.h"
|
||
|
#include "llvm/MC/MCObjectFileInfo.h"
|
||
|
#include "llvm/MC/MCObjectStreamer.h"
|
||
|
#include "llvm/MC/MCStreamer.h"
|
||
|
|
||
|
#define DEBUG_TYPE "mcpseudoprobe"
|
||
|
|
||
|
using namespace llvm;
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
int MCPseudoProbeTable::DdgPrintIndent = 0;
|
||
|
#endif
|
||
|
|
||
|
static const MCExpr *buildSymbolDiff(MCObjectStreamer *MCOS, const MCSymbol *A,
|
||
|
const MCSymbol *B) {
|
||
|
MCContext &Context = MCOS->getContext();
|
||
|
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
|
||
|
const MCExpr *ARef = MCSymbolRefExpr::create(A, Variant, Context);
|
||
|
const MCExpr *BRef = MCSymbolRefExpr::create(B, Variant, Context);
|
||
|
const MCExpr *AddrDelta =
|
||
|
MCBinaryExpr::create(MCBinaryExpr::Sub, ARef, BRef, Context);
|
||
|
return AddrDelta;
|
||
|
}
|
||
|
|
||
|
void MCPseudoProbe::emit(MCObjectStreamer *MCOS,
|
||
|
const MCPseudoProbe *LastProbe) const {
|
||
|
// Emit Index
|
||
|
MCOS->emitULEB128IntValue(Index);
|
||
|
// Emit Type and the flag:
|
||
|
// Type (bit 0 to 3), with bit 4 to 6 for attributes.
|
||
|
// Flag (bit 7, 0 - code address, 1 - address delta). This indicates whether
|
||
|
// the following field is a symbolic code address or an address delta.
|
||
|
assert(Type <= 0xF && "Probe type too big to encode, exceeding 15");
|
||
|
assert(Attributes <= 0x7 &&
|
||
|
"Probe attributes too big to encode, exceeding 7");
|
||
|
uint8_t PackedType = Type | (Attributes << 4);
|
||
|
uint8_t Flag = LastProbe ? ((int8_t)MCPseudoProbeFlag::AddressDelta << 7) : 0;
|
||
|
MCOS->emitInt8(Flag | PackedType);
|
||
|
|
||
|
if (LastProbe) {
|
||
|
// Emit the delta between the address label and LastProbe.
|
||
|
const MCExpr *AddrDelta =
|
||
|
buildSymbolDiff(MCOS, Label, LastProbe->getLabel());
|
||
|
int64_t Delta;
|
||
|
if (AddrDelta->evaluateAsAbsolute(Delta, MCOS->getAssemblerPtr())) {
|
||
|
MCOS->emitSLEB128IntValue(Delta);
|
||
|
} else {
|
||
|
MCOS->insert(new MCPseudoProbeAddrFragment(AddrDelta));
|
||
|
}
|
||
|
} else {
|
||
|
// Emit label as a symbolic code address.
|
||
|
MCOS->emitSymbolValue(
|
||
|
Label, MCOS->getContext().getAsmInfo()->getCodePointerSize());
|
||
|
}
|
||
|
|
||
|
LLVM_DEBUG({
|
||
|
dbgs().indent(MCPseudoProbeTable::DdgPrintIndent);
|
||
|
dbgs() << "Probe: " << Index << "\n";
|
||
|
});
|
||
|
}
|
||
|
|
||
|
MCPseudoProbeInlineTree::~MCPseudoProbeInlineTree() {
|
||
|
for (auto &Inlinee : Inlinees)
|
||
|
delete Inlinee.second;
|
||
|
}
|
||
|
|
||
|
MCPseudoProbeInlineTree *
|
||
|
MCPseudoProbeInlineTree::getOrAddNode(InlineSite Site) {
|
||
|
auto Iter = Inlinees.find(Site);
|
||
|
if (Iter == Inlinees.end()) {
|
||
|
auto *Node = new MCPseudoProbeInlineTree(std::get<0>(Site));
|
||
|
Inlinees[Site] = Node;
|
||
|
return Node;
|
||
|
} else {
|
||
|
return Iter->second;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MCPseudoProbeInlineTree::addPseudoProbe(
|
||
|
const MCPseudoProbe &Probe, const MCPseudoProbeInlineStack &InlineStack) {
|
||
|
// The function should not be called on the root.
|
||
|
assert(isRoot() && "Should not be called on root");
|
||
|
|
||
|
// When it comes here, the input look like:
|
||
|
// Probe: GUID of C, ...
|
||
|
// InlineStack: [88, A], [66, B]
|
||
|
// which means, Function A inlines function B at call site with a probe id of
|
||
|
// 88, and B inlines C at probe 66. The tri-tree expects a tree path like {[0,
|
||
|
// A], [88, B], [66, C]} to locate the tree node where the probe should be
|
||
|
// added. Note that the edge [0, A] means A is the top-level function we are
|
||
|
// emitting probes for.
|
||
|
|
||
|
// Make a [0, A] edge.
|
||
|
// An empty inline stack means the function that the probe originates from
|
||
|
// is a top-level function.
|
||
|
InlineSite Top;
|
||
|
if (InlineStack.empty()) {
|
||
|
Top = InlineSite(Probe.getGuid(), 0);
|
||
|
} else {
|
||
|
Top = InlineSite(std::get<0>(InlineStack.front()), 0);
|
||
|
}
|
||
|
|
||
|
auto *Cur = getOrAddNode(Top);
|
||
|
|
||
|
// Make interior edges by walking the inline stack. Once it's done, Cur should
|
||
|
// point to the node that the probe originates from.
|
||
|
if (!InlineStack.empty()) {
|
||
|
auto Iter = InlineStack.begin();
|
||
|
auto Index = std::get<1>(*Iter);
|
||
|
Iter++;
|
||
|
for (; Iter != InlineStack.end(); Iter++) {
|
||
|
// Make an edge by using the previous probe id and current GUID.
|
||
|
Cur = Cur->getOrAddNode(InlineSite(std::get<0>(*Iter), Index));
|
||
|
Index = std::get<1>(*Iter);
|
||
|
}
|
||
|
Cur = Cur->getOrAddNode(InlineSite(Probe.getGuid(), Index));
|
||
|
}
|
||
|
|
||
|
Cur->Probes.push_back(Probe);
|
||
|
}
|
||
|
|
||
|
void MCPseudoProbeInlineTree::emit(MCObjectStreamer *MCOS,
|
||
|
const MCPseudoProbe *&LastProbe) {
|
||
|
LLVM_DEBUG({
|
||
|
dbgs().indent(MCPseudoProbeTable::DdgPrintIndent);
|
||
|
dbgs() << "Group [\n";
|
||
|
MCPseudoProbeTable::DdgPrintIndent += 2;
|
||
|
});
|
||
|
// Emit probes grouped by GUID.
|
||
|
if (Guid != 0) {
|
||
|
LLVM_DEBUG({
|
||
|
dbgs().indent(MCPseudoProbeTable::DdgPrintIndent);
|
||
|
dbgs() << "GUID: " << Guid << "\n";
|
||
|
});
|
||
|
// Emit Guid
|
||
|
MCOS->emitInt64(Guid);
|
||
|
// Emit number of probes in this node
|
||
|
MCOS->emitULEB128IntValue(Probes.size());
|
||
|
// Emit number of direct inlinees
|
||
|
MCOS->emitULEB128IntValue(Inlinees.size());
|
||
|
// Emit probes in this group
|
||
|
for (const auto &Probe : Probes) {
|
||
|
Probe.emit(MCOS, LastProbe);
|
||
|
LastProbe = &Probe;
|
||
|
}
|
||
|
} else {
|
||
|
assert(Probes.empty() && "Root should not have probes");
|
||
|
}
|
||
|
|
||
|
// Emit descendent
|
||
|
for (const auto &Inlinee : Inlinees) {
|
||
|
if (Guid) {
|
||
|
// Emit probe index
|
||
|
MCOS->emitULEB128IntValue(std::get<1>(Inlinee.first));
|
||
|
LLVM_DEBUG({
|
||
|
dbgs().indent(MCPseudoProbeTable::DdgPrintIndent);
|
||
|
dbgs() << "InlineSite: " << std::get<1>(Inlinee.first) << "\n";
|
||
|
});
|
||
|
}
|
||
|
// Emit the group
|
||
|
Inlinee.second->emit(MCOS, LastProbe);
|
||
|
}
|
||
|
|
||
|
LLVM_DEBUG({
|
||
|
MCPseudoProbeTable::DdgPrintIndent -= 2;
|
||
|
dbgs().indent(MCPseudoProbeTable::DdgPrintIndent);
|
||
|
dbgs() << "]\n";
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void MCPseudoProbeSection::emit(MCObjectStreamer *MCOS) {
|
||
|
MCContext &Ctx = MCOS->getContext();
|
||
|
|
||
|
for (auto &ProbeSec : MCProbeDivisions) {
|
||
|
const MCPseudoProbe *LastProbe = nullptr;
|
||
|
if (auto *S =
|
||
|
Ctx.getObjectFileInfo()->getPseudoProbeSection(ProbeSec.first)) {
|
||
|
// Switch to the .pseudoprobe section or a comdat group.
|
||
|
MCOS->SwitchSection(S);
|
||
|
// Emit probes grouped by GUID.
|
||
|
ProbeSec.second.emit(MCOS, LastProbe);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This emits the pseudo probe tables.
|
||
|
//
|
||
|
void MCPseudoProbeTable::emit(MCObjectStreamer *MCOS) {
|
||
|
MCContext &Ctx = MCOS->getContext();
|
||
|
auto &ProbeTable = Ctx.getMCPseudoProbeTable();
|
||
|
|
||
|
// Bail out early so we don't switch to the pseudo_probe section needlessly
|
||
|
// and in doing so create an unnecessary (if empty) section.
|
||
|
auto &ProbeSections = ProbeTable.getProbeSections();
|
||
|
if (ProbeSections.empty())
|
||
|
return;
|
||
|
|
||
|
LLVM_DEBUG(MCPseudoProbeTable::DdgPrintIndent = 0);
|
||
|
|
||
|
// Put out the probe.
|
||
|
ProbeSections.emit(MCOS);
|
||
|
}
|