diff --git a/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.aggperfscript b/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.aggperfscript new file mode 100644 index 0000000000000..9dbd2725c8e4d --- /dev/null +++ b/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.aggperfscript @@ -0,0 +1,11 @@ +2 + aabb1122:4005dc + aabb1122:400634 + aabb1122:400684 + 7f68c5788793 + aabb1122:0x4005c8/aabb1122:0x4005dc aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005c8/aabb1122:0x4005dc +2 + aabb1122:4005b0 + aabb1122:400684 + 7f68c5788793 + aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005c8/aabb1122:0x4005dc aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 diff --git a/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.perfscript b/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.perfscript new file mode 100644 index 0000000000000..fdc5c721b91a6 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/Inputs/buildid-cs-noprobe.perfscript @@ -0,0 +1,9 @@ + aabb1122:4005dc + aabb1122:400634 + aabb1122:400684 + 7f68c5788793 + aabb1122:0x4005c8/aabb1122:0x4005dc aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005c8/aabb1122:0x4005dc + aabb1122:4005b0 + aabb1122:400684 + 7f68c5788793 + aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005c8/aabb1122:0x4005dc aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 aabb1122:0x400645/aabb1122:0x4005ff aabb1122:0x400637/aabb1122:0x400645 aabb1122:0x4005e9/aabb1122:0x400634 aabb1122:0x4005d7/aabb1122:0x4005e5 aabb1122:0x40062f/aabb1122:0x4005b0 diff --git a/llvm/test/tools/llvm-profgen/filter-build-id.test b/llvm/test/tools/llvm-profgen/filter-build-id.test new file mode 100644 index 0000000000000..498d3b8f7ba4d --- /dev/null +++ b/llvm/test/tools/llvm-profgen/filter-build-id.test @@ -0,0 +1,57 @@ +; REQUIRES: x86_64-linux +; Test that [buildid:]0xaddr format is correctly parsed in hybrid perfscript +; input. Both callstack frames and LBR entries may carry buildid prefixes. + +;; Test 1: Hybrid perfscript with buildid prefix on callstack frames and +;; LBR entries, using --filter-build-id to match "aabb1122". +; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/buildid-cs-noprobe.aggperfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --skip-symbolization --profile-summary-cold-count=0 --filter-build-id=aabb1122 +; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-HYBRID + +; CHECK-HYBRID: [foo] +; CHECK-HYBRID-NEXT: 3 +; CHECK-HYBRID-NEXT: 5ff-62f:6 +; CHECK-HYBRID-NEXT: 634-637:6 +; CHECK-HYBRID-NEXT: 645-645:6 +; CHECK-HYBRID-NEXT: 3 +; CHECK-HYBRID-NEXT: 62f->5b0:6 +; CHECK-HYBRID-NEXT: 637->645:6 +; CHECK-HYBRID-NEXT: 645->5ff:6 +; CHECK-HYBRID-NEXT: [foo:3 @ bar] +; CHECK-HYBRID-NEXT: 4 +; CHECK-HYBRID-NEXT: 5b0-5c8:2 +; CHECK-HYBRID-NEXT: 5b0-5d7:4 +; CHECK-HYBRID-NEXT: 5dc-5e9:2 +; CHECK-HYBRID-NEXT: 5e5-5e9:4 +; CHECK-HYBRID-NEXT: 3 +; CHECK-HYBRID-NEXT: 5c8->5dc:4 +; CHECK-HYBRID-NEXT: 5d7->5e5:4 +; CHECK-HYBRID-NEXT: 5e9->634:6 + +;; Test 2: Non-pre-aggregated perfscript with buildid prefix (no leading count). +; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/buildid-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t2 --skip-symbolization --profile-summary-cold-count=0 --filter-build-id=aabb1122 +; RUN: FileCheck %s --input-file %t2 --check-prefix=CHECK-NOAGG + +; CHECK-NOAGG: [foo] +; CHECK-NOAGG-NEXT: 3 +; CHECK-NOAGG-NEXT: 5ff-62f:3 +; CHECK-NOAGG-NEXT: 634-637:3 +; CHECK-NOAGG-NEXT: 645-645:3 +; CHECK-NOAGG-NEXT: 3 +; CHECK-NOAGG-NEXT: 62f->5b0:3 +; CHECK-NOAGG-NEXT: 637->645:3 +; CHECK-NOAGG-NEXT: 645->5ff:3 +; CHECK-NOAGG-NEXT: [foo:3 @ bar] +; CHECK-NOAGG-NEXT: 4 +; CHECK-NOAGG-NEXT: 5b0-5c8:1 +; CHECK-NOAGG-NEXT: 5b0-5d7:2 +; CHECK-NOAGG-NEXT: 5dc-5e9:1 +; CHECK-NOAGG-NEXT: 5e5-5e9:2 +; CHECK-NOAGG-NEXT: 3 +; CHECK-NOAGG-NEXT: 5c8->5dc:2 +; CHECK-NOAGG-NEXT: 5d7->5e5:2 +; CHECK-NOAGG-NEXT: 5e9->634:3 + +;; Test 3: With non-matching filter, callstack frames are filtered out, +;; resulting in no samples. +; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/buildid-cs-noprobe.aggperfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t3 --skip-symbolization --filter-build-id=ccdd3344 2>&1 | FileCheck %s --check-prefix=CHECK-NOMATCH +; CHECK-NOMATCH: warning: No samples in perf script! diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp index d61c70e69d6e9..75909f3083e03 100644 --- a/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/llvm/tools/llvm-profgen/PerfReader.cpp @@ -69,6 +69,13 @@ cl::opt TimeProfGen("time-profgen", cl::desc("Time llvm-profgen phases"), static const char *TimerGroupName = "profgen"; static const char *TimerGroupDesc = "llvm-profgen"; +static cl::opt FilterBuildID( + "filter-build-id", + cl::desc("Override auto-detected build ID for filtering perfscript " + "addresses in [buildid:]addr format. When set, only addresses " + "with a matching build ID prefix are kept."), + cl::cat(ProfGenCategory)); + namespace sampleprof { void VirtualUnwinder::unwindCall(UnwindState &State) { @@ -665,12 +672,37 @@ void HybridPerfReader::unwindSamples() { } /// Parse a hex address from \p Str. -static bool parseAddress(StringRef Str, uint64_t &Addr, bool HasPrefix) { +/// Parse an optional [buildid:] prefix into \p BuildID. +static bool parseAddress(StringRef Str, uint64_t &Addr, bool HasPrefix, + StringRef &BuildID) { + size_t ColonPos = Str.find(':'); + if (ColonPos != StringRef::npos) { + BuildID = Str.substr(0, ColonPos); + Str = Str.substr(ColonPos + 1); + } if (Str.consume_front("0x") != HasPrefix) return true; return Str.getAsInteger(16, Addr); } +/// Return the build ID to use for filtering perfscript addresses. +/// If --filter-build-id is specified, use it as an override (with a warning +/// if it doesn't match the binary's auto-detected build ID). +/// Otherwise, use the auto-detected value from the binary. +static StringRef getFilterBuildID(const ProfiledBinary *Binary) { + StringRef BinaryBuildID = Binary->getFilterBuildID(); + if (FilterBuildID.getNumOccurrences() == 0) + return BinaryBuildID; + static bool Warned = false; + if (!Warned && !BinaryBuildID.empty() && FilterBuildID != BinaryBuildID) { + WithColor::warning() << "--filter-build-id=" << FilterBuildID + << " does not match binary build ID " << BinaryBuildID + << "\n"; + Warned = true; + } + return FilterBuildID; +} + bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, SmallVectorImpl &LBRStack) { // The raw format of LBR stack is like: @@ -688,8 +720,9 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, // Skip the leading instruction pointer. size_t Index = 0; uint64_t LeadingAddr; + StringRef LeadingBuildID; if (!Records.empty() && !Records[0].contains('/')) { - if (parseAddress(Records[0], LeadingAddr, false)) { + if (parseAddress(Records[0], LeadingAddr, false, LeadingBuildID)) { WarnInvalidLBR(TraceIt); TraceIt.advance(); return false; @@ -709,10 +742,12 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, Token.split(Addresses, "/"); uint64_t Src; uint64_t Dst; + StringRef SrcBuildID, DstBuildID; // Stop at broken LBR records. - if (Addresses.size() < 2 || parseAddress(Addresses[0], Src, true) || - parseAddress(Addresses[1], Dst, true)) { + if (Addresses.size() < 2 || + parseAddress(Addresses[0], Src, true, SrcBuildID) || + parseAddress(Addresses[1], Dst, true, DstBuildID)) { WarnInvalidLBR(TraceIt); break; } @@ -720,8 +755,12 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, // Canonicalize to use preferred load address as base address. Src = Binary->canonicalizeVirtualAddress(Src); Dst = Binary->canonicalizeVirtualAddress(Dst); - bool SrcIsInternal = Binary->addressIsCode(Src); - bool DstIsInternal = Binary->addressIsCode(Dst); + // Filter by build ID. + StringRef BinaryBuildID = getFilterBuildID(Binary); + bool SrcIsInternal = + SrcBuildID == BinaryBuildID && Binary->addressIsCode(Src); + bool DstIsInternal = + DstBuildID == BinaryBuildID && Binary->addressIsCode(Dst); if (!SrcIsInternal) Src = ExternalAddr; if (!DstIsInternal) @@ -739,16 +778,17 @@ bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, bool PerfScriptReader::extractCallstack(TraceStream &TraceIt, SmallVectorImpl &CallStack) { // The raw format of call stack is like: - // 4005dc # leaf frame + // 4005dc # leaf frame (no buildid) // 400634 - // 400684 # root frame + // deadbeef:400684 # root frame (with buildid prefix) // It's in bottom-up order with each frame in one line. // Extract stack frames from sample while (!TraceIt.isAtEoF() && !isLBRSample(TraceIt.getCurrentLine(), true)) { StringRef FrameStr = TraceIt.getCurrentLine().ltrim(); uint64_t FrameAddr = 0; - if (parseAddress(FrameStr, FrameAddr, false)) { + StringRef FrameBuildID; + if (parseAddress(FrameStr, FrameAddr, false, FrameBuildID)) { // We might parse a non-perf sample line like empty line and comments, // skip it TraceIt.advance(); @@ -758,7 +798,9 @@ bool PerfScriptReader::extractCallstack(TraceStream &TraceIt, FrameAddr = Binary->canonicalizeVirtualAddress(FrameAddr); // Currently intermixed frame from different binaries is not supported. - if (!Binary->addressIsCode(FrameAddr)) { + bool IsExternal = FrameBuildID != getFilterBuildID(Binary) || + !Binary->addressIsCode(FrameAddr); + if (IsExternal) { if (CallStack.empty()) NumLeafExternalFrame++; // Push a special value(ExternalAddr) for the external frames so that @@ -1171,7 +1213,7 @@ void PerfScriptReader::parseAndAggregateTrace() { // A LBR sample is like: // 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ... // A heuristic for fast detection by checking whether a -// leading " 0x" and the '/' exist. +// leading " 0x" or " buildid:0x" and the '/' exist. bool PerfScriptReader::isLBRSample(StringRef Line, bool CheckLineStart) { // Skip the leading instruction pointer SmallVector Records; @@ -1180,7 +1222,8 @@ bool PerfScriptReader::isLBRSample(StringRef Line, bool CheckLineStart) { Line.split(Records, " ", 2, CheckLineStart); if (Records.size() < 2) return false; - if (Records[1].starts_with("0x") && Records[1].contains('/')) + if ((Records[1].starts_with("0x") || Records[1].contains(":0x")) && + Records[1].contains('/')) return true; return false; } @@ -1218,8 +1261,10 @@ PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) { // Detect sample with call stack int32_t Count = 0; + StringRef FrameBuildId; while (!TraceIt.isAtEoF() && - !parseAddress(TraceIt.getCurrentLine().ltrim(), FrameAddr, false)) { + !parseAddress(TraceIt.getCurrentLine().ltrim(), FrameAddr, false, + FrameBuildId)) { Count++; TraceIt.advance(); }