1 #include "llvm/ProfileData/MemProf.h"
2 #include "llvm/ADT/DenseMap.h"
3 #include "llvm/ADT/MapVector.h"
4 #include "llvm/DebugInfo/DIContext.h"
5 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
6 #include "llvm/Object/ObjectFile.h"
7 #include "llvm/ProfileData/InstrProf.h"
8 #include "llvm/ProfileData/MemProfData.inc"
9 #include "llvm/ProfileData/RawMemProfReader.h"
10 #include "llvm/Support/Error.h"
11 #include "llvm/Support/MD5.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15 
16 #include <initializer_list>
17 
18 namespace {
19 
20 using ::llvm::DIGlobal;
21 using ::llvm::DIInliningInfo;
22 using ::llvm::DILineInfo;
23 using ::llvm::DILineInfoSpecifier;
24 using ::llvm::DILocal;
25 using ::llvm::memprof::CallStackMap;
26 using ::llvm::memprof::MemInfoBlock;
27 using ::llvm::memprof::MemProfRecord;
28 using ::llvm::memprof::MemProfSchema;
29 using ::llvm::memprof::Meta;
30 using ::llvm::memprof::PortableMemInfoBlock;
31 using ::llvm::memprof::RawMemProfReader;
32 using ::llvm::memprof::SegmentEntry;
33 using ::llvm::object::SectionedAddress;
34 using ::llvm::symbolize::SymbolizableModule;
35 using ::testing::Return;
36 
37 class MockSymbolizer : public SymbolizableModule {
38 public:
39   MOCK_CONST_METHOD3(symbolizeInlinedCode,
40                      DIInliningInfo(SectionedAddress, DILineInfoSpecifier,
41                                     bool));
42   // Most of the methods in the interface are unused. We only mock the
43   // method that we expect to be called from the memprof reader.
44   virtual DILineInfo symbolizeCode(SectionedAddress, DILineInfoSpecifier,
45                                    bool) const {
46     llvm_unreachable("unused");
47   }
48   virtual DIGlobal symbolizeData(SectionedAddress) const {
49     llvm_unreachable("unused");
50   }
51   virtual std::vector<DILocal> symbolizeFrame(SectionedAddress) const {
52     llvm_unreachable("unused");
53   }
54   virtual bool isWin32Module() const { llvm_unreachable("unused"); }
55   virtual uint64_t getModulePreferredBase() const {
56     llvm_unreachable("unused");
57   }
58 };
59 
60 struct MockInfo {
61   std::string FunctionName;
62   uint32_t Line;
63   uint32_t StartLine;
64   uint32_t Column;
65 };
66 DIInliningInfo makeInliningInfo(std::initializer_list<MockInfo> MockFrames) {
67   DIInliningInfo Result;
68   for (const auto &Item : MockFrames) {
69     DILineInfo Frame;
70     Frame.FunctionName = Item.FunctionName;
71     Frame.Line = Item.Line;
72     Frame.StartLine = Item.StartLine;
73     Frame.Column = Item.Column;
74     Result.addFrame(Frame);
75   }
76   return Result;
77 }
78 
79 llvm::SmallVector<SegmentEntry, 4> makeSegments() {
80   llvm::SmallVector<SegmentEntry, 4> Result;
81   // Mimic an entry for a non position independent executable.
82   Result.emplace_back(0x0, 0x40000, 0x0);
83   return Result;
84 }
85 
86 const DILineInfoSpecifier specifier() {
87   return DILineInfoSpecifier(
88       DILineInfoSpecifier::FileLineInfoKind::RawValue,
89       DILineInfoSpecifier::FunctionNameKind::LinkageName);
90 }
91 
92 MATCHER_P4(FrameContains, Function, LineOffset, Column, Inline, "") {
93   const std::string ExpectedHash = std::to_string(llvm::MD5Hash(Function));
94   if (arg.Function != ExpectedHash) {
95     *result_listener << "Hash mismatch";
96     return false;
97   }
98   if (arg.LineOffset == LineOffset && arg.Column == Column &&
99       arg.IsInlineFrame == Inline) {
100     return true;
101   }
102   *result_listener << "LineOffset, Column or Inline mismatch";
103   return false;
104 }
105 
106 MemProfSchema getFullSchema() {
107   MemProfSchema Schema;
108 #define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name);
109 #include "llvm/ProfileData/MIBEntryDef.inc"
110 #undef MIBEntryDef
111   return Schema;
112 }
113 
114 TEST(MemProf, FillsValue) {
115   std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());
116 
117   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
118                                                 specifier(), false))
119       .Times(2)
120       .WillRepeatedly(Return(makeInliningInfo({
121           {"foo", 10, 5, 30},
122           {"bar", 201, 150, 20},
123       })));
124 
125   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x6000},
126                                                 specifier(), false))
127       .Times(1)
128       .WillRepeatedly(Return(makeInliningInfo({
129           {"baz", 10, 5, 30},
130           {"qux.llvm.12345", 75, 70, 10},
131       })));
132 
133   CallStackMap CSM;
134   CSM[0x1] = {0x2000};
135   CSM[0x2] = {0x6000, 0x2000};
136 
137   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
138   Prof[0x1].AllocCount = 1;
139   Prof[0x2].AllocCount = 2;
140 
141   auto Seg = makeSegments();
142 
143   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);
144 
145   std::vector<MemProfRecord> Records;
146   for (const MemProfRecord &R : Reader) {
147     Records.push_back(R);
148   }
149   EXPECT_EQ(Records.size(), 2U);
150 
151   EXPECT_EQ(Records[0].Info.getAllocCount(), 1U);
152   EXPECT_EQ(Records[1].Info.getAllocCount(), 2U);
153   EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false));
154   EXPECT_THAT(Records[0].CallStack[1], FrameContains("bar", 51U, 20U, true));
155 
156   EXPECT_THAT(Records[1].CallStack[0], FrameContains("baz", 5U, 30U, false));
157   EXPECT_THAT(Records[1].CallStack[1], FrameContains("qux", 5U, 10U, true));
158   EXPECT_THAT(Records[1].CallStack[2], FrameContains("foo", 5U, 30U, false));
159   EXPECT_THAT(Records[1].CallStack[3], FrameContains("bar", 51U, 20U, true));
160 }
161 
162 TEST(MemProf, PortableWrapper) {
163   MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
164                     /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
165                     /*dealloc_cpu=*/4);
166 
167   const auto Schema = getFullSchema();
168   PortableMemInfoBlock WriteBlock(Info);
169 
170   std::string Buffer;
171   llvm::raw_string_ostream OS(Buffer);
172   WriteBlock.serialize(Schema, OS);
173   OS.flush();
174 
175   PortableMemInfoBlock ReadBlock(
176       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));
177 
178   EXPECT_EQ(ReadBlock, WriteBlock);
179   // Here we compare directly with the actual counts instead of MemInfoBlock
180   // members. Since the MemInfoBlock struct is packed and the EXPECT_EQ macros
181   // take a reference to the params, this results in unaligned accesses.
182   EXPECT_EQ(1UL, ReadBlock.getAllocCount());
183   EXPECT_EQ(7ULL, ReadBlock.getTotalAccessCount());
184   EXPECT_EQ(3UL, ReadBlock.getAllocCpuId());
185 }
186 
187 } // namespace
188