xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ProfileData/MemProfYAML.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 #ifndef LLVM_PROFILEDATA_MEMPROFYAML_H_
2 #define LLVM_PROFILEDATA_MEMPROFYAML_H_
3 
4 #include "llvm/ADT/SmallVector.h"
5 #include "llvm/ProfileData/DataAccessProf.h"
6 #include "llvm/ProfileData/MemProf.h"
7 #include "llvm/Support/Format.h"
8 #include "llvm/Support/YAMLTraits.h"
9 
10 namespace llvm {
11 namespace memprof {
12 // A "typedef" for GUID.  See ScalarTraits<memprof::GUIDHex64> for how a GUID is
13 // serialized and deserialized in YAML.
14 LLVM_YAML_STRONG_TYPEDEF(uint64_t, GUIDHex64)
15 
16 // Helper struct for AllMemProfData.  In YAML, we treat the GUID and the fields
17 // within MemProfRecord at the same level as if the GUID were part of
18 // MemProfRecord.
19 struct GUIDMemProfRecordPair {
20   GUIDHex64 GUID;
21   MemProfRecord Record;
22 };
23 
24 // Helper struct to yamlify memprof::DataAccessProfData. The struct
25 // members use owned strings. This is for simplicity and assumes that most real
26 // world use cases do look-ups and regression test scale is small.
27 struct YamlDataAccessProfData {
28   std::vector<memprof::DataAccessProfRecord> Records;
29   std::vector<uint64_t> KnownColdStrHashes;
30   std::vector<std::string> KnownColdSymbols;
31 
isEmptyYamlDataAccessProfData32   bool isEmpty() const {
33     return Records.empty() && KnownColdStrHashes.empty() &&
34            KnownColdSymbols.empty();
35   }
36 };
37 
38 // The top-level data structure, only used with YAML for now.
39 struct AllMemProfData {
40   std::vector<GUIDMemProfRecordPair> HeapProfileRecords;
41   YamlDataAccessProfData YamlifiedDataAccessProfiles;
42 };
43 } // namespace memprof
44 
45 namespace yaml {
46 template <> struct ScalarTraits<memprof::GUIDHex64> {
47   static void output(const memprof::GUIDHex64 &Val, void *, raw_ostream &Out) {
48     // Print GUID as a hexadecimal number with 0x prefix, no padding to keep
49     // test strings compact.
50     Out << format("0x%" PRIx64, (uint64_t)Val);
51   }
52   static StringRef input(StringRef Scalar, void *, memprof::GUIDHex64 &Val) {
53     // Reject decimal GUIDs.
54     if (all_of(Scalar, [](char C) { return std::isdigit(C); }))
55       return "use a hexadecimal GUID or a function instead";
56 
57     uint64_t Num;
58     if (Scalar.starts_with_insensitive("0x")) {
59       // Accept hexadecimal numbers starting with 0x or 0X.
60       if (Scalar.getAsInteger(0, Num))
61         return "invalid hex64 number";
62       Val = Num;
63     } else {
64       // Otherwise, treat the input as a string containing a function name.
65       Val = memprof::getGUID(Scalar);
66     }
67     return StringRef();
68   }
69   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
70 };
71 
72 template <> struct MappingTraits<memprof::Frame> {
73   // Essentially the same as memprof::Frame except that Function is of type
74   // memprof::GUIDHex64 instead of GlobalValue::GUID.  This class helps in two
75   // ways.  During serialization, we print Function as a 16-digit hexadecimal
76   // number.  During deserialization, we accept a function name as an
77   // alternative to the usual GUID expressed as a hexadecimal number.
78   class FrameWithHex64 {
79   public:
80     FrameWithHex64(IO &) {}
81     FrameWithHex64(IO &, const memprof::Frame &F)
82         : Function(F.Function), LineOffset(F.LineOffset), Column(F.Column),
83           IsInlineFrame(F.IsInlineFrame) {}
84     memprof::Frame denormalize(IO &) {
85       return memprof::Frame(Function, LineOffset, Column, IsInlineFrame);
86     }
87 
88     memprof::GUIDHex64 Function = 0;
89     static_assert(std::is_same_v<decltype(Function.value),
90                                  decltype(memprof::Frame::Function)>);
91     decltype(memprof::Frame::LineOffset) LineOffset = 0;
92     decltype(memprof::Frame::Column) Column = 0;
93     decltype(memprof::Frame::IsInlineFrame) IsInlineFrame = false;
94   };
95 
96   static void mapping(IO &Io, memprof::Frame &F) {
97     MappingNormalization<FrameWithHex64, memprof::Frame> Keys(Io, F);
98 
99     Io.mapRequired("Function", Keys->Function);
100     Io.mapRequired("LineOffset", Keys->LineOffset);
101     Io.mapRequired("Column", Keys->Column);
102     Io.mapRequired("IsInlineFrame", Keys->IsInlineFrame);
103 
104     // Assert that the definition of Frame matches what we expect.  The
105     // structured bindings below detect changes to the number of fields.
106     // static_assert checks the type of each field.
107     const auto &[Function, SymbolName, LineOffset, Column, IsInlineFrame] = F;
108     static_assert(
109         std::is_same_v<remove_cvref_t<decltype(Function)>, GlobalValue::GUID>);
110     static_assert(std::is_same_v<remove_cvref_t<decltype(SymbolName)>,
111                                  std::unique_ptr<std::string>>);
112     static_assert(
113         std::is_same_v<remove_cvref_t<decltype(LineOffset)>, uint32_t>);
114     static_assert(std::is_same_v<remove_cvref_t<decltype(Column)>, uint32_t>);
115     static_assert(
116         std::is_same_v<remove_cvref_t<decltype(IsInlineFrame)>, bool>);
117 
118     // MSVC issues unused variable warnings despite the uses in static_assert
119     // above.
120     (void)Function;
121     (void)SymbolName;
122     (void)LineOffset;
123     (void)Column;
124     (void)IsInlineFrame;
125   }
126 
127   // Request the inline notation for brevity:
128   //   { Function: 123, LineOffset: 11, Column: 10; IsInlineFrame: true }
129   static const bool flow = true;
130 };
131 
132 template <> struct CustomMappingTraits<memprof::PortableMemInfoBlock> {
133   static void inputOne(IO &Io, StringRef KeyStr,
134                        memprof::PortableMemInfoBlock &MIB) {
135     // PortableMemInfoBlock keeps track of the set of fields that actually have
136     // values.  We update the set here as we receive a key-value pair from the
137     // YAML document.
138     //
139     // We set MIB.Name via a temporary variable because ScalarTraits<uintptr_t>
140     // isn't available on macOS.
141 #define MIBEntryDef(NameTag, Name, Type)                                       \
142   if (KeyStr == #Name) {                                                       \
143     uint64_t Value;                                                            \
144     Io.mapRequired(KeyStr.str().c_str(), Value);                               \
145     MIB.Name = static_cast<Type>(Value);                                       \
146     MIB.Schema.set(llvm::to_underlying(memprof::Meta::Name));                  \
147     return;                                                                    \
148   }
149 #include "llvm/ProfileData/MIBEntryDef.inc"
150 #undef MIBEntryDef
151     Io.setError("Key is not a valid validation event");
152   }
153 
154   static void output(IO &Io, memprof::PortableMemInfoBlock &MIB) {
155     auto Schema = MIB.getSchema();
156 #define MIBEntryDef(NameTag, Name, Type)                                       \
157   if (Schema.test(llvm::to_underlying(memprof::Meta::Name))) {                 \
158     uint64_t Value = MIB.Name;                                                 \
159     Io.mapRequired(#Name, Value);                                              \
160   }
161 #include "llvm/ProfileData/MIBEntryDef.inc"
162 #undef MIBEntryDef
163   }
164 };
165 
166 template <> struct MappingTraits<memprof::AllocationInfo> {
167   static void mapping(IO &Io, memprof::AllocationInfo &AI) {
168     Io.mapRequired("Callstack", AI.CallStack);
169     Io.mapRequired("MemInfoBlock", AI.Info);
170   }
171 };
172 
173 // In YAML, we use GUIDMemProfRecordPair instead of MemProfRecord so that we can
174 // treat the GUID and the fields within MemProfRecord at the same level as if
175 // the GUID were part of MemProfRecord.
176 template <> struct MappingTraits<memprof::CallSiteInfo> {
177   // Helper class to normalize CalleeGuids to use GUIDHex64 for YAML I/O.
178   class CallSiteInfoWithHex64Guids {
179   public:
180     CallSiteInfoWithHex64Guids(IO &) {}
181     CallSiteInfoWithHex64Guids(IO &, const memprof::CallSiteInfo &CS)
182         : Frames(CS.Frames) {
183       // Convert uint64_t GUIDs to GUIDHex64 for serialization.
184       CalleeGuids.reserve(CS.CalleeGuids.size());
185       for (uint64_t Guid : CS.CalleeGuids)
186         CalleeGuids.push_back(memprof::GUIDHex64(Guid));
187     }
188 
189     memprof::CallSiteInfo denormalize(IO &) {
190       memprof::CallSiteInfo CS;
191       CS.Frames = Frames;
192       // Convert GUIDHex64 back to uint64_t GUIDs after deserialization.
193       CS.CalleeGuids.reserve(CalleeGuids.size());
194       for (memprof::GUIDHex64 HexGuid : CalleeGuids)
195         CS.CalleeGuids.push_back(HexGuid.value);
196       return CS;
197     }
198 
199     // Keep Frames as is, since MappingTraits<memprof::Frame> handles its
200     // Function GUID.
201     decltype(memprof::CallSiteInfo::Frames) Frames;
202     // Use a vector of GUIDHex64 for CalleeGuids to leverage its ScalarTraits.
203     SmallVector<memprof::GUIDHex64> CalleeGuids;
204   };
205 
206   static void mapping(IO &Io, memprof::CallSiteInfo &CS) {
207     // Use MappingNormalization to handle the conversion between
208     // memprof::CallSiteInfo and CallSiteInfoWithHex64Guids.
209     MappingNormalization<CallSiteInfoWithHex64Guids, memprof::CallSiteInfo>
210         Keys(Io, CS);
211     Io.mapRequired("Frames", Keys->Frames);
212     // Map the normalized CalleeGuids (which are now GUIDHex64).
213     Io.mapOptional("CalleeGuids", Keys->CalleeGuids);
214   }
215 };
216 
217 template <> struct MappingTraits<memprof::GUIDMemProfRecordPair> {
218   static void mapping(IO &Io, memprof::GUIDMemProfRecordPair &Pair) {
219     Io.mapRequired("GUID", Pair.GUID);
220     Io.mapRequired("AllocSites", Pair.Record.AllocSites);
221     Io.mapRequired("CallSites", Pair.Record.CallSites);
222   }
223 };
224 
225 template <> struct MappingTraits<memprof::SourceLocation> {
226   static void mapping(IO &Io, memprof::SourceLocation &Loc) {
227     Io.mapOptional("FileName", Loc.FileName);
228     Io.mapOptional("Line", Loc.Line);
229   }
230 };
231 
232 template <> struct MappingTraits<memprof::DataAccessProfRecord> {
233   static void mapping(IO &Io, memprof::DataAccessProfRecord &Rec) {
234     if (Io.outputting()) {
235       if (std::holds_alternative<std::string>(Rec.SymHandle)) {
236         Io.mapOptional("Symbol", std::get<std::string>(Rec.SymHandle));
237       } else {
238         Io.mapOptional("Hash", std::get<uint64_t>(Rec.SymHandle));
239       }
240     } else {
241       std::string SymName;
242       uint64_t Hash = 0;
243       Io.mapOptional("Symbol", SymName);
244       Io.mapOptional("Hash", Hash);
245       if (!SymName.empty()) {
246         Rec.SymHandle = SymName;
247       } else {
248         Rec.SymHandle = Hash;
249       }
250     }
251     Io.mapRequired("AccessCount", Rec.AccessCount);
252     Io.mapOptional("Locations", Rec.Locations);
253   }
254 };
255 
256 template <> struct MappingTraits<memprof::YamlDataAccessProfData> {
257   static void mapping(IO &Io, memprof::YamlDataAccessProfData &Data) {
258     Io.mapOptional("SampledRecords", Data.Records);
259     Io.mapOptional("KnownColdSymbols", Data.KnownColdSymbols);
260     Io.mapOptional("KnownColdStrHashes", Data.KnownColdStrHashes);
261   }
262 };
263 
264 template <> struct MappingTraits<memprof::AllMemProfData> {
265   static void mapping(IO &Io, memprof::AllMemProfData &Data) {
266     Io.mapRequired("HeapProfileRecords", Data.HeapProfileRecords);
267     // Map data access profiles if reading input, or if writing output &&
268     // the struct is populated.
269     if (!Io.outputting() || !Data.YamlifiedDataAccessProfiles.isEmpty())
270       Io.mapOptional("DataAccessProfiles", Data.YamlifiedDataAccessProfiles);
271   }
272 };
273 
274 template <> struct SequenceTraits<SmallVector<memprof::GUIDHex64>> {
275   static size_t size(IO &io, SmallVector<memprof::GUIDHex64> &Seq) {
276     return Seq.size();
277   }
278   static memprof::GUIDHex64 &
279   element(IO &io, SmallVector<memprof::GUIDHex64> &Seq, size_t Index) {
280     if (Index >= Seq.size())
281       Seq.resize(Index + 1);
282     return Seq[Index];
283   }
284   static const bool flow = true;
285 };
286 
287 } // namespace yaml
288 } // namespace llvm
289 
290 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::Frame)
291 LLVM_YAML_IS_SEQUENCE_VECTOR(std::vector<memprof::Frame>)
292 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::AllocationInfo)
293 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::CallSiteInfo)
294 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::GUIDMemProfRecordPair)
295 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::GUIDHex64) // Used for CalleeGuids
296 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::DataAccessProfRecord)
297 LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::SourceLocation)
298 
299 #endif // LLVM_PROFILEDATA_MEMPROFYAML_H_
300