1 //===- RawMemProfReader.cpp - Instrumented memory profiling reader --------===//
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 // This file contains support for reading MemProf profiling data.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include <cstdint>
14 #include <type_traits>
15 
16 #include "llvm/ProfileData/InstrProf.h"
17 #include "llvm/ProfileData/MemProfData.inc"
18 #include "llvm/ProfileData/RawMemProfReader.h"
19 
20 namespace llvm {
21 namespace memprof {
22 namespace {
23 
24 struct Summary {
25   uint64_t Version;
26   uint64_t TotalSizeBytes;
27   uint64_t NumSegments;
28   uint64_t NumMIBInfo;
29   uint64_t NumStackOffsets;
30 };
31 
32 template <class T = uint64_t> inline T alignedRead(const char *Ptr) {
33   static_assert(std::is_pod<T>::value, "Not a pod type.");
34   assert(reinterpret_cast<size_t>(Ptr) % sizeof(T) == 0 && "Unaligned Read");
35   return *reinterpret_cast<const T *>(Ptr);
36 }
37 
38 Summary computeSummary(const char *Start) {
39   auto *H = reinterpret_cast<const Header *>(Start);
40 
41   // Check alignment while reading the number of items in each section.
42   return Summary{
43       H->Version,
44       H->TotalSize,
45       alignedRead(Start + H->SegmentOffset),
46       alignedRead(Start + H->MIBOffset),
47       alignedRead(Start + H->StackOffset),
48   };
49 }
50 
51 } // namespace
52 
53 Expected<std::unique_ptr<RawMemProfReader>>
54 RawMemProfReader::create(const Twine &Path) {
55   auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/true);
56   if (std::error_code EC = BufferOr.getError())
57     return errorCodeToError(EC);
58 
59   std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
60 
61   if (Buffer->getBufferSize() == 0)
62     return make_error<InstrProfError>(instrprof_error::empty_raw_profile);
63 
64   if (!RawMemProfReader::hasFormat(*Buffer))
65     return make_error<InstrProfError>(instrprof_error::bad_magic);
66 
67   if (Buffer->getBufferSize() < sizeof(Header)) {
68     return make_error<InstrProfError>(instrprof_error::truncated);
69   }
70 
71   // The size of the buffer can be > header total size since we allow repeated
72   // serialization of memprof profiles to the same file.
73   uint64_t TotalSize = 0;
74   const char *Next = Buffer->getBufferStart();
75   while (Next < Buffer->getBufferEnd()) {
76     auto *H = reinterpret_cast<const Header *>(Next);
77     if (H->Version != MEMPROF_RAW_VERSION) {
78       return make_error<InstrProfError>(instrprof_error::unsupported_version);
79     }
80 
81     TotalSize += H->TotalSize;
82     Next += H->TotalSize;
83   }
84 
85   if (Buffer->getBufferSize() != TotalSize) {
86     return make_error<InstrProfError>(instrprof_error::malformed);
87   }
88 
89   return std::make_unique<RawMemProfReader>(std::move(Buffer));
90 }
91 
92 bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
93   if (Buffer.getBufferSize() < sizeof(uint64_t))
94     return false;
95   // Aligned read to sanity check that the buffer was allocated with at least 8b
96   // alignment.
97   const uint64_t Magic = alignedRead(Buffer.getBufferStart());
98   return Magic == MEMPROF_RAW_MAGIC_64;
99 }
100 
101 void RawMemProfReader::printSummaries(raw_ostream &OS) const {
102   int Count = 0;
103   const char *Next = DataBuffer->getBufferStart();
104   while (Next < DataBuffer->getBufferEnd()) {
105     auto Summary = computeSummary(Next);
106     OS << "MemProf Profile " << ++Count << "\n";
107     OS << "  Version: " << Summary.Version << "\n";
108     OS << "  TotalSizeBytes: " << Summary.TotalSizeBytes << "\n";
109     OS << "  NumSegments: " << Summary.NumSegments << "\n";
110     OS << "  NumMIBInfo: " << Summary.NumMIBInfo << "\n";
111     OS << "  NumStackOffsets: " << Summary.NumStackOffsets << "\n";
112     // TODO: Print the build ids once we can record them using the
113     // sanitizer_procmaps library for linux.
114 
115     auto *H = reinterpret_cast<const Header *>(Next);
116     Next += H->TotalSize;
117   }
118 }
119 
120 } // namespace memprof
121 } // namespace llvm
122