1 //===--- PGOCtxProfJSONReader.h - JSON format ------------------*- 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 /// \file 10 /// 11 /// JSON format for the contextual profile for testing. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "llvm/ADT/STLExtras.h" 16 #include "llvm/IR/GlobalValue.h" 17 #include "llvm/ProfileData/CtxInstrContextNode.h" 18 #include "llvm/ProfileData/PGOCtxProfWriter.h" 19 #include "llvm/Support/CommandLine.h" 20 #include "llvm/Support/Error.h" 21 #include "llvm/Support/ErrorHandling.h" 22 #include "llvm/Support/InitLLVM.h" 23 #include "llvm/Support/JSON.h" 24 #include "llvm/Support/MemoryBuffer.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace llvm; 28 29 static cl::SubCommand FromJSON("fromJSON", "Convert from json"); 30 31 static cl::opt<std::string> InputFilename( 32 "input", cl::value_desc("input"), cl::init("-"), 33 cl::desc( 34 "Input file. The format is an array of contexts.\n" 35 "Each context is a dictionary with the following keys:\n" 36 "'Guid', mandatory. The value is a 64-bit integer.\n" 37 "'Counters', mandatory. An array of 32-bit ints. These are the " 38 "counter values.\n" 39 "'Contexts', optional. An array containing arrays of contexts. The " 40 "context array at a position 'i' is the set of callees at that " 41 "callsite index. Use an empty array to indicate no callees."), 42 cl::sub(FromJSON)); 43 44 static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 45 cl::init("-"), 46 cl::desc("Output file"), 47 cl::sub(FromJSON)); 48 49 namespace { 50 // A structural representation of the JSON input. 51 struct DeserializableCtx { 52 GlobalValue::GUID Guid = 0; 53 std::vector<uint64_t> Counters; 54 std::vector<std::vector<DeserializableCtx>> Callsites; 55 }; 56 57 ctx_profile::ContextNode * 58 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 59 const std::vector<DeserializableCtx> &DCList); 60 61 // Convert a DeserializableCtx into a ContextNode, potentially linking it to 62 // its sibling (e.g. callee at same callsite) "Next". 63 ctx_profile::ContextNode * 64 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 65 const DeserializableCtx &DC, 66 ctx_profile::ContextNode *Next = nullptr) { 67 auto AllocSize = ctx_profile::ContextNode::getAllocSize(DC.Counters.size(), 68 DC.Callsites.size()); 69 auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get(); 70 std::memset(Mem, 0, AllocSize); 71 auto *Ret = new (Mem) ctx_profile::ContextNode(DC.Guid, DC.Counters.size(), 72 DC.Callsites.size(), Next); 73 std::memcpy(Ret->counters(), DC.Counters.data(), 74 sizeof(uint64_t) * DC.Counters.size()); 75 for (const auto &[I, DCList] : llvm::enumerate(DC.Callsites)) 76 Ret->subContexts()[I] = createNode(Nodes, DCList); 77 return Ret; 78 } 79 80 // Convert a list of DeserializableCtx into a linked list of ContextNodes. 81 ctx_profile::ContextNode * 82 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 83 const std::vector<DeserializableCtx> &DCList) { 84 ctx_profile::ContextNode *List = nullptr; 85 for (const auto &DC : DCList) 86 List = createNode(Nodes, DC, List); 87 return List; 88 } 89 } // namespace 90 91 namespace llvm { 92 namespace json { 93 // Hook into the JSON deserialization. 94 bool fromJSON(const Value &E, DeserializableCtx &R, Path P) { 95 json::ObjectMapper Mapper(E, P); 96 return Mapper && Mapper.map("Guid", R.Guid) && 97 Mapper.map("Counters", R.Counters) && 98 Mapper.mapOptional("Callsites", R.Callsites); 99 } 100 } // namespace json 101 } // namespace llvm 102 103 // Save the bitstream profile from the JSON representation. 104 Error convertFromJSON() { 105 auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilename); 106 if (!BufOrError) 107 return createFileError(InputFilename, BufOrError.getError()); 108 auto P = json::parse(BufOrError.get()->getBuffer()); 109 if (!P) 110 return P.takeError(); 111 112 std::vector<DeserializableCtx> DCList; 113 json::Path::Root R(""); 114 if (!fromJSON(*P, DCList, R)) 115 return R.getError(); 116 // Nodes provides memory backing for the ContextualNodes. 117 std::vector<std::unique_ptr<char[]>> Nodes; 118 std::error_code EC; 119 raw_fd_stream Out(OutputFilename, EC); 120 if (EC) 121 return createStringError(EC, "failed to open output"); 122 PGOCtxProfileWriter Writer(Out); 123 for (const auto &DC : DCList) { 124 auto *TopList = createNode(Nodes, DC); 125 if (!TopList) 126 return createStringError( 127 "Unexpected error converting internal structure to ctx profile"); 128 Writer.write(*TopList); 129 } 130 if (EC) 131 return createStringError(EC, "failed to write output"); 132 return Error::success(); 133 } 134 135 int main(int argc, const char **argv) { 136 cl::ParseCommandLineOptions(argc, argv, "LLVM Contextual Profile Utils\n"); 137 ExitOnError ExitOnErr("llvm-ctxprof-util: "); 138 if (FromJSON) { 139 if (auto E = convertFromJSON()) { 140 handleAllErrors(std::move(E), [&](const ErrorInfoBase &E) { 141 E.log(errs()); 142 errs() << "\n"; 143 }); 144 return 1; 145 } 146 return 0; 147 } 148 cl::PrintHelpMessage(); 149 return 1; 150 } 151