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