[llvm] a5f411b - [llvm-profgen] Allow unsymbolized profile as perf input

via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 25 23:58:41 PDT 2021


Author: wlei
Date: 2021-10-25T23:58:08-07:00
New Revision: a5f411b7f88caf1ef000a0397eb93d1a39d25690

URL: https://github.com/llvm/llvm-project/commit/a5f411b7f88caf1ef000a0397eb93d1a39d25690
DIFF: https://github.com/llvm/llvm-project/commit/a5f411b7f88caf1ef000a0397eb93d1a39d25690.diff

LOG: [llvm-profgen] Allow unsymbolized profile as perf input

This change allows the unsymbolized profile as input. The unsymbolized profile is created by `llvm-profgen` with `--skip-symbolization` and it's after the sample aggregation but before symbolization , so it has much small file size. It can be used for sample merging and trimming,  also is useful for debugging or adding test cases. A switch `--unsymbolized-profile=file-patch` is added for this.

Format of unsymbolized profile:
```

   [context stack1]    # If it's a CS profile
      number of entries in RangeCounter
      from_1-to_1:count_1
      from_2-to_2:count_2
      ......
      from_n-to_n:count_n
      number of entries in BranchCounter
      src_1->dst_1:count_1
      src_2->dst_2:count_2
      ......
      src_n->dst_n:count_n
    [context stack2]
      ......
```

Reviewed By: hoy, wenlei

Differential Revision: https://reviews.llvm.org/D111750

Added: 
    

Modified: 
    llvm/include/llvm/ProfileData/SampleProf.h
    llvm/test/tools/llvm-profgen/inline-noprobe.test
    llvm/test/tools/llvm-profgen/inline-noprobe2.test
    llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test
    llvm/tools/llvm-profgen/PerfReader.cpp
    llvm/tools/llvm-profgen/PerfReader.h
    llvm/tools/llvm-profgen/llvm-profgen.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 3e0753ba14dd5..89faa835a15ea 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -495,28 +495,32 @@ class SampleContext {
       State = UnknownContext;
       Name = ContextStr;
     } else {
-      // Remove encapsulating '[' and ']' if any
-      ContextStr = ContextStr.substr(1, ContextStr.size() - 2);
       CSNameTable.emplace_back();
       SampleContextFrameVector &Context = CSNameTable.back();
-      /// Create a context vector from a given context string and save it in
-      /// `Context`.
-      StringRef ContextRemain = ContextStr;
-      StringRef ChildContext;
-      StringRef CalleeName;
-      while (!ContextRemain.empty()) {
-        auto ContextSplit = ContextRemain.split(" @ ");
-        ChildContext = ContextSplit.first;
-        ContextRemain = ContextSplit.second;
-        LineLocation CallSiteLoc(0, 0);
-        decodeContextString(ChildContext, CalleeName, CallSiteLoc);
-        Context.emplace_back(CalleeName, CallSiteLoc);
-      }
-
+      createCtxVectorFromStr(ContextStr, Context);
       setContext(Context, CState);
     }
   }
 
+  /// Create a context vector from a given context string and save it in
+  /// `Context`.
+  static void createCtxVectorFromStr(StringRef ContextStr,
+                                     SampleContextFrameVector &Context) {
+    // Remove encapsulating '[' and ']' if any
+    ContextStr = ContextStr.substr(1, ContextStr.size() - 2);
+    StringRef ContextRemain = ContextStr;
+    StringRef ChildContext;
+    StringRef CalleeName;
+    while (!ContextRemain.empty()) {
+      auto ContextSplit = ContextRemain.split(" @ ");
+      ChildContext = ContextSplit.first;
+      ContextRemain = ContextSplit.second;
+      LineLocation CallSiteLoc(0, 0);
+      decodeContextString(ChildContext, CalleeName, CallSiteLoc);
+      Context.emplace_back(CalleeName, CallSiteLoc);
+    }
+  }
+
   // Promote context by removing top frames with the length of
   // `ContextFramesToRemove`. Note that with array representation of context,
   // the promotion is effectively a slice operation with first

diff  --git a/llvm/test/tools/llvm-profgen/inline-noprobe.test b/llvm/test/tools/llvm-profgen/inline-noprobe.test
index 7d8a876f46da1..f7a61470f261a 100644
--- a/llvm/test/tools/llvm-profgen/inline-noprobe.test
+++ b/llvm/test/tools/llvm-profgen/inline-noprobe.test
@@ -2,8 +2,8 @@
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE
 ; RUN: llvm-profgen --format=text --use-dwarf-correlation --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t --skip-symbolization
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE
-; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t
-; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
+; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe.perfbin --output=%t1
+; RUN: FileCheck %s --input-file %t1 --check-prefix=CHECK
 ; RUN: llvm-profgen --format=text --use-dwarf-correlation --perfscript=%S/Inputs/inline-noprobe.perfscript --binary=%S/Inputs/inline-noprobe.perfbin --output=%t
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
 

diff  --git a/llvm/test/tools/llvm-profgen/inline-noprobe2.test b/llvm/test/tools/llvm-profgen/inline-noprobe2.test
index 147c509d3f3d0..3fcc1cb5541c2 100644
--- a/llvm/test/tools/llvm-profgen/inline-noprobe2.test
+++ b/llvm/test/tools/llvm-profgen/inline-noprobe2.test
@@ -2,8 +2,8 @@
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-ARTIFICIAL-BRANCH
 ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t  --skip-symbolization --use-offset=0
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-RAW-PROFILE
-; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t
-; RUN: FileCheck %s --input-file %t --check-prefix=CHECK
+; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t1 --use-offset=0
+; RUN: FileCheck %s --input-file %t1 --check-prefix=CHECK
 
 ; RUN: llvm-profgen --format=extbinary --perfscript=%S/Inputs/inline-noprobe2.perfscript --binary=%S/Inputs/inline-noprobe2.perfbin --output=%t
 ; RUN: llvm-profdata show -show-prof-sym-list -sample %t | FileCheck %s --check-prefix=CHECK-SYM-LIST

diff  --git a/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test b/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test
index 46d358f9a669f..9f4918741d855 100644
--- a/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test
+++ b/llvm/test/tools/llvm-profgen/noinline-cs-noprobe.test
@@ -2,8 +2,8 @@
 ; REQUIRES: x86_64-linux
 ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --skip-symbolization --profile-summary-cold-count=0
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-UNWINDER
-; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --profile-summary-cold-count=0
-; RUN: FileCheck %s --input-file %t
+; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t1 --profile-summary-cold-count=0
+; RUN: FileCheck %s --input-file %t1
 ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.perfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --profile-summary-cold-count=0 --ignore-stack-samples
 ; RUN: FileCheck %s --input-file %t --check-prefix=CHECK-STRIP-CTX
 ; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/noinline-cs-noprobe.aggperfscript --binary=%S/Inputs/noinline-cs-noprobe.perfbin --output=%t --skip-symbolization --profile-summary-cold-count=0

diff  --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 8224c085d6071..e36c707c4fcd8 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -24,7 +24,8 @@ static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::init(false),
 
 static cl::opt<bool>
     UseOffset("use-offset", cl::init(true), cl::ZeroOrMore,
-              cl::desc("Work with `--skip-symbolization` to dump the "
+              cl::desc("Work with `--skip-symbolization` or "
+                       "`--unsymbolized-profile` to write/read the "
                        "offset instead of virtual address."));
 static cl::opt<bool>
     IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
@@ -277,24 +278,29 @@ bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
   return true;
 }
 
-std::unique_ptr<PerfReaderBase> PerfReaderBase::create(ProfiledBinary *Binary,
-                                                       StringRef PerfInputFile,
-                                                       bool IsPerfData) {
-  // For perf data input, we need to convert them into perf script first.
-  if (IsPerfData) {
-    std::string ConvertedPerfScript =
-        convertPerfDataToTrace(Binary, PerfInputFile);
-    // Let commoand opt own the string for converted perf trace file name
-    PerfTraceFilename = ConvertedPerfScript;
-    PerfInputFile = PerfTraceFilename;
+std::unique_ptr<PerfReaderBase>
+PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput) {
+  std::unique_ptr<PerfReaderBase> PerfReader;
+
+  if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
+    PerfReader.reset(
+        new UnsymbolizedProfileReader(Binary, PerfInput.InputFile));
+    return PerfReader;
   }
 
-  PerfScriptType PerfType = checkPerfScriptType(PerfInputFile);
-  std::unique_ptr<PerfReaderBase> PerfReader;
-  if (PerfType == PERF_LBR_STACK) {
-    PerfReader.reset(new HybridPerfReader(Binary, PerfInputFile));
-  } else if (PerfType == PERF_LBR) {
-    PerfReader.reset(new LBRPerfReader(Binary, PerfInputFile));
+  // For perf data input, we need to convert them into perf script first.
+  if (PerfInput.Format == PerfFormat::PerfData)
+    PerfInput = PerfScriptReader::convertPerfDataToTrace(Binary, PerfInput);
+
+  assert((PerfInput.Format == PerfFormat::PerfScript) &&
+         "Should be a perfscript!");
+
+  PerfInput.Content =
+      PerfScriptReader::checkPerfScriptType(PerfInput.InputFile);
+  if (PerfInput.Content == PerfContent::LBRStack) {
+    PerfReader.reset(new HybridPerfReader(Binary, PerfInput.InputFile));
+  } else if (PerfInput.Content == PerfContent::LBR) {
+    PerfReader.reset(new LBRPerfReader(Binary, PerfInput.InputFile));
   } else {
     exitWithError("Unsupported perfscript!");
   }
@@ -302,8 +308,9 @@ std::unique_ptr<PerfReaderBase> PerfReaderBase::create(ProfiledBinary *Binary,
   return PerfReader;
 }
 
-std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary,
-                                                   StringRef PerfData) {
+PerfInputFile PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary,
+                                                       PerfInputFile &File) {
+  StringRef PerfData = File.InputFile;
   // Run perf script to retrieve PIDs matching binary we're interested in.
   auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
   if (!PerfExecutable) {
@@ -348,10 +355,10 @@ std::string PerfReaderBase::convertPerfDataToTrace(ProfiledBinary *Binary,
                                   PIDs,     "-i",         PerfData};
   sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects);
 
-  return PerfTraceFile;
+  return {PerfTraceFile, PerfFormat::PerfScript, PerfContent::UnknownContent};
 }
 
-void PerfReaderBase::updateBinaryAddress(const MMapEvent &Event) {
+void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
   // Drop the event which doesn't belong to user-provided binary
   StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath);
   if (Binary->getName() != BinaryName)
@@ -432,8 +439,8 @@ void HybridPerfReader::unwindSamples() {
                          << format("0x%" PRIx64, Address) << "\n";
 }
 
-bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
-                                     SmallVectorImpl<LBREntry> &LBRStack) {
+bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
+                                       SmallVectorImpl<LBREntry> &LBRStack) {
   // The raw format of LBR stack is like:
   // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
   //                           ... 0x4005c8/0x4005dc/P/-/-/0
@@ -551,8 +558,8 @@ bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
   return !LBRStack.empty();
 }
 
-bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
-                                      SmallVectorImpl<uint64_t> &CallStack) {
+bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
+                                        SmallVectorImpl<uint64_t> &CallStack) {
   // The raw format of call stack is like:
   //            4005dc      # leaf frame
   //	          400634
@@ -609,7 +616,7 @@ bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
          !Binary->addressInPrologEpilog(CallStack.front());
 }
 
-void PerfReaderBase::warnIfMissingMMap() {
+void PerfScriptReader::warnIfMissingMMap() {
   if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
     WithColor::warning() << "No relevant mmap event is matched for "
                          << Binary->getName()
@@ -663,43 +670,28 @@ void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
   }
 }
 
-void PerfReaderBase::writeRawProfile(StringRef Filename) {
+void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) {
   std::error_code EC;
   raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
   if (EC)
     exitWithError(EC, Filename);
-  writeRawProfile(OS);
+  writeUnsymbolizedProfile(OS);
 }
 
 // Use ordered map to make the output deterministic
 using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
 
-void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
-  /*
-     Format:
-     [context string]
-     number of entries in RangeCounter
-     from_1-to_1:count_1
-     from_2-to_2:count_2
-     ......
-     from_n-to_n:count_n
-     number of entries in BranchCounter
-     src_1->dst_1:count_1
-     src_2->dst_2:count_2
-     ......
-     src_n->dst_n:count_n
-  */
-
+void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) {
   OrderedCounterForPrint OrderedCounters;
   for (auto &CI : SampleCounters) {
     OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second;
   }
 
-  auto SCounterPrinter = [&](RangeSample Counter, StringRef Separator,
+  auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
                              uint32_t Indent) {
     OS.indent(Indent);
     OS << Counter.size() << "\n";
-    for (auto I : Counter) {
+    for (auto &I : Counter) {
       uint64_t Start = UseOffset ? I.first.first
                                  : Binary->offsetToVirtualAddr(I.first.first);
       uint64_t End = UseOffset ? I.first.second
@@ -712,7 +704,7 @@ void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
 
   for (auto &CI : OrderedCounters) {
     uint32_t Indent = 0;
-    if (!CI.first.empty()) {
+    if (ProfileIsCS) {
       // Context string key
       OS << "[" << CI.first << "]\n";
       Indent = 2;
@@ -724,8 +716,95 @@ void PerfReaderBase::writeRawProfile(raw_fd_ostream &OS) {
   }
 }
 
-void LBRPerfReader::computeCounterFromLBR(const PerfSample *Sample,
-                                          uint64_t Repeat) {
+// Format of input:
+// number of entries in RangeCounter
+// from_1-to_1:count_1
+// from_2-to_2:count_2
+// ......
+// from_n-to_n:count_n
+// number of entries in BranchCounter
+// src_1->dst_1:count_1
+// src_2->dst_2:count_2
+// ......
+// src_n->dst_n:count_n
+void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt,
+                                                   SampleCounter &SCounters) {
+  auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) {
+    std::string Msg = TraceIt.isAtEoF()
+                          ? "Invalid raw profile!"
+                          : "Invalid raw profile at line " +
+                                Twine(TraceIt.getLineNumber()).str() + ": " +
+                                TraceIt.getCurrentLine().str();
+    exitWithError(Msg);
+  };
+  auto ReadNumber = [&](uint64_t &Num) {
+    if (TraceIt.isAtEoF())
+      exitWithErrorForTraceLine(TraceIt);
+    if (TraceIt.getCurrentLine().ltrim().getAsInteger(10, Num))
+      exitWithErrorForTraceLine(TraceIt);
+    TraceIt.advance();
+  };
+
+  auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) {
+    uint64_t Num = 0;
+    ReadNumber(Num);
+    while (Num--) {
+      if (TraceIt.isAtEoF())
+        exitWithErrorForTraceLine(TraceIt);
+      StringRef Line = TraceIt.getCurrentLine().ltrim();
+
+      uint64_t Count = 0;
+      auto LineSplit = Line.split(":");
+      if (LineSplit.second.empty() || LineSplit.second.getAsInteger(10, Count))
+        exitWithErrorForTraceLine(TraceIt);
+
+      uint64_t Source = 0;
+      uint64_t Target = 0;
+      auto Range = LineSplit.first.split(Separator);
+      if (Range.second.empty() || Range.first.getAsInteger(16, Source) ||
+          Range.second.getAsInteger(16, Target))
+        exitWithErrorForTraceLine(TraceIt);
+
+      if (!UseOffset) {
+        Source = Binary->virtualAddrToOffset(Source);
+        Target = Binary->virtualAddrToOffset(Target);
+      }
+
+      Counter[{Source, Target}] += Count;
+      TraceIt.advance();
+    }
+  };
+
+  ReadCounter(SCounters.RangeCounter, "-");
+  ReadCounter(SCounters.BranchCounter, "->");
+}
+
+void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) {
+  TraceStream TraceIt(FileName);
+  while (!TraceIt.isAtEoF()) {
+    std::shared_ptr<StringBasedCtxKey> Key =
+        std::make_shared<StringBasedCtxKey>();
+    StringRef Line = TraceIt.getCurrentLine();
+    // Read context stack for CS profile.
+    if (Line.startswith("[")) {
+      ProfileIsCS = true;
+      auto I = ContextStrSet.insert(Line.str());
+      SampleContext::createCtxVectorFromStr(*I.first, Key->Context);
+      TraceIt.advance();
+    }
+    Key->genHashCode();
+    auto Ret =
+        SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter());
+    readSampleCounters(TraceIt, Ret.first->second);
+  }
+}
+
+void UnsymbolizedProfileReader::parsePerfTraces() {
+  readUnsymbolizedProfile(PerfTraceFile);
+}
+
+void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
+                                             uint64_t Repeat) {
   SampleCounter &Counter = SampleCounters.begin()->second;
   uint64_t EndOffeset = 0;
   for (const LBREntry &LBR : Sample->LBRStack) {
@@ -755,7 +834,7 @@ void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
   }
 }
 
-void LBRPerfReader::generateRawProfile() {
+void PerfScriptReader::generateUnsymbolizedProfile() {
   // There is no context for LBR only sample, so initialize one entry with
   // fake "empty" context key.
   assert(SampleCounters.empty() &&
@@ -770,7 +849,7 @@ void LBRPerfReader::generateRawProfile() {
   }
 }
 
-uint64_t PerfReaderBase::parseAggregatedCount(TraceStream &TraceIt) {
+uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) {
   // The aggregated count is optional, so do not skip the line and return 1 if
   // it's unmatched
   uint64_t Count = 1;
@@ -779,15 +858,15 @@ uint64_t PerfReaderBase::parseAggregatedCount(TraceStream &TraceIt) {
   return Count;
 }
 
-void PerfReaderBase::parseSample(TraceStream &TraceIt) {
+void PerfScriptReader::parseSample(TraceStream &TraceIt) {
   uint64_t Count = parseAggregatedCount(TraceIt);
   assert(Count >= 1 && "Aggregated count should be >= 1!");
   parseSample(TraceIt, Count);
 }
 
-bool PerfReaderBase::extractMMap2EventForBinary(ProfiledBinary *Binary,
-                                                StringRef Line,
-                                                MMapEvent &MMap) {
+bool PerfScriptReader::extractMMap2EventForBinary(ProfiledBinary *Binary,
+                                                  StringRef Line,
+                                                  MMapEvent &MMap) {
   // Parse a line like:
   //  PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
   //  08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
@@ -831,21 +910,21 @@ bool PerfReaderBase::extractMMap2EventForBinary(ProfiledBinary *Binary,
   return Binary->getName() == BinaryName;
 }
 
-void PerfReaderBase::parseMMap2Event(TraceStream &TraceIt) {
+void PerfScriptReader::parseMMap2Event(TraceStream &TraceIt) {
   MMapEvent MMap;
   if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap))
     updateBinaryAddress(MMap);
   TraceIt.advance();
 }
 
-void PerfReaderBase::parseEventOrSample(TraceStream &TraceIt) {
+void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
   if (isMMap2Event(TraceIt.getCurrentLine()))
     parseMMap2Event(TraceIt);
   else
     parseSample(TraceIt);
 }
 
-void PerfReaderBase::parseAndAggregateTrace() {
+void PerfScriptReader::parseAndAggregateTrace() {
   // Trace line iterator
   TraceStream TraceIt(PerfTraceFile);
   while (!TraceIt.isAtEoF())
@@ -856,7 +935,7 @@ void PerfReaderBase::parseAndAggregateTrace() {
 // 40062f 0x5c6313f/0x5c63170/P/-/-/0  0x5c630e7/0x5c63130/P/-/-/0 ...
 // A heuristic for fast detection by checking whether a
 // leading "  0x" and the '/' exist.
-bool PerfReaderBase::isLBRSample(StringRef Line) {
+bool PerfScriptReader::isLBRSample(StringRef Line) {
   // Skip the leading instruction pointer
   SmallVector<StringRef, 32> Records;
   Line.trim().split(Records, " ", 2, false);
@@ -867,7 +946,7 @@ bool PerfReaderBase::isLBRSample(StringRef Line) {
   return false;
 }
 
-bool PerfReaderBase::isMMap2Event(StringRef Line) {
+bool PerfScriptReader::isMMap2Event(StringRef Line) {
   // Short cut to avoid string find is possible.
   if (Line.empty() || Line.size() < 50)
     return false;
@@ -890,7 +969,7 @@ bool PerfReaderBase::isMMap2Event(StringRef Line) {
 // Determine the perfscript contains hybrid samples(call stack + LBRs) by
 // checking whether there is a non-empty call stack immediately followed by
 // a LBR sample
-PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) {
+PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
   TraceStream TraceIt(FileName);
   uint64_t FrameAddr = 0;
   while (!TraceIt.isAtEoF()) {
@@ -908,27 +987,27 @@ PerfScriptType PerfReaderBase::checkPerfScriptType(StringRef FileName) {
     if (!TraceIt.isAtEoF()) {
       if (isLBRSample(TraceIt.getCurrentLine())) {
         if (Count > 0)
-          return PERF_LBR_STACK;
+          return PerfContent::LBRStack;
         else
-          return PERF_LBR;
+          return PerfContent::LBR;
       }
       TraceIt.advance();
     }
   }
 
   exitWithError("Invalid perf script input!");
-  return PERF_INVALID;
+  return PerfContent::UnknownContent;
 }
 
-void HybridPerfReader::generateRawProfile() {
+void HybridPerfReader::generateUnsymbolizedProfile() {
   ProfileIsCS = !IgnoreStackSamples;
   if (ProfileIsCS)
     unwindSamples();
   else
-    LBRPerfReader::generateRawProfile();
+    PerfScriptReader::generateUnsymbolizedProfile();
 }
 
-void PerfReaderBase::warnTruncatedStack() {
+void PerfScriptReader::warnTruncatedStack() {
   for (auto Address : InvalidReturnAddresses) {
     WithColor::warning()
         << "Truncated stack sample due to invalid return address at "
@@ -937,16 +1016,16 @@ void PerfReaderBase::warnTruncatedStack() {
   }
 }
 
-void PerfReaderBase::parsePerfTraces() {
+void PerfScriptReader::parsePerfTraces() {
   // Parse perf traces and do aggregation.
   parseAndAggregateTrace();
 
   // Generate unsymbolized profile.
   warnTruncatedStack();
-  generateRawProfile();
+  generateUnsymbolizedProfile();
 
   if (SkipSymbolization)
-    writeRawProfile(OutputFilename);
+    writeUnsymbolizedProfile(OutputFilename);
 }
 
 } // end namespace sampleprof

diff  --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 9870bcc5411aa..c9f74313c166d 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -58,12 +58,26 @@ class TraceStream {
   }
 };
 
-// The type of perfscript
-enum PerfScriptType {
-  PERF_UNKNOWN = 0,
-  PERF_INVALID = 1,
-  PERF_LBR = 2,       // Only LBR sample
-  PERF_LBR_STACK = 3, // Hybrid sample including call stack and LBR stack.
+// The type of input format.
+enum PerfFormat {
+  UnknownFormat = 0,
+  PerfData = 1,            // Raw linux perf.data.
+  PerfScript = 2,          // Perf script create by `perf script` command.
+  UnsymbolizedProfile = 3, // Unsymbolized profile generated by llvm-profgen.
+
+};
+
+// The type of perfscript content.
+enum PerfContent {
+  UnknownContent = 0,
+  LBR = 1,      // Only LBR sample.
+  LBRStack = 2, // Hybrid sample including call stack and LBR stack.
+};
+
+struct PerfInputFile {
+  std::string InputFile;
+  PerfFormat Format = PerfFormat::UnknownFormat;
+  PerfContent Content = PerfContent::UnknownContent;
 };
 
 // The parsed LBR sample entry.
@@ -503,24 +517,44 @@ class VirtualUnwinder {
 // Read perf trace to parse the events and samples.
 class PerfReaderBase {
 public:
-  PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace,
-                 PerfScriptType Type = PERF_UNKNOWN)
-      : Binary(B), PerfTraceFile(PerfTrace), PerfType(Type) {
+  PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace)
+      : Binary(B), PerfTraceFile(PerfTrace) {
     // Initialize the base address to preferred address.
     Binary->setBaseAddress(Binary->getPreferredBaseAddress());
   };
   virtual ~PerfReaderBase() = default;
-  static std::unique_ptr<PerfReaderBase>
-  create(ProfiledBinary *Binary, StringRef PerfInputFile, bool IsPerfData);
+  static std::unique_ptr<PerfReaderBase> create(ProfiledBinary *Binary,
+                                                PerfInputFile &PerfInput);
 
-  PerfScriptType getPerfScriptType() const { return PerfType; }
   // Entry of the reader to parse multiple perf traces
-  void parsePerfTraces();
+  virtual void parsePerfTraces() = 0;
   const ContextSampleCounterMap &getSampleCounters() const {
     return SampleCounters;
   }
   bool profileIsCS() { return ProfileIsCS; }
 
+protected:
+  ProfiledBinary *Binary = nullptr;
+  StringRef PerfTraceFile;
+
+  ContextSampleCounterMap SampleCounters;
+  bool ProfileIsCS = false;
+};
+
+// Read perf script to parse the events and samples.
+class PerfScriptReader : public PerfReaderBase {
+public:
+  PerfScriptReader(ProfiledBinary *B, StringRef PerfTrace)
+      : PerfReaderBase(B, PerfTrace){};
+
+  // Entry of the reader to parse multiple perf traces
+  virtual void parsePerfTraces() override;
+  // Generate perf script from perf data
+  static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary,
+                                              PerfInputFile &File);
+  // Extract perf script type by peaking at the input
+  static PerfContent checkPerfScriptType(StringRef FileName);
+
 protected:
   // The parsed MMap event
   struct MMapEvent {
@@ -530,15 +564,11 @@ class PerfReaderBase {
     uint64_t Offset = 0;
     StringRef BinaryPath;
   };
-  // Generate perf script from perf data
-  static std::string convertPerfDataToTrace(ProfiledBinary *Binary,
-                                            StringRef PerfData);
+
   // Check whether a given line is LBR sample
   static bool isLBRSample(StringRef Line);
   // Check whether a given line is MMAP event
   static bool isMMap2Event(StringRef Line);
-  // Extract perf script type by peaking at the input
-  static PerfScriptType checkPerfScriptType(StringRef FileName);
   // Parse a single line of a PERF_RECORD_MMAP2 event looking for a
   // mapping between the binary name and its memory layout.
   static bool extractMMap2EventForBinary(ProfiledBinary *Binary, StringRef Line,
@@ -567,23 +597,18 @@ class PerfReaderBase {
   void parseSample(TraceStream &TraceIt);
   // An aggregated count is given to indicate how many times the sample is
   // repeated.
-  virtual void parseSample(TraceStream &TraceIt, uint64_t Count) = 0;
+  virtual void parseSample(TraceStream &TraceIt, uint64_t Count){};
+  void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat);
   // Post process the profile after trace aggregation, we will do simple range
   // overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).
-  virtual void generateRawProfile() = 0;
-  void writeRawProfile(StringRef Filename);
-  void writeRawProfile(raw_fd_ostream &OS);
+  virtual void generateUnsymbolizedProfile();
+  void writeUnsymbolizedProfile(StringRef Filename);
+  void writeUnsymbolizedProfile(raw_fd_ostream &OS);
 
-  ProfiledBinary *Binary = nullptr;
-  StringRef PerfTraceFile;
-  ContextSampleCounterMap SampleCounters;
   // Samples with the repeating time generated by the perf reader
   AggregatedCounter AggregatedSamples;
-  PerfScriptType PerfType = PERF_UNKNOWN;
   // Keep track of all invalid return addresses
   std::set<uint64_t> InvalidReturnAddresses;
-
-  bool ProfileIsCS = false;
 };
 
 /*
@@ -592,17 +617,12 @@ class PerfReaderBase {
     40062f 0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
           ... 0x4005c8/0x4005dc/P/-/-/0
 */
-class LBRPerfReader : public PerfReaderBase {
+class LBRPerfReader : public PerfScriptReader {
 public:
-  LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
-                PerfScriptType Type = PERF_LBR)
-      : PerfReaderBase(Binary, PerfTrace, Type){};
+  LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
+      : PerfScriptReader(Binary, PerfTrace){};
   // Parse the LBR only sample.
   virtual void parseSample(TraceStream &TraceIt, uint64_t Count) override;
-  virtual void generateRawProfile() override;
-
-private:
-  void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat);
 };
 
 /*
@@ -614,19 +634,51 @@ class LBRPerfReader : public PerfReaderBase {
     0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
           ... 0x4005c8/0x4005dc/P/-/-/0    # LBR Entries
 */
-class HybridPerfReader : public LBRPerfReader {
+class HybridPerfReader : public PerfScriptReader {
 public:
   HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
-      : LBRPerfReader(Binary, PerfTrace, PERF_LBR_STACK){};
+      : PerfScriptReader(Binary, PerfTrace){};
   // Parse the hybrid sample including the call and LBR line
   void parseSample(TraceStream &TraceIt, uint64_t Count) override;
-  void generateRawProfile() override;
+  void generateUnsymbolizedProfile() override;
 
 private:
   // Unwind the hybrid samples after aggregration
   void unwindSamples();
 };
 
+/*
+   Format of unsymbolized profile:
+
+    [frame1 @ frame2 @ ...]  # If it's a CS profile
+      number of entries in RangeCounter
+      from_1-to_1:count_1
+      from_2-to_2:count_2
+      ......
+      from_n-to_n:count_n
+      number of entries in BranchCounter
+      src_1->dst_1:count_1
+      src_2->dst_2:count_2
+      ......
+      src_n->dst_n:count_n
+    [frame1 @ frame2 @ ...]  # Next context
+      ......
+
+Note that non-CS profile doesn't have the empty `[]` context.
+*/
+class UnsymbolizedProfileReader : public PerfReaderBase {
+public:
+  UnsymbolizedProfileReader(ProfiledBinary *Binary, StringRef PerfTrace)
+      : PerfReaderBase(Binary, PerfTrace){};
+  void parsePerfTraces() override;
+
+private:
+  void readSampleCounters(TraceStream &TraceIt, SampleCounter &SCounters);
+  void readUnsymbolizedProfile(StringRef Filename);
+
+  std::unordered_set<std::string> ContextStrSet;
+};
+
 } // end namespace sampleprof
 } // end namespace llvm
 

diff  --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp
index b5be250fc12f9..ad7e6dcb029a3 100644
--- a/llvm/tools/llvm-profgen/llvm-profgen.cpp
+++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp
@@ -21,12 +21,14 @@
 
 static cl::OptionCategory ProfGenCategory("ProfGen Options");
 
-cl::opt<std::string> PerfTraceFilename(
+static cl::opt<std::string> PerfScriptFilename(
     "perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore,
     llvm::cl::MiscFlags::CommaSeparated,
     cl::desc("Path of perf-script trace created by Linux perf tool with "
              "`script` command(the raw perf.data should be profiled with -b)"),
     cl::cat(ProfGenCategory));
+static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
+                     cl::aliasopt(PerfScriptFilename));
 
 static cl::opt<std::string> PerfDataFilename(
     "perfdata", cl::value_desc("perfdata"), cl::ZeroOrMore,
@@ -34,6 +36,17 @@ static cl::opt<std::string> PerfDataFilename(
     cl::desc("Path of raw perf data created by Linux perf tool (it should be "
              "profiled with -b)"),
     cl::cat(ProfGenCategory));
+static cl::alias PDA("pd", cl::desc("Alias for --perfdata"),
+                     cl::aliasopt(PerfDataFilename));
+
+static cl::opt<std::string> UnsymbolizedProfFilename(
+    "unsymbolized-profile", cl::value_desc("unsymbolized profile"),
+    cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated,
+    cl::desc("Path of the unsymbolized profile created by "
+             "`llvm-profgen` with `--skip-symbolization`"),
+    cl::cat(ProfGenCategory));
+static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"),
+                     cl::aliasopt(UnsymbolizedProfFilename));
 
 static cl::opt<std::string> BinaryPath(
     "binary", cl::value_desc("binary"), cl::Required,
@@ -49,36 +62,34 @@ using namespace sampleprof;
 
 // Validate the command line input.
 static void validateCommandLine() {
-  // Validate input profile is provided only once
-  if (PerfDataFilename.getNumOccurrences() &&
-      PerfTraceFilename.getNumOccurrences()) {
-    std::string Msg = "`-perfdata` and `-perfscript` cannot be used together.";
-    exitWithError(Msg);
-  }
-
-  // Validate input profile is provided
-  if (!PerfDataFilename.getNumOccurrences() &&
-      !PerfTraceFilename.getNumOccurrences()) {
-    std::string Msg =
-        "Use `-perfdata` or `-perfscript` to provide input perf profile.";
-    exitWithError(Msg);
-  }
-
-  // Allow the invalid perfscript if we only use to show binary disassembly.
+  // Allow the missing perfscript if we only use to show binary disassembly.
   if (!ShowDisassemblyOnly) {
-    if (PerfTraceFilename.getNumOccurrences() &&
-        !llvm::sys::fs::exists(PerfTraceFilename)) {
+    // Validate input profile is provided only once
+    uint16_t HasPerfData = PerfDataFilename.getNumOccurrences();
+    uint16_t HasPerfScript = PerfScriptFilename.getNumOccurrences();
+    uint16_t HasUnsymbolizedProfile =
+        UnsymbolizedProfFilename.getNumOccurrences();
+    uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile;
+    if (S != 1) {
       std::string Msg =
-          "Input perf script(" + PerfTraceFilename + ") doesn't exist.";
+          S > 1
+              ? "`--perfscript`, `--perfdata` and `--unsymbolized-profile` "
+                "cannot be used together."
+              : "Perf input file is missing, please use one of `--perfscript`, "
+                "`--perfdata` and `--unsymbolized-profile` for the input.";
       exitWithError(Msg);
     }
 
-    if (PerfDataFilename.getNumOccurrences() &&
-        !llvm::sys::fs::exists(PerfDataFilename)) {
-      std::string Msg =
-          "Input perf data(" + PerfDataFilename + ") doesn't exist.";
-      exitWithError(Msg);
-    }
+    auto CheckFileExists = [](bool H, StringRef File) {
+      if (H && !llvm::sys::fs::exists(File)) {
+        std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
+        exitWithError(Msg);
+      }
+    };
+
+    CheckFileExists(HasPerfData, PerfDataFilename);
+    CheckFileExists(HasPerfScript, PerfScriptFilename);
+    CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
   }
 
   if (!llvm::sys::fs::exists(BinaryPath)) {
@@ -95,6 +106,21 @@ static void validateCommandLine() {
   }
 }
 
+static PerfInputFile getPerfInputFile() {
+  PerfInputFile File;
+  if (PerfDataFilename.getNumOccurrences()) {
+    File.InputFile = PerfDataFilename;
+    File.Format = PerfFormat::PerfData;
+  } else if (PerfScriptFilename.getNumOccurrences()) {
+    File.InputFile = PerfScriptFilename;
+    File.Format = PerfFormat::PerfScript;
+  } else if (UnsymbolizedProfFilename.getNumOccurrences()) {
+    File.InputFile = UnsymbolizedProfFilename;
+    File.Format = PerfFormat::UnsymbolizedProfile;
+  }
+  return File;
+}
+
 int main(int argc, const char *argv[]) {
   InitLLVM X(argc, argv);
 
@@ -113,15 +139,10 @@ int main(int argc, const char *argv[]) {
   if (ShowDisassemblyOnly)
     return EXIT_SUCCESS;
 
-  // Parse perf events and samples
-  StringRef PerfInputFile;
-  bool IsPerfData = PerfDataFilename.getNumOccurrences();
-  if (IsPerfData)
-    PerfInputFile = PerfDataFilename;
-  else
-    PerfInputFile = PerfTraceFilename;
+  PerfInputFile PerfFile = getPerfInputFile();
   std::unique_ptr<PerfReaderBase> Reader =
-      PerfReaderBase::create(Binary.get(), PerfInputFile, IsPerfData);
+      PerfReaderBase::create(Binary.get(), PerfFile);
+  // Parse perf events and samples
   Reader->parsePerfTraces();
 
   if (SkipSymbolization)


        


More information about the llvm-commits mailing list