xref: /llvm-project/llvm/tools/llvm-ctxprof-util/llvm-ctxprof-util.cpp (revision 7f3dcf4b2e1001c829ec431996d4ee2662a229fa)
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