| //===-- ProfileGenerator.cpp - Profile Generator ---------------*- C++ -*-===// |
| // |
| // 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 "ProfileGenerator.h" |
| |
| static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), |
| cl::Required, |
| cl::desc("Output profile file")); |
| static cl::alias OutputA("o", cl::desc("Alias for --output"), |
| cl::aliasopt(OutputFilename)); |
| |
| static cl::opt<SampleProfileFormat> OutputFormat( |
| "format", cl::desc("Format of output profile"), cl::init(SPF_Text), |
| cl::values( |
| clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"), |
| clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"), |
| clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"), |
| clEnumValN(SPF_Text, "text", "Text encoding"), |
| clEnumValN(SPF_GCC, "gcc", |
| "GCC encoding (only meaningful for -sample)"))); |
| |
| static cl::opt<int32_t, true> RecursionCompression( |
| "compress-recursion", |
| cl::desc("Compressing recursion by deduplicating adjacent frame " |
| "sequences up to the specified size. -1 means no size limit."), |
| cl::Hidden, |
| cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize)); |
| |
| static cl::opt<uint64_t> CSProfColdThres( |
| "csprof-cold-thres", cl::init(100), cl::ZeroOrMore, |
| cl::desc("Specify the total samples threshold for a context profile to " |
| "be considered cold, any cold profiles will be merged into " |
| "context-less base profiles")); |
| |
| static cl::opt<bool> CSProfKeepCold( |
| "csprof-keep-cold", cl::init(false), cl::ZeroOrMore, |
| cl::desc("This works together with --csprof-cold-thres. If the total count " |
| "of the profile after all merge is done is still smaller than the " |
| "csprof-cold-thres, it will be trimmed unless csprof-keep-cold " |
| "flag is specified.")); |
| |
| using namespace llvm; |
| using namespace sampleprof; |
| |
| namespace llvm { |
| namespace sampleprof { |
| |
| // Initialize the MaxCompressionSize to -1 which means no size limit |
| int32_t CSProfileGenerator::MaxCompressionSize = -1; |
| |
| static bool |
| usePseudoProbes(const BinarySampleCounterMap &BinarySampleCounters) { |
| return BinarySampleCounters.size() && |
| BinarySampleCounters.begin()->first->usePseudoProbes(); |
| } |
| |
| std::unique_ptr<ProfileGenerator> |
| ProfileGenerator::create(const BinarySampleCounterMap &BinarySampleCounters, |
| enum PerfScriptType SampleType) { |
| std::unique_ptr<ProfileGenerator> ProfileGenerator; |
| if (SampleType == PERF_LBR_STACK) { |
| if (usePseudoProbes(BinarySampleCounters)) { |
| ProfileGenerator.reset( |
| new PseudoProbeCSProfileGenerator(BinarySampleCounters)); |
| } else { |
| ProfileGenerator.reset(new CSProfileGenerator(BinarySampleCounters)); |
| } |
| } else { |
| // TODO: |
| llvm_unreachable("Unsupported perfscript!"); |
| } |
| |
| return ProfileGenerator; |
| } |
| |
| void ProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, |
| StringMap<FunctionSamples> &ProfileMap) { |
| Writer->write(ProfileMap); |
| } |
| |
| void ProfileGenerator::write() { |
| auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); |
| if (std::error_code EC = WriterOrErr.getError()) |
| exitWithError(EC, OutputFilename); |
| write(std::move(WriterOrErr.get()), ProfileMap); |
| } |
| |
| void ProfileGenerator::findDisjointRanges(RangeSample &DisjointRanges, |
| const RangeSample &Ranges) { |
| |
| /* |
| Regions may overlap with each other. Using the boundary info, find all |
| disjoint ranges and their sample count. BoundaryPoint contains the count |
| multiple samples begin/end at this points. |
| |
| |<--100-->| Sample1 |
| |<------200------>| Sample2 |
| A B C |
| |
| In the example above, |
| Sample1 begins at A, ends at B, its value is 100. |
| Sample2 beings at A, ends at C, its value is 200. |
| For A, BeginCount is the sum of sample begins at A, which is 300 and no |
| samples ends at A, so EndCount is 0. |
| Then boundary points A, B, and C with begin/end counts are: |
| A: (300, 0) |
| B: (0, 100) |
| C: (0, 200) |
| */ |
| struct BoundaryPoint { |
| // Sum of sample counts beginning at this point |
| uint64_t BeginCount; |
| // Sum of sample counts ending at this point |
| uint64_t EndCount; |
| |
| BoundaryPoint() : BeginCount(0), EndCount(0){}; |
| |
| void addBeginCount(uint64_t Count) { BeginCount += Count; } |
| |
| void addEndCount(uint64_t Count) { EndCount += Count; } |
| }; |
| |
| /* |
| For the above example. With boundary points, follwing logic finds two |
| disjoint region of |
| |
| [A,B]: 300 |
| [B+1,C]: 200 |
| |
| If there is a boundary point that both begin and end, the point itself |
| becomes a separate disjoint region. For example, if we have original |
| ranges of |
| |
| |<--- 100 --->| |
| |<--- 200 --->| |
| A B C |
| |
| there are three boundary points with their begin/end counts of |
| |
| A: (100, 0) |
| B: (200, 100) |
| C: (0, 200) |
| |
| the disjoint ranges would be |
| |
| [A, B-1]: 100 |
| [B, B]: 300 |
| [B+1, C]: 200. |
| */ |
| std::map<uint64_t, BoundaryPoint> Boundaries; |
| |
| for (auto Item : Ranges) { |
| uint64_t Begin = Item.first.first; |
| uint64_t End = Item.first.second; |
| uint64_t Count = Item.second; |
| if (Boundaries.find(Begin) == Boundaries.end()) |
| Boundaries[Begin] = BoundaryPoint(); |
| Boundaries[Begin].addBeginCount(Count); |
| |
| if (Boundaries.find(End) == Boundaries.end()) |
| Boundaries[End] = BoundaryPoint(); |
| Boundaries[End].addEndCount(Count); |
| } |
| |
| uint64_t BeginAddress = 0; |
| int Count = 0; |
| for (auto Item : Boundaries) { |
| uint64_t Address = Item.first; |
| BoundaryPoint &Point = Item.second; |
| if (Point.BeginCount) { |
| if (BeginAddress) |
| DisjointRanges[{BeginAddress, Address - 1}] = Count; |
| Count += Point.BeginCount; |
| BeginAddress = Address; |
| } |
| if (Point.EndCount) { |
| assert(BeginAddress && "First boundary point cannot be 'end' point"); |
| DisjointRanges[{BeginAddress, Address}] = Count; |
| Count -= Point.EndCount; |
| BeginAddress = Address + 1; |
| } |
| } |
| } |
| |
| FunctionSamples & |
| CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr) { |
| auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples()); |
| if (Ret.second) { |
| SampleContext FContext(Ret.first->first(), RawContext); |
| FunctionSamples &FProfile = Ret.first->second; |
| FProfile.setContext(FContext); |
| } |
| return Ret.first->second; |
| } |
| |
| void CSProfileGenerator::updateBodySamplesforFunctionProfile( |
| FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc, |
| uint64_t Count) { |
| // Filter out invalid negative(int type) lineOffset |
| if (LeafLoc.second.LineOffset & 0x80000000) |
| return; |
| // Use the maximum count of samples with same line location |
| ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt( |
| LeafLoc.second.LineOffset, LeafLoc.second.Discriminator); |
| uint64_t PreviousCount = R ? R.get() : 0; |
| if (PreviousCount < Count) { |
| FunctionProfile.addBodySamples(LeafLoc.second.LineOffset, |
| LeafLoc.second.Discriminator, |
| Count - PreviousCount); |
| } |
| } |
| |
| void CSProfileGenerator::populateFunctionBodySamples( |
| FunctionSamples &FunctionProfile, const RangeSample &RangeCounter, |
| ProfiledBinary *Binary) { |
| // Compute disjoint ranges first, so we can use MAX |
| // for calculating count for each location. |
| RangeSample Ranges; |
| findDisjointRanges(Ranges, RangeCounter); |
| for (auto Range : Ranges) { |
| uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); |
| uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); |
| uint64_t Count = Range.second; |
| // Disjoint ranges have introduce zero-filled gap that |
| // doesn't belong to current context, filter them out. |
| if (Count == 0) |
| continue; |
| |
| InstructionPointer IP(Binary, RangeBegin, true); |
| |
| // Disjoint ranges may have range in the middle of two instr, |
| // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range |
| // can be Addr1+1 to Addr2-1. We should ignore such range. |
| if (IP.Address > RangeEnd) |
| continue; |
| |
| while (IP.Address <= RangeEnd) { |
| uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); |
| auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); |
| if (LeafLoc.hasValue()) { |
| // Recording body sample for this specific context |
| updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count); |
| } |
| // Accumulate total sample count even it's a line with invalid debug info |
| FunctionProfile.addTotalSamples(Count); |
| // Move to next IP within the range |
| IP.advance(); |
| } |
| } |
| } |
| |
| void CSProfileGenerator::populateFunctionBoundarySamples( |
| StringRef ContextId, FunctionSamples &FunctionProfile, |
| const BranchSample &BranchCounters, ProfiledBinary *Binary) { |
| |
| for (auto Entry : BranchCounters) { |
| uint64_t SourceOffset = Entry.first.first; |
| uint64_t TargetOffset = Entry.first.second; |
| uint64_t Count = Entry.second; |
| // Get the callee name by branch target if it's a call branch |
| StringRef CalleeName = FunctionSamples::getCanonicalFnName( |
| Binary->getFuncFromStartOffset(TargetOffset)); |
| if (CalleeName.size() == 0) |
| continue; |
| |
| // Record called target sample and its count |
| auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); |
| if (!LeafLoc.hasValue()) |
| continue; |
| FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset, |
| LeafLoc->second.Discriminator, |
| CalleeName, Count); |
| |
| // Record head sample for called target(callee) |
| std::ostringstream OCalleeCtxStr; |
| if (ContextId.find(" @ ") != StringRef::npos) { |
| OCalleeCtxStr << ContextId.rsplit(" @ ").first.str(); |
| OCalleeCtxStr << " @ "; |
| } |
| OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str(); |
| |
| FunctionSamples &CalleeProfile = |
| getFunctionProfileForContext(OCalleeCtxStr.str()); |
| assert(Count != 0 && "Unexpected zero weight branch"); |
| CalleeProfile.addHeadSamples(Count); |
| } |
| } |
| |
| static FrameLocation getCallerContext(StringRef CalleeContext, |
| StringRef &CallerNameWithContext) { |
| StringRef CallerContext = CalleeContext.rsplit(" @ ").first; |
| CallerNameWithContext = CallerContext.rsplit(':').first; |
| auto ContextSplit = CallerContext.rsplit(" @ "); |
| StringRef CallerFrameStr = ContextSplit.second.size() == 0 |
| ? ContextSplit.first |
| : ContextSplit.second; |
| FrameLocation LeafFrameLoc = {"", {0, 0}}; |
| StringRef Funcname; |
| SampleContext::decodeContextString(CallerFrameStr, Funcname, |
| LeafFrameLoc.second); |
| LeafFrameLoc.first = Funcname.str(); |
| return LeafFrameLoc; |
| } |
| |
| void CSProfileGenerator::populateInferredFunctionSamples() { |
| for (const auto &Item : ProfileMap) { |
| const StringRef CalleeContext = Item.first(); |
| const FunctionSamples &CalleeProfile = Item.second; |
| |
| // If we already have head sample counts, we must have value profile |
| // for call sites added already. Skip to avoid double counting. |
| if (CalleeProfile.getHeadSamples()) |
| continue; |
| // If we don't have context, nothing to do for caller's call site. |
| // This could happen for entry point function. |
| if (CalleeContext.find(" @ ") == StringRef::npos) |
| continue; |
| |
| // Infer Caller's frame loc and context ID through string splitting |
| StringRef CallerContextId; |
| FrameLocation &&CallerLeafFrameLoc = |
| getCallerContext(CalleeContext, CallerContextId); |
| |
| // It's possible that we haven't seen any sample directly in the caller, |
| // in which case CallerProfile will not exist. But we can't modify |
| // ProfileMap while iterating it. |
| // TODO: created function profile for those callers too |
| if (ProfileMap.find(CallerContextId) == ProfileMap.end()) |
| continue; |
| FunctionSamples &CallerProfile = ProfileMap[CallerContextId]; |
| |
| // Since we don't have call count for inlined functions, we |
| // estimate it from inlinee's profile using entry body sample. |
| uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); |
| // If we don't have samples with location, use 1 to indicate live. |
| if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) |
| EstimatedCallCount = 1; |
| CallerProfile.addCalledTargetSamples( |
| CallerLeafFrameLoc.second.LineOffset, |
| CallerLeafFrameLoc.second.Discriminator, |
| CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount); |
| CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset, |
| CallerLeafFrameLoc.second.Discriminator, |
| EstimatedCallCount); |
| CallerProfile.addTotalSamples(EstimatedCallCount); |
| } |
| } |
| |
| void CSProfileGenerator::mergeAndTrimColdProfile( |
| StringMap<FunctionSamples> &ProfileMap) { |
| // Nothing to merge if sample threshold is zero |
| if (!CSProfColdThres) |
| return; |
| |
| // Filter the cold profiles from ProfileMap and move them into a tmp |
| // container |
| std::vector<std::pair<StringRef, const FunctionSamples *>> ToRemoveVec; |
| for (const auto &I : ProfileMap) { |
| const FunctionSamples &FunctionProfile = I.second; |
| if (FunctionProfile.getTotalSamples() >= CSProfColdThres) |
| continue; |
| ToRemoveVec.emplace_back(I.getKey(), &I.second); |
| } |
| |
| // Remove the code profile from ProfileMap and merge them into BaseProileMap |
| StringMap<FunctionSamples> BaseProfileMap; |
| for (const auto &I : ToRemoveVec) { |
| auto Ret = BaseProfileMap.try_emplace( |
| I.second->getContext().getNameWithoutContext(), FunctionSamples()); |
| FunctionSamples &BaseProfile = Ret.first->second; |
| BaseProfile.merge(*I.second); |
| ProfileMap.erase(I.first); |
| } |
| |
| // Merge the base profiles into ProfileMap; |
| for (const auto &I : BaseProfileMap) { |
| // Filter the cold base profile |
| if (!CSProfKeepCold && I.second.getTotalSamples() < CSProfColdThres && |
| ProfileMap.find(I.getKey()) == ProfileMap.end()) |
| continue; |
| // Merge the profile if the original profile exists, otherwise just insert |
| // as a new profile |
| FunctionSamples &OrigProfile = getFunctionProfileForContext(I.getKey()); |
| OrigProfile.merge(I.second); |
| } |
| } |
| |
| void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, |
| StringMap<FunctionSamples> &ProfileMap) { |
| mergeAndTrimColdProfile(ProfileMap); |
| // Add bracket for context key to support different profile binary format |
| StringMap<FunctionSamples> CxtWithBracketPMap; |
| for (const auto &Item : ProfileMap) { |
| std::string ContextWithBracket = "[" + Item.first().str() + "]"; |
| auto Ret = CxtWithBracketPMap.try_emplace(ContextWithBracket, Item.second); |
| assert(Ret.second && "Must be a unique context"); |
| SampleContext FContext(Ret.first->first(), RawContext); |
| FunctionSamples &FProfile = Ret.first->second; |
| FProfile.setName(FContext.getNameWithContext(true)); |
| FProfile.setContext(FContext); |
| } |
| Writer->write(CxtWithBracketPMap); |
| } |
| |
| // Helper function to extract context prefix string stack |
| // Extract context stack for reusing, leaf context stack will |
| // be added compressed while looking up function profile |
| static void |
| extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack, |
| const SmallVectorImpl<const PseudoProbe *> &Probes, |
| ProfiledBinary *Binary) { |
| for (const auto *P : Probes) { |
| Binary->getInlineContextForProbe(P, ContextStrStack, true); |
| } |
| } |
| |
| void PseudoProbeCSProfileGenerator::generateProfile() { |
| // Enable pseudo probe functionalities in SampleProf |
| FunctionSamples::ProfileIsProbeBased = true; |
| for (const auto &BI : BinarySampleCounters) { |
| ProfiledBinary *Binary = BI.first; |
| for (const auto &CI : BI.second) { |
| const ProbeBasedCtxKey *CtxKey = |
| dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); |
| SmallVector<std::string, 16> ContextStrStack; |
| extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary); |
| // Fill in function body samples from probes, also infer caller's samples |
| // from callee's probe |
| populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack, |
| Binary); |
| // Fill in boundary samples for a call probe |
| populateBoundarySamplesWithProbes(CI.second.BranchCounter, |
| ContextStrStack, Binary); |
| } |
| } |
| } |
| |
| void PseudoProbeCSProfileGenerator::extractProbesFromRange( |
| const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter, |
| ProfiledBinary *Binary) { |
| RangeSample Ranges; |
| findDisjointRanges(Ranges, RangeCounter); |
| for (const auto &Range : Ranges) { |
| uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); |
| uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); |
| uint64_t Count = Range.second; |
| // Disjoint ranges have introduce zero-filled gap that |
| // doesn't belong to current context, filter them out. |
| if (Count == 0) |
| continue; |
| |
| InstructionPointer IP(Binary, RangeBegin, true); |
| |
| // Disjoint ranges may have range in the middle of two instr, |
| // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range |
| // can be Addr1+1 to Addr2-1. We should ignore such range. |
| if (IP.Address > RangeEnd) |
| continue; |
| |
| while (IP.Address <= RangeEnd) { |
| const AddressProbesMap &Address2ProbesMap = |
| Binary->getAddress2ProbesMap(); |
| auto It = Address2ProbesMap.find(IP.Address); |
| if (It != Address2ProbesMap.end()) { |
| for (const auto &Probe : It->second) { |
| if (!Probe.isBlock()) |
| continue; |
| ProbeCounter[&Probe] += Count; |
| } |
| } |
| |
| IP.advance(); |
| } |
| } |
| } |
| |
| void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes( |
| const RangeSample &RangeCounter, |
| SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { |
| ProbeCounterMap ProbeCounter; |
| // Extract the top frame probes by looking up each address among the range in |
| // the Address2ProbeMap |
| extractProbesFromRange(RangeCounter, ProbeCounter, Binary); |
| for (auto PI : ProbeCounter) { |
| const PseudoProbe *Probe = PI.first; |
| uint64_t Count = PI.second; |
| FunctionSamples &FunctionProfile = |
| getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); |
| |
| FunctionProfile.addBodySamples(Probe->Index, 0, Count); |
| FunctionProfile.addTotalSamples(Count); |
| if (Probe->isEntry()) { |
| FunctionProfile.addHeadSamples(Count); |
| // Look up for the caller's function profile |
| const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); |
| if (InlinerDesc != nullptr) { |
| // Since the context id will be compressed, we have to use callee's |
| // context id to infer caller's context id to ensure they share the |
| // same context prefix. |
| StringRef CalleeContextId = |
| FunctionProfile.getContext().getNameWithContext(true); |
| StringRef CallerContextId; |
| FrameLocation &&CallerLeafFrameLoc = |
| getCallerContext(CalleeContextId, CallerContextId); |
| uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset; |
| assert(CallerIndex && |
| "Inferred caller's location index shouldn't be zero!"); |
| FunctionSamples &CallerProfile = |
| getFunctionProfileForContext(CallerContextId); |
| CallerProfile.setFunctionHash(InlinerDesc->FuncHash); |
| CallerProfile.addBodySamples(CallerIndex, 0, Count); |
| CallerProfile.addTotalSamples(Count); |
| CallerProfile.addCalledTargetSamples( |
| CallerIndex, 0, |
| FunctionProfile.getContext().getNameWithoutContext(), Count); |
| } |
| } |
| } |
| } |
| |
| void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes( |
| const BranchSample &BranchCounter, |
| SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { |
| for (auto BI : BranchCounter) { |
| uint64_t SourceOffset = BI.first.first; |
| uint64_t TargetOffset = BI.first.second; |
| uint64_t Count = BI.second; |
| uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); |
| const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress); |
| if (CallProbe == nullptr) |
| continue; |
| FunctionSamples &FunctionProfile = |
| getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary); |
| FunctionProfile.addBodySamples(CallProbe->Index, 0, Count); |
| FunctionProfile.addTotalSamples(Count); |
| StringRef CalleeName = FunctionSamples::getCanonicalFnName( |
| Binary->getFuncFromStartOffset(TargetOffset)); |
| if (CalleeName.size() == 0) |
| continue; |
| FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName, |
| Count); |
| } |
| } |
| |
| FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( |
| SmallVectorImpl<std::string> &ContextStrStack, |
| const PseudoProbeFuncDesc *LeafFuncDesc) { |
| assert(ContextStrStack.size() && "Profile context must have the leaf frame"); |
| // Compress the context string except for the leaf frame |
| std::string LeafFrame = ContextStrStack.back(); |
| ContextStrStack.pop_back(); |
| CSProfileGenerator::compressRecursionContext(ContextStrStack); |
| |
| std::ostringstream OContextStr; |
| for (uint32_t I = 0; I < ContextStrStack.size(); I++) { |
| if (OContextStr.str().size()) |
| OContextStr << " @ "; |
| OContextStr << ContextStrStack[I]; |
| } |
| // For leaf inlined context with the top frame, we should strip off the top |
| // frame's probe id, like: |
| // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" |
| if (OContextStr.str().size()) |
| OContextStr << " @ "; |
| OContextStr << StringRef(LeafFrame).split(":").first.str(); |
| |
| FunctionSamples &FunctionProile = |
| getFunctionProfileForContext(OContextStr.str()); |
| FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); |
| return FunctionProile; |
| } |
| |
| FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( |
| SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe, |
| ProfiledBinary *Binary) { |
| // Explicitly copy the context for appending the leaf context |
| SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), |
| ContextStrStack.end()); |
| Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy); |
| // Note that the context from probe doesn't include leaf frame, |
| // hence we need to retrieve and append the leaf frame. |
| const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); |
| ContextStrStackCopy.emplace_back(FuncDesc->FuncName + ":" + |
| Twine(LeafProbe->Index).str()); |
| return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc); |
| } |
| |
| } // end namespace sampleprof |
| } // end namespace llvm |