1 //===--- ClangTidyProfiling.cpp - clang-tidy --------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ClangTidyProfiling.h" 10 #include "llvm/ADT/SmallString.h" 11 #include "llvm/Support/FileSystem.h" 12 #include "llvm/Support/Path.h" 13 #include "llvm/Support/raw_ostream.h" 14 #include <optional> 15 #include <system_error> 16 #include <utility> 17 18 #define DEBUG_TYPE "clang-tidy-profiling" 19 20 namespace clang::tidy { 21 22 ClangTidyProfiling::StorageParams::StorageParams(llvm::StringRef ProfilePrefix, 23 llvm::StringRef SourceFile) 24 : Timestamp(std::chrono::system_clock::now()), SourceFilename(SourceFile) { 25 llvm::SmallString<32> TimestampStr; 26 llvm::raw_svector_ostream OS(TimestampStr); 27 llvm::format_provider<decltype(Timestamp)>::format(Timestamp, OS, 28 "%Y%m%d%H%M%S%N"); 29 30 llvm::SmallString<256> FinalPrefix(ProfilePrefix); 31 llvm::sys::path::append(FinalPrefix, TimestampStr); 32 33 // So the full output name is: /ProfilePrefix/timestamp-inputfilename.json 34 StoreFilename = llvm::Twine(FinalPrefix + "-" + 35 llvm::sys::path::filename(SourceFile) + ".json") 36 .str(); 37 } 38 39 void ClangTidyProfiling::printUserFriendlyTable(llvm::raw_ostream &OS, 40 llvm::TimerGroup &TG) { 41 TG.print(OS); 42 OS.flush(); 43 } 44 45 void ClangTidyProfiling::printAsJSON(llvm::raw_ostream &OS, 46 llvm::TimerGroup &TG) { 47 OS << "{\n"; 48 OS << R"("file": ")" << Storage->SourceFilename << "\",\n"; 49 OS << R"("timestamp": ")" << Storage->Timestamp << "\",\n"; 50 OS << "\"profile\": {\n"; 51 TG.printJSONValues(OS, ""); 52 OS << "\n}\n"; 53 OS << "}\n"; 54 OS.flush(); 55 } 56 57 void ClangTidyProfiling::storeProfileData(llvm::TimerGroup &TG) { 58 assert(Storage && "We should have a filename."); 59 60 llvm::SmallString<256> OutputDirectory(Storage->StoreFilename); 61 llvm::sys::path::remove_filename(OutputDirectory); 62 if (std::error_code EC = llvm::sys::fs::create_directories(OutputDirectory)) { 63 llvm::errs() << "Unable to create output directory '" << OutputDirectory 64 << "': " << EC.message() << "\n"; 65 return; 66 } 67 68 std::error_code EC; 69 llvm::raw_fd_ostream OS(Storage->StoreFilename, EC, llvm::sys::fs::OF_None); 70 if (EC) { 71 llvm::errs() << "Error opening output file '" << Storage->StoreFilename 72 << "': " << EC.message() << "\n"; 73 return; 74 } 75 76 printAsJSON(OS, TG); 77 } 78 79 ClangTidyProfiling::ClangTidyProfiling(std::optional<StorageParams> Storage) 80 : Storage(std::move(Storage)) {} 81 82 ClangTidyProfiling::~ClangTidyProfiling() { 83 llvm::TimerGroup TG{"clang-tidy", "clang-tidy checks profiling", Records}; 84 if (!Storage) 85 printUserFriendlyTable(llvm::errs(), TG); 86 else 87 storeProfileData(TG); 88 } 89 90 } // namespace clang::tidy 91