//===-- Core/PerfSupport.cpp - Perf measurement helpers -------------------===//
//
//                     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/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.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::fs::create_directories(DirectoryName);

  // Get PID and current time.
  // FIXME: id_type on Windows is NOT a process id despite the function name.
  // Need to call GetProcessId() providing it what get_id() returns. For now
  // disabling PID-based file names until this is fixed properly.
  //llvm::sys::self_process *SP = llvm::sys::process::get_self();
  //id_type Pid = SP->get_id();
  unsigned Pid = 0;
  llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime();

  std::string FileName;
  llvm::raw_string_ostream SS(FileName);
  SS << DirectoryName << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid
     << ".json";

  std::error_code EC;
  llvm::raw_fd_ostream FileStream(SS.str(), EC, llvm::sys::fs::F_Text);
  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";
    }
  }
}
