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 "gmock/gmock.h"
13 #include "gtest/gtest.h"
14 
15 #include <initializer_list>
16 
17 namespace {
18 
19 using ::llvm::DIGlobal;
20 using ::llvm::DIInliningInfo;
21 using ::llvm::DILineInfo;
22 using ::llvm::DILineInfoSpecifier;
23 using ::llvm::DILocal;
24 using ::llvm::memprof::CallStackMap;
25 using ::llvm::memprof::MemInfoBlock;
26 using ::llvm::memprof::MemProfRecord;
27 using ::llvm::memprof::RawMemProfReader;
28 using ::llvm::memprof::SegmentEntry;
29 using ::llvm::object::SectionedAddress;
30 using ::llvm::symbolize::SymbolizableModule;
31 using ::testing::Return;
32 
33 class MockSymbolizer : public SymbolizableModule {
34 public:
35   MOCK_CONST_METHOD3(symbolizeInlinedCode,
36                      DIInliningInfo(SectionedAddress, DILineInfoSpecifier,
37                                     bool));
38   // Most of the methods in the interface are unused. We only mock the
39   // method that we expect to be called from the memprof reader.
40   virtual DILineInfo symbolizeCode(SectionedAddress, DILineInfoSpecifier,
41                                    bool) const {
42     llvm_unreachable("unused");
43   }
44   virtual DIGlobal symbolizeData(SectionedAddress) const {
45     llvm_unreachable("unused");
46   }
47   virtual std::vector<DILocal> symbolizeFrame(SectionedAddress) const {
48     llvm_unreachable("unused");
49   }
50   virtual bool isWin32Module() const { llvm_unreachable("unused"); }
51   virtual uint64_t getModulePreferredBase() const {
52     llvm_unreachable("unused");
53   }
54 };
55 
56 struct MockInfo {
57   std::string FunctionName;
58   uint32_t Line;
59   uint32_t StartLine;
60   uint32_t Column;
61 };
62 DIInliningInfo makeInliningInfo(std::initializer_list<MockInfo> MockFrames) {
63   DIInliningInfo Result;
64   for (const auto &Item : MockFrames) {
65     DILineInfo Frame;
66     Frame.FunctionName = Item.FunctionName;
67     Frame.Line = Item.Line;
68     Frame.StartLine = Item.StartLine;
69     Frame.Column = Item.Column;
70     Result.addFrame(Frame);
71   }
72   return Result;
73 }
74 
75 llvm::SmallVector<SegmentEntry, 4> makeSegments() {
76   llvm::SmallVector<SegmentEntry, 4> Result;
77   // Mimic an entry for a non position independent executable.
78   Result.emplace_back(0x0, 0x40000, 0x0);
79   return Result;
80 }
81 
82 const DILineInfoSpecifier specifier() {
83   return DILineInfoSpecifier(
84       DILineInfoSpecifier::FileLineInfoKind::RawValue,
85       DILineInfoSpecifier::FunctionNameKind::LinkageName);
86 }
87 
88 MATCHER_P4(FrameContains, Function, LineOffset, Column, Inline, "") {
89   const std::string ExpectedHash = std::to_string(llvm::MD5Hash(Function));
90   if (arg.Function != ExpectedHash) {
91     *result_listener << "Hash mismatch";
92     return false;
93   }
94   if (arg.LineOffset == LineOffset && arg.Column == Column &&
95       arg.IsInlineFrame == Inline) {
96     return true;
97   }
98   *result_listener << "LineOffset, Column or Inline mismatch";
99   return false;
100 }
101 
102 TEST(MemProf, FillsValue) {
103   std::unique_ptr<MockSymbolizer> Symbolizer(new MockSymbolizer());
104 
105   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x2000},
106                                                 specifier(), false))
107       .Times(2)
108       .WillRepeatedly(Return(makeInliningInfo({
109           {"foo", 10, 5, 30},
110           {"bar", 201, 150, 20},
111       })));
112 
113   EXPECT_CALL(*Symbolizer, symbolizeInlinedCode(SectionedAddress{0x6000},
114                                                 specifier(), false))
115       .Times(1)
116       .WillRepeatedly(Return(makeInliningInfo({
117           {"baz", 10, 5, 30},
118           {"qux.llvm.12345", 75, 70, 10},
119       })));
120 
121   CallStackMap CSM;
122   CSM[0x1] = {0x2000};
123   CSM[0x2] = {0x6000, 0x2000};
124 
125   llvm::MapVector<uint64_t, MemInfoBlock> Prof;
126   Prof[0x1].alloc_count = 1;
127   Prof[0x2].alloc_count = 2;
128 
129   auto Seg = makeSegments();
130 
131   RawMemProfReader Reader(std::move(Symbolizer), Seg, Prof, CSM);
132 
133   std::vector<MemProfRecord> Records;
134   for (const MemProfRecord &R : Reader) {
135     Records.push_back(R);
136   }
137   EXPECT_EQ(Records.size(), 2U);
138 
139   EXPECT_EQ(Records[0].Info.alloc_count, 1U);
140   EXPECT_EQ(Records[1].Info.alloc_count, 2U);
141   EXPECT_THAT(Records[0].CallStack[0], FrameContains("foo", 5U, 30U, false));
142   EXPECT_THAT(Records[0].CallStack[1], FrameContains("bar", 51U, 20U, true));
143 
144   EXPECT_THAT(Records[1].CallStack[0], FrameContains("baz", 5U, 30U, false));
145   EXPECT_THAT(Records[1].CallStack[1], FrameContains("qux", 5U, 10U, true));
146   EXPECT_THAT(Records[1].CallStack[2], FrameContains("foo", 5U, 30U, false));
147   EXPECT_THAT(Records[1].CallStack[3], FrameContains("bar", 51U, 20U, true));
148 }
149 
150 } // namespace
151