Hi tareqsiraj, arielbernal,

Performance timers captured in each transform for all files they process
are now collected and arranged per source file in preparation for
writing to disk.

http://llvm-reviews.chandlerc.com/D912

Files:
  cpp11-migrate/Core/CMakeLists.txt
  cpp11-migrate/Core/PerfSupport.cpp
  cpp11-migrate/Core/PerfSupport.h
  cpp11-migrate/Core/Transform.cpp
  cpp11-migrate/Core/Transform.h
  cpp11-migrate/tool/Cpp11Migrate.cpp
  unittests/cpp11-migrate/CMakeLists.txt
  unittests/cpp11-migrate/PerfSupportTest.cpp
Index: cpp11-migrate/Core/CMakeLists.txt
===================================================================
--- cpp11-migrate/Core/CMakeLists.txt
+++ cpp11-migrate/Core/CMakeLists.txt
@@ -4,6 +4,7 @@
   Transforms.cpp
   Transform.cpp
   IncludeExcludeInfo.cpp
+  PerfSupport.cpp
   )
 target_link_libraries(migrateCore
   clangTooling
Index: cpp11-migrate/Core/PerfSupport.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/Core/PerfSupport.cpp
@@ -0,0 +1,97 @@
+//===-- cpp11-migrate/Cpp11Migrate.cpp - Main file C++11 migration tool ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides implementations for performance measuring helpers.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PerfSupport.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Path.h"
+
+void collectSourcePerfData(const Transform &T, SourcePerfData &Data) {
+  for (Transform::TimingVec::const_iterator I = T.timing_begin(),
+                                            E = T.timing_end();
+       I != E; ++I) {
+    SourcePerfData::iterator DataI = Data.insert(
+        SourcePerfData::value_type(I->first, std::vector<PerfItem>())).first;
+    DataI->second
+        .push_back(PerfItem(T.getName(), I->second.getProcessTime() * 1000.0));
+  }
+}
+
+void writePerfDataJSON(
+    const llvm::StringRef DirectoryName,
+    const SourcePerfData &TimingResults) {
+  // Create directory path if it doesn't exist
+  llvm::sys::Path P(DirectoryName);
+  P.createDirectoryOnDisk(true);
+
+  // Get PID and current time.
+  llvm::sys::self_process *SP = llvm::sys::process::get_self();
+  unsigned Pid = SP->get_id();
+  llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime();
+
+  std::string FileName;
+  llvm::raw_string_ostream SS(FileName);
+  SS << P.str() << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
+     << ".json";
+
+  std::string ErrorInfo;
+  llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo);
+  FileStream << "{\n";
+  FileStream << "  \"Sources\" : [\n";
+  for (SourcePerfData::const_iterator I = TimingResults.begin(),
+                                      E = TimingResults.end();
+       I != E; ++I) {
+    // Terminate the last source with a comma before continuing to the next one.
+    if (I != TimingResults.begin())
+      FileStream << ",\n";
+
+    FileStream << "    {\n";
+    FileStream << "      \"Source \" : \"" << I->first << "\",\n";
+    FileStream << "      \"Data\" : [\n";
+    for (std::vector<PerfItem>::const_iterator IE = I->second.begin(),
+                                               EE = I->second.end();
+         IE != EE; ++IE) {
+      // Terminate the last perf item with a comma before continuing to the next
+      // one.
+      if (IE != I->second.begin())
+        FileStream << ",\n";
+
+      FileStream << "        {\n";
+      FileStream << "          \"TimerId\" : \"" << IE->Label << "\",\n";
+      FileStream << "          \"Time\" : " << llvm::format("%.2f", IE->Duration)
+                 << "\n";
+
+      FileStream << "        }";
+
+    }
+    FileStream << "\n      ]\n";
+    FileStream << "    }";
+  }
+  FileStream << "\n  ]\n";
+  FileStream << "}";
+}
+
+void dumpPerfData(const SourcePerfData &Data) {
+  for (SourcePerfData::const_iterator I = Data.begin(), E = Data.end(); I != E;
+       ++I) {
+    llvm::errs() << I->first << ":\n";
+    for (std::vector<PerfItem>::const_iterator VecI = I->second.begin(),
+                                               VecE = I->second.end();
+         VecI != VecE; ++VecI) {
+      llvm::errs() << "  " << VecI->Label << ": "
+                   << llvm::format("%.1f", VecI->Duration) << "ms\n";
+    }
+  }
+}
Index: cpp11-migrate/Core/PerfSupport.h
===================================================================
--- /dev/null
+++ cpp11-migrate/Core/PerfSupport.h
@@ -0,0 +1,56 @@
+//===-- cpp11-migrate/PerfSupport.h - Perf measurement helpers --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides helper functionality for measuring performance and
+/// recording data to file.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CPP11_MIGRATE_PERFSUPPORT_H
+#define CPP11_MIGRATE_PERFSUPPORT_H
+
+#include <map>
+#include <vector>
+#include "Transform.h"
+#include "llvm/ADT/StringRef.h"
+
+/// \brief A single piece of performance data: a duration in milliseconds and a
+/// label for that duration.
+struct PerfItem {
+  PerfItem(const llvm::StringRef Label, double Duration)
+      : Label(Label), Duration(Duration) {}
+
+  /// Label for this performance measurement.
+  std::string Label;
+
+  /// Duration in milliseconds.
+  double Duration;
+};
+
+/// Maps source file names to a vector of durations/labels.
+typedef std::map<std::string, std::vector<PerfItem> > SourcePerfData;
+
+/// Extracts durations collected by a Transform for all sources and adds them
+/// to a SourcePerfData map where data is organized by source file.
+extern void collectSourcePerfData(const Transform &T, SourcePerfData &Data);
+
+/// Write timing results to a JSON formatted file.
+///
+/// File is placed in the directory given by \p DirectoryName. File is named in
+/// a unique way with time and process ID to avoid naming collisions with
+/// existing files or files being generated by other migrator processes.
+void writePerfDataJSON(
+    const llvm::StringRef DirectoryName,
+    const SourcePerfData &TimingResults);
+
+/// Dump a SourcePerfData map to llvm::errs().
+extern void dumpPerfData(const SourcePerfData &Data);
+
+#endif // CPP11_MIGRATE_PERFSUPPORT_H
Index: cpp11-migrate/Core/Transform.cpp
===================================================================
--- cpp11-migrate/Core/Transform.cpp
+++ cpp11-migrate/Core/Transform.cpp
@@ -51,3 +51,7 @@
 
   Timings.back().second += llvm::TimeRecord::getCurrentTime(false);
 }
+
+void Transform::addTiming(llvm::StringRef Label, llvm::TimeRecord Duration) {
+  Timings.push_back(std::make_pair(Label.str(), Duration));
+}
Index: cpp11-migrate/Core/Transform.h
===================================================================
--- cpp11-migrate/Core/Transform.h
+++ cpp11-migrate/Core/Transform.h
@@ -207,6 +207,13 @@
     DeferredChanges = Changes;
   }
 
+  /// \brief Allows subclasses to manually add performance timer data.
+  ///
+  /// \p Label should probably include the source file name somehow as the
+  /// duration info is simply added to the vector of timing data which holds
+  /// data for all sources processed by this transform.
+  void addTiming(llvm::StringRef Label, llvm::TimeRecord Duration);
+
 private:
   const std::string Name;
   bool EnableTiming;
Index: cpp11-migrate/tool/Cpp11Migrate.cpp
===================================================================
--- cpp11-migrate/tool/Cpp11Migrate.cpp
+++ cpp11-migrate/tool/Cpp11Migrate.cpp
@@ -17,17 +17,15 @@
 
 #include "Core/Transforms.h"
 #include "Core/Transform.h"
+#include "Core/PerfSupport.h"
 #include "LoopConvert/LoopConvert.h"
 #include "UseNullptr/UseNullptr.h"
 #include "UseAuto/UseAuto.h"
 #include "AddOverride/AddOverride.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/Process.h"
 #include "llvm/Support/Signals.h"
-#include "llvm/Support/Timer.h"
 
 namespace cl = llvm::cl;
 using namespace clang::tooling;
@@ -55,8 +53,8 @@
 
 const char NoTiming[] = "no_timing";
 static cl::opt<std::string> TimingDirectoryName(
-    "report-times", cl::desc("Capture performance data and output to specified "
-                             "directory. Default ./migrate_perf"),
+    "perf", cl::desc("Capture performance data and output to specified "
+                     "directory. Default: ./migrate_perf"),
     cl::init(NoTiming), cl::ValueOptional, cl::value_desc("directory name"));
 
 // TODO: Remove cl::Hidden when functionality for acknowledging include/exclude
@@ -87,60 +85,6 @@
   }
 };
 
-struct ExecutionTime {
-  std::string TimerId;
-  float Time;
-  ExecutionTime(const std::string &TimerId, float Time)
-      : TimerId(TimerId), Time(Time) {}
-};
-
-// Save execution times to a json formatted file.
-void reportExecutionTimes(
-    const llvm::StringRef DirectoryName,
-    const std::map<std::string, std::vector<ExecutionTime> > &TimingResults) {
-  // Create directory path if it doesn't exist
-  llvm::sys::Path P(DirectoryName);
-  P.createDirectoryOnDisk(true);
-
-  // Get PID and current time.
-  llvm::sys::self_process *SP = llvm::sys::process::get_self();
-  unsigned Pid = SP->get_id();
-  llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime();
-
-  std::string FileName;
-  llvm::raw_string_ostream SS(FileName);
-  SS << P.str() << "/" << static_cast<int>(T.getWallTime()) << Pid << ".json";
-
-
-  std::string ErrorInfo;
-  llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo);
-  FileStream << "{\n";
-  FileStream << "  \"Sources\" : [\n";
-  for (std::map<std::string, std::vector<ExecutionTime> >::const_iterator
-           I = TimingResults.begin(),
-           E = TimingResults.end();
-       I != E; ++I) {
-    FileStream << "    {\n";
-    FileStream << "      \"Source \" : \"" << I->first << "\",\n";
-    FileStream << "      \"Data\" : [\n";
-    for (std::vector<ExecutionTime>::const_iterator IE = I->second.begin(),
-                                                    EE = I->second.end();
-         IE != EE; ++IE) {
-      FileStream << "        {\n";
-      FileStream << "          \"TimerId\" : \"" << (*IE).TimerId << "\",\n";
-      FileStream << "          \"Time\" : " << llvm::format("%6.2f", (*IE).Time)
-                 << "\n";
-
-      FileStream << "        },\n";
-
-    }
-    FileStream << "      ]\n";
-    FileStream << "    },\n";
-  }
-  FileStream << "  ]\n";
-  FileStream << "}";
-}
-
 int main(int argc, const char **argv) {
   llvm::sys::PrintStackTraceOnErrorSignal();
   Transforms TransformManager;
@@ -165,8 +109,6 @@
   // Since ExecutionTimeDirectoryName could be an empty string we compare
   // against the default value when the command line option is not specified.
   bool EnableTiming = (TimingDirectoryName != NoTiming);
-  std::map<std::string, std::vector<ExecutionTime> > TimingResults;
-
   TransformManager.createSelectedTransforms(EnableTiming);
 
   if (TransformManager.begin() == TransformManager.end()) {
@@ -177,6 +119,8 @@
   FileContentsByPath FileStates1, FileStates2,
       *InputFileStates = &FileStates1, *OutputFileStates = &FileStates2;
 
+  SourcePerfData PerfData;
+
   // Apply transforms.
   for (Transforms::const_iterator I = TransformManager.begin(),
                                   E = TransformManager.end();
@@ -188,6 +132,10 @@
       // FIXME: Improve ClangTool to not abort if just one file fails.
       return 1;
     }
+
+    if (EnableTiming)
+      collectSourcePerfData(**I, PerfData);
+
     if (SummaryMode) {
       llvm::outs() << "Transform: " << (*I)->getName()
                    << " - Accepted: "
@@ -236,12 +184,12 @@
   }
 
   // Report execution times.
-  if (EnableTiming && TimingResults.size() > 0) {
+  if (EnableTiming && !PerfData.empty()) {
     std::string DirectoryName = TimingDirectoryName;
     // Use default directory name.
-    if (DirectoryName == "")
+    if (DirectoryName.empty())
       DirectoryName = "./migrate_perf";
-    reportExecutionTimes(DirectoryName, TimingResults);
+    writePerfDataJSON(DirectoryName, PerfData);
   }
 
   return 0;
Index: unittests/cpp11-migrate/CMakeLists.txt
===================================================================
--- unittests/cpp11-migrate/CMakeLists.txt
+++ unittests/cpp11-migrate/CMakeLists.txt
@@ -8,7 +8,8 @@
 
 add_extra_unittest(Cpp11MigrateTests
   TransformTest.cpp
-  IncludeExcludeTest.cpp)
+  IncludeExcludeTest.cpp
+  PerfSupportTest.cpp)
 
 target_link_libraries(Cpp11MigrateTests
   migrateCore
Index: unittests/cpp11-migrate/PerfSupportTest.cpp
===================================================================
--- /dev/null
+++ unittests/cpp11-migrate/PerfSupportTest.cpp
@@ -0,0 +1,89 @@
+#include "gtest/gtest.h"
+#include "Core/PerfSupport.h"
+
+using namespace llvm;
+using namespace clang;
+
+class TransformA : public Transform {
+public:
+  TransformA()
+      : Transform("TransformA", false) {}
+
+  virtual int apply(const FileContentsByPath &, RiskLevel,
+                    const tooling::CompilationDatabase &,
+                    const std::vector<std::string> &, FileContentsByPath &) {
+    return 0;
+  }
+
+  void addTiming(StringRef Label, TimeRecord Duration) {
+    Transform::addTiming(Label, Duration);
+  }
+};
+
+class TransformB : public Transform {
+public:
+  TransformB()
+      : Transform("TransformB", false) {}
+
+  virtual int apply(const FileContentsByPath &, RiskLevel,
+                    const tooling::CompilationDatabase &,
+                    const std::vector<std::string> &, FileContentsByPath &) {
+    return 0;
+  }
+
+  void addTiming(StringRef Label, TimeRecord Duration) {
+    Transform::addTiming(Label, Duration);
+  }
+};
+
+struct ExpectedResults {
+  const char *SourceName;
+  unsigned DataCount;
+  struct Datum {
+    const char *Label;
+    float Duration;
+  } Data[2];
+};
+
+TEST(PerfSupport, collectSourcePerfData) {
+  TransformA A;
+  TransformB B;
+  
+  // The actual durations don't matter. Below only their relative ordering is
+  // tested to ensure times, labels, and sources all stay together properly.
+  A.addTiming("FileA.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+  A.addTiming("FileC.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+  B.addTiming("FileC.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+  B.addTiming("FileB.cpp", TimeRecord::getCurrentTime(/*Start=*/true));
+
+  SourcePerfData PerfData;
+  collectSourcePerfData(A, PerfData);
+
+  SourcePerfData::const_iterator FileAI = PerfData.find("FileA.cpp");
+  EXPECT_NE(FileAI, PerfData.end());
+  SourcePerfData::const_iterator FileCI = PerfData.find("FileC.cpp");
+  EXPECT_NE(FileCI, PerfData.end());
+  EXPECT_EQ(2u, PerfData.size());
+
+  EXPECT_EQ(1u, FileAI->second.size());
+  EXPECT_EQ("TransformA", FileAI->second[0].Label);
+  EXPECT_EQ(1u, FileCI->second.size());
+  EXPECT_EQ("TransformA", FileCI->second[0].Label);
+  EXPECT_LE(FileAI->second[0].Duration, FileCI->second[0].Duration);
+
+  collectSourcePerfData(B, PerfData);
+
+  SourcePerfData::const_iterator FileBI = PerfData.find("FileB.cpp");
+  EXPECT_NE(FileBI, PerfData.end());
+  EXPECT_EQ(3u, PerfData.size());
+
+  EXPECT_EQ(1u, FileAI->second.size());
+  EXPECT_EQ("TransformA", FileAI->second[0].Label);
+  EXPECT_EQ(2u, FileCI->second.size());
+  EXPECT_EQ("TransformA", FileCI->second[0].Label);
+  EXPECT_EQ("TransformB", FileCI->second[1].Label);
+  EXPECT_LE(FileCI->second[0].Duration, FileCI->second[1].Duration);
+  EXPECT_EQ(1u, FileBI->second.size());
+  EXPECT_EQ("TransformB", FileBI->second[0].Label);
+  EXPECT_LE(FileCI->second[1].Duration, FileBI->second[0].Duration);
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to