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/IR/Function.h"
7 #include "llvm/Object/ObjectFile.h"
8 #include "llvm/ProfileData/InstrProf.h"
9 #include "llvm/ProfileData/MemProfData.inc"
10 #include "llvm/ProfileData/RawMemProfReader.h"
11 #include "llvm/Support/Error.h"
12 #include "llvm/Support/MD5.h"
13 #include "llvm/Support/raw_ostream.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 #include <initializer_list>
18 
19 namespace {
20 
21 using ::llvm::DIGlobal;
22 using ::llvm::DIInliningInfo;
23 using ::llvm::DILineInfo;
24 using ::llvm::DILineInfoSpecifier;
25 using ::llvm::DILocal;
26 using ::llvm::memprof::CallStackMap;
27 using ::llvm::memprof::MemInfoBlock;
28 using ::llvm::memprof::MemProfRecord;
29 using ::llvm::memprof::MemProfSchema;
30 using ::llvm::memprof::Meta;
31 using ::llvm::memprof::PortableMemInfoBlock;
32 using ::llvm::memprof::RawMemProfReader;
33 using ::llvm::memprof::SegmentEntry;
34 using ::llvm::object::SectionedAddress;
35 using ::llvm::symbolize::SymbolizableModule;
36 using ::testing::Return;
37 
38 class MockSymbolizer : public SymbolizableModule {
39 public:
40   MOCK_CONST_METHOD3(symbolizeInlinedCode,
41                      DIInliningInfo(SectionedAddress, DILineInfoSpecifier,
42                                     bool));
43   // Most of the methods in the interface are unused. We only mock the
44   // method that we expect to be called from the memprof reader.
45   virtual DILineInfo symbolizeCode(SectionedAddress, DILineInfoSpecifier,
46                                    bool) const {
47     llvm_unreachable("unused");
48   }
49   virtual DIGlobal symbolizeData(SectionedAddress) const {
50     llvm_unreachable("unused");
51   }
52   virtual std::vector<DILocal> symbolizeFrame(SectionedAddress) const {
53     llvm_unreachable("unused");
54   }
55   virtual bool isWin32Module() const { llvm_unreachable("unused"); }
56   virtual uint64_t getModulePreferredBase() const {
57     llvm_unreachable("unused");
58   }
59 };
60 
61 struct MockInfo {
62   std::string FunctionName;
63   uint32_t Line;
64   uint32_t StartLine;
65   uint32_t Column;
66   std::string FileName = "valid/path.cc";
67 };
68 DIInliningInfo makeInliningInfo(std::initializer_list<MockInfo> MockFrames) {
69   DIInliningInfo Result;
70   for (const auto &Item : MockFrames) {
71     DILineInfo Frame;
72     Frame.FunctionName = Item.FunctionName;
73     Frame.Line = Item.Line;
74     Frame.StartLine = Item.StartLine;
75     Frame.Column = Item.Column;
76     Frame.FileName = Item.FileName;
77     Result.addFrame(Frame);
78   }
79   return Result;
80 }
81 
82 llvm::SmallVector<SegmentEntry, 4> makeSegments() {
83   llvm::SmallVector<SegmentEntry, 4> Result;
84   // Mimic an entry for a non position independent executable.
85   Result.emplace_back(0x0, 0x40000, 0x0);
86   return Result;
87 }
88 
89 const DILineInfoSpecifier specifier() {
90   return DILineInfoSpecifier(
91       DILineInfoSpecifier::FileLineInfoKind::RawValue,
92       DILineInfoSpecifier::FunctionNameKind::LinkageName);
93 }
94 
95 MATCHER_P4(FrameContains, FunctionName, LineOffset, Column, Inline, "") {
96   const uint64_t ExpectedHash = llvm::Function::getGUID(FunctionName);
97   if (arg.Function != ExpectedHash) {
98     *result_listener << "Hash mismatch";
99     return false;
100   }
101   if (arg.LineOffset == LineOffset && arg.Column == Column &&
102       arg.IsInlineFrame == Inline) {
103     return true;
104   }
105   *result_listener << "LineOffset, Column or Inline mismatch";
106   return false;
107 }
108 
109 MATCHER_P(EqualsRecord, Want, "") {
110   if (arg == Want)
111     return true;
112 
113   std::string Explanation;
114   llvm::raw_string_ostream OS(Explanation);
115   OS << "\n Want: \n";
116   Want.print(OS);
117   OS << "\n Got: \n";
118   arg.print(OS);
119   OS.flush();
120 
121   *result_listener << Explanation;
122   return false;
123 }
124 
125 MemProfSchema getFullSchema() {
126   MemProfSchema Schema;
127 #define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name);
128 #include "llvm/ProfileData/MIBEntryDef.inc"
129 #undef MIBEntryDef
130   return Schema;
131 }
132 
133 TEST(MemProf, FillsValue) {
134   std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());
135 
136   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
137                                                 specifier(), false))
138       .Times(1) // Only once since we cache the result for future lookups.
139       .WillRepeatedly(Return(makeInliningInfo({
140           {"foo", 10, 5, 30},
141           {"bar", 201, 150, 20},
142       })));
143 
144   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x6000},
145                                                 specifier(), false))
146       .Times(1)
147       .WillRepeatedly(Return(makeInliningInfo({
148           {"baz", 10, 5, 30},
149           {"qux.llvm.12345", 75, 70, 10},
150       })));
151 
152   CallStackMap CSM;
153   CSM[0x1] = {0x2000};
154   CSM[0x2] = {0x6000, 0x2000};
155 
156   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
157   Prof[0x1].AllocCount = 1;
158   Prof[0x2].AllocCount = 2;
159 
160   auto Seg = makeSegments();
161 
162   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);
163 
164   std::vector<MemProfRecord> Records;
165   for (const MemProfRecord &R : Reader) {
166     Records.push_back(R);
167   }
168   EXPECT_EQ(Records.size(), 2U);
169 
170   EXPECT_EQ(Records[0].Info.getAllocCount(), 1U);
171   EXPECT_EQ(Records[1].Info.getAllocCount(), 2U);
172   EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false));
173   EXPECT_THAT(Records[0].CallStack[1], FrameContains("bar", 51U, 20U, true));
174 
175   EXPECT_THAT(Records[1].CallStack[0], FrameContains("baz", 5U, 30U, false));
176   EXPECT_THAT(Records[1].CallStack[1], FrameContains("qux", 5U, 10U, true));
177   EXPECT_THAT(Records[1].CallStack[2], FrameContains("foo", 5U, 30U, false));
178   EXPECT_THAT(Records[1].CallStack[3], FrameContains("bar", 51U, 20U, true));
179 }
180 
181 TEST(MemProf, PortableWrapper) {
182   MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
183                     /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
184                     /*dealloc_cpu=*/4);
185 
186   const auto Schema = getFullSchema();
187   PortableMemInfoBlock WriteBlock(Info);
188 
189   std::string Buffer;
190   llvm::raw_string_ostream OS(Buffer);
191   WriteBlock.serialize(Schema, OS);
192   OS.flush();
193 
194   PortableMemInfoBlock ReadBlock(
195       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));
196 
197   EXPECT_EQ(ReadBlock, WriteBlock);
198   // Here we compare directly with the actual counts instead of MemInfoBlock
199   // members. Since the MemInfoBlock struct is packed and the EXPECT_EQ macros
200   // take a reference to the params, this results in unaligned accesses.
201   EXPECT_EQ(1UL, ReadBlock.getAllocCount());
202   EXPECT_EQ(7ULL, ReadBlock.getTotalAccessCount());
203   EXPECT_EQ(3UL, ReadBlock.getAllocCpuId());
204 }
205 
206 TEST(MemProf, RecordSerializationRoundTrip) {
207   const MemProfSchema Schema = getFullSchema();
208 
209   llvm::SmallVector<MemProfRecord, 3> Records;
210   MemProfRecord MR;
211 
212   MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000,
213                     /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3,
214                     /*dealloc_cpu=*/4);
215 
216   MR.Info = PortableMemInfoBlock(Info);
217   MR.CallStack.push_back({0x123, 1, 2, false});
218   MR.CallStack.push_back({0x345, 3, 4, false});
219   Records.push_back(MR);
220 
221   MR.clear();
222   MR.Info = PortableMemInfoBlock(Info);
223   MR.CallStack.push_back({0x567, 5, 6, false});
224   MR.CallStack.push_back({0x789, 7, 8, false});
225   Records.push_back(MR);
226 
227   std::string Buffer;
228   llvm::raw_string_ostream OS(Buffer);
229   serializeRecords(Records, Schema, OS);
230   OS.flush();
231 
232   const llvm::SmallVector<MemProfRecord, 4> GotRecords = deserializeRecords(
233       Schema, reinterpret_cast<const unsigned char *>(Buffer.data()));
234 
235   ASSERT_TRUE(!GotRecords.empty());
236   EXPECT_EQ(GotRecords.size(), Records.size());
237   EXPECT_THAT(GotRecords[0], EqualsRecord(Records[0]));
238   EXPECT_THAT(GotRecords[1], EqualsRecord(Records[1]));
239 }
240 
241 TEST(MemProf, SymbolizationFilter) {
242   std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());
243 
244   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x1000},
245                                                 specifier(), false))
246       .Times(1) // once since we don't lookup invalid PCs repeatedly.
247       .WillRepeatedly(Return(makeInliningInfo({
248           {"malloc", 70, 57, 3, "memprof/memprof_malloc_linux.cpp"},
249       })));
250 
251   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
252                                                 specifier(), false))
253       .Times(1) // once since we don't lookup invalid PCs repeatedly.
254       .WillRepeatedly(Return(makeInliningInfo({
255           {"new", 70, 57, 3, "memprof/memprof_new_delete.cpp"},
256       })));
257 
258   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x3000},
259                                                 specifier(), false))
260       .Times(1) // once since we don't lookup invalid PCs repeatedly.
261       .WillRepeatedly(Return(makeInliningInfo({
262           {DILineInfo::BadString, 0, 0, 0},
263       })));
264 
265   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x4000},
266                                                 specifier(), false))
267       .Times(1)
268       .WillRepeatedly(Return(makeInliningInfo({
269           {"foo", 10, 5, 30},
270       })));
271 
272   CallStackMap CSM;
273   CSM[0x1] = {0x1000, 0x2000, 0x3000, 0x4000};
274   // This entry should be dropped since all PCs are either not
275   // symbolizable or belong to the runtime.
276   CSM[0x2] = {0x1000, 0x2000};
277 
278   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
279   Prof[0x1].AllocCount = 1;
280   Prof[0x2].AllocCount = 1;
281 
282   auto Seg = makeSegments();
283 
284   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);
285 
286   std::vector<MemProfRecord> Records;
287   for (const MemProfRecord &R : Reader) {
288     Records.push_back(R);
289   }
290   ASSERT_EQ(Records.size(), 1U);
291   ASSERT_EQ(Records[0].CallStack.size(), 1U);
292   EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false));
293 }
294 } // namespace
295