* synced up.
*
\*===----------------------------------------------------------------------===*/
+#include <string.h>
#ifdef _MSC_VER
#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
(uint64_t)'o' << 24 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129)
// The version number of the raw binary format.
-#define MEMPROF_RAW_VERSION 2ULL
+#define MEMPROF_RAW_VERSION 3ULL
+
+#define MEMPROF_BUILDID_MAX_SIZE 32ULL
namespace llvm {
namespace memprof {
uint64_t StackOffset;
});
-
// A struct describing the information necessary to describe a /proc/maps
// segment entry for a particular binary/library identified by its build id.
PACKED(struct SegmentEntry {
uint64_t Start;
uint64_t End;
uint64_t Offset;
- // This field is unused until sanitizer procmaps support for build ids for
- // Linux-Elf is implemented.
- uint8_t BuildId[32] = {0};
+ uint64_t BuildIdSize;
+ uint8_t BuildId[MEMPROF_BUILDID_MAX_SIZE] = {0};
- SegmentEntry(uint64_t S, uint64_t E, uint64_t O) :
- Start(S), End(E), Offset(O) {}
+ // This constructor is only used in tests so don't set the BuildId.
+ SegmentEntry(uint64_t S, uint64_t E, uint64_t O)
+ : Start(S), End(E), Offset(O), BuildIdSize(0) {}
SegmentEntry(const SegmentEntry& S) {
Start = S.Start;
End = S.End;
Offset = S.Offset;
+ BuildIdSize = S.BuildIdSize;
+ memcpy(BuildId, S.BuildId, S.BuildIdSize);
}
SegmentEntry& operator=(const SegmentEntry& S) {
Start = S.Start;
End = S.End;
Offset = S.Offset;
+ BuildIdSize = S.BuildIdSize;
+ memcpy(BuildId, S.BuildId, S.BuildIdSize);
return *this;
}
bool operator==(const SegmentEntry& S) const {
- return Start == S.Start &&
- End == S.End &&
- Offset == S.Offset;
+ return Start == S.Start && End == S.End && Offset == S.Offset &&
+ BuildIdSize == S.BuildIdSize &&
+ memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0;
}
});
#include "sanitizer_common/sanitizer_allocator_checks.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_errno.h"
#include "sanitizer_common/sanitizer_file.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include <sched.h>
// memprof_rawprofile.h.
char *Buffer = nullptr;
- MemoryMappingLayout Layout(/*cache_enabled=*/true);
- u64 BytesSerialized = SerializeToRawProfile(MIBMap, Layout, Buffer);
+ __sanitizer::ListOfModules List;
+ List.init();
+ ArrayRef<LoadedModule> Modules(List.begin(), List.end());
+ u64 BytesSerialized = SerializeToRawProfile(MIBMap, Modules, Buffer);
CHECK(Buffer && BytesSerialized && "could not serialize to buffer");
report_file.Write(Buffer, BytesSerialized);
}
}
} // namespace
-u64 SegmentSizeBytes(MemoryMappingLayoutBase &Layout) {
+u64 SegmentSizeBytes(ArrayRef<LoadedModule> Modules) {
u64 NumSegmentsToRecord = 0;
- MemoryMappedSegment segment;
- for (Layout.Reset(); Layout.Next(&segment);)
- if (segment.IsReadable() && segment.IsExecutable())
- NumSegmentsToRecord++;
+ for (const auto &Module : Modules) {
+ for (const auto &Segment : Module.ranges()) {
+ if (Segment.executable)
+ NumSegmentsToRecord++;
+ }
+ }
return sizeof(u64) // A header which stores the number of records.
+ sizeof(SegmentEntry) * NumSegmentsToRecord;
// Start
// End
// Offset
-// BuildID 32B
+// UuidSize
+// Uuid 32B
// ----------
// ...
-void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout,
+void SerializeSegmentsToBuffer(ArrayRef<LoadedModule> Modules,
const u64 ExpectedNumBytes, char *&Buffer) {
char *Ptr = Buffer;
// Reserve space for the final count.
Ptr += sizeof(u64);
u64 NumSegmentsRecorded = 0;
- MemoryMappedSegment segment;
-
- for (Layout.Reset(); Layout.Next(&segment);) {
- if (segment.IsReadable() && segment.IsExecutable()) {
- // TODO: Record segment.uuid when it is implemented for Linux-Elf.
- SegmentEntry Entry(segment.start, segment.end, segment.offset);
- memcpy(Ptr, &Entry, sizeof(SegmentEntry));
- Ptr += sizeof(SegmentEntry);
- NumSegmentsRecorded++;
+
+ for (const auto &Module : Modules) {
+ for (const auto &Segment : Module.ranges()) {
+ if (Segment.executable) {
+ SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address());
+ CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE);
+ Entry.BuildIdSize = Module.uuid_size();
+ memcpy(Entry.BuildId, Module.uuid(), Module.uuid_size());
+ memcpy(Ptr, &Entry, sizeof(SegmentEntry));
+ Ptr += sizeof(SegmentEntry);
+ NumSegmentsRecorded++;
+ }
}
}
-
// Store the number of segments we recorded in the space we reserved.
*((u64 *)Buffer) = NumSegmentsRecorded;
CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
// ----------
// Optional Padding Bytes
// ...
-u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout,
+u64 SerializeToRawProfile(MIBMapTy &MIBMap, ArrayRef<LoadedModule> Modules,
char *&Buffer) {
// Each section size is rounded up to 8b since the first entry in each section
// is a u64 which holds the number of entries in the section by convention.
- const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Layout), 8);
+ const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Modules), 8);
Vector<u64> StackIds;
MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds));
sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes};
Ptr = WriteBytes(header, Ptr);
- SerializeSegmentsToBuffer(Layout, NumSegmentBytes, Ptr);
+ SerializeSegmentsToBuffer(Modules, NumSegmentBytes, Ptr);
Ptr += NumSegmentBytes;
SerializeMIBInfoToBuffer(MIBMap, StackIds, NumMIBInfoBytes, Ptr);
#define MEMPROF_RAWPROFILE_H_
#include "memprof_mibmap.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_common.h"
namespace __memprof {
// Serialize the in-memory representation of the memprof profile to the raw
// binary format. The format itself is documented memprof_rawprofile.cpp.
-u64 SerializeToRawProfile(MIBMapTy &BlockCache, MemoryMappingLayoutBase &Layout,
+u64 SerializeToRawProfile(MIBMapTy &BlockCache, ArrayRef<LoadedModule> Modules,
char *&Buffer);
} // namespace __memprof
using ::__memprof::MIBMapTy;
using ::__memprof::SerializeToRawProfile;
-using ::__sanitizer::MemoryMappedSegment;
-using ::__sanitizer::MemoryMappingLayoutBase;
using ::__sanitizer::StackDepotPut;
using ::__sanitizer::StackTrace;
using ::llvm::memprof::MemInfoBlock;
-using ::testing::_;
-using ::testing::Action;
-using ::testing::DoAll;
-using ::testing::Return;
-using ::testing::SetArgPointee;
-
-class MockMemoryMappingLayout final : public MemoryMappingLayoutBase {
-public:
- MOCK_METHOD(bool, Next, (MemoryMappedSegment *), (override));
- MOCK_METHOD(void, Reset, (), (override));
-};
uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uint64_t StackPCBegin,
MIBMapTy &FakeMap) {
}
TEST(MemProf, Basic) {
- MockMemoryMappingLayout Layout;
- MemoryMappedSegment FakeSegment;
- memset(&FakeSegment, 0, sizeof(FakeSegment));
- FakeSegment.start = 0x10;
- FakeSegment.end = 0x20;
- FakeSegment.offset = 0x10;
- uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
- memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize);
- FakeSegment.protection =
- __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead;
-
- const Action<bool(MemoryMappedSegment *)> SetSegment =
- DoAll(SetArgPointee<0>(FakeSegment), Return(true));
- EXPECT_CALL(Layout, Next(_))
- .WillOnce(SetSegment)
- .WillOnce(Return(false))
- .WillOnce(SetSegment)
- .WillRepeatedly(Return(false));
-
- EXPECT_CALL(Layout, Reset).Times(2);
+ __sanitizer::LoadedModule FakeModule;
+ FakeModule.addAddressRange(/*begin=*/0x10, /*end=*/0x20, /*executable=*/true,
+ /*writable=*/false, /*name=*/"");
+ const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
+ FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE);
+ __sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule,
+ (&FakeModule) + 1);
MIBMapTy FakeMap;
MemInfoBlock FakeMIB;
FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap);
char *Ptr = nullptr;
- uint64_t NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr);
+ uint64_t NumBytes = SerializeToRawProfile(FakeMap, Modules, Ptr);
const char *Buffer = Ptr;
ASSERT_GT(NumBytes, 0ULL);
// Should be equal to the size of the raw profile header.
EXPECT_EQ(SegmentOffset, 48ULL);
- // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry
+ // We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry
// in memprof_rawprofile.cpp.
- EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL);
+ EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL);
- EXPECT_EQ(MIBOffset, 112ULL);
+ EXPECT_EQ(MIBOffset, 120ULL);
// We expect 2 mib entry, 8b for the count and sizeof(uint64_t) +
// sizeof(MemInfoBlock) contains stack id + MeminfoBlock.
EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock)));
- EXPECT_EQ(StackOffset, 336ULL);
+ EXPECT_EQ(StackOffset, 408ULL);
// We expect 2 stack entries, with 5 frames - 8b for total count,
// 2 * (8b for id, 8b for frame count and 5*8b for fake frames).
// Since this is the last section, there may be additional padding at the end
EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
// ============= Check contents.
- // The Uuid field is not yet populated on Linux-Elf by the sanitizer procmaps
- // library, so we expect it to be filled with 0 for now.
- unsigned char ExpectedSegmentBytes[64] = {
- 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries
- 0x10, 0, 0, 0, 0, 0, 0, 0, // Start
- 0x20, 0, 0, 0, 0, 0, 0, 0, // End
- 0x10, 0, 0, 0, 0, 0, 0, 0, // Offset
- 0x0, // Uuid
+ unsigned char ExpectedSegmentBytes[72] = {
+ 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries
+ 0x10, 0, 0, 0, 0, 0, 0, 0, // Start
+ 0x20, 0, 0, 0, 0, 0, 0, 0, // End
+ 0x0, 0, 0, 0, 0, 0, 0, 0, // Offset
+ 0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize
+ 0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid
};
- EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0);
+ EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 72), 0);
// Check that the number of entries is 2.
EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL);
sizeof(ExpectedStackBytes[1])),
0);
}
-
} // namespace
* synced up.
*
\*===----------------------------------------------------------------------===*/
+#include <string.h>
#ifdef _MSC_VER
#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
(uint64_t)'o' << 24 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129)
// The version number of the raw binary format.
-#define MEMPROF_RAW_VERSION 2ULL
+#define MEMPROF_RAW_VERSION 3ULL
+
+#define MEMPROF_BUILDID_MAX_SIZE 32ULL
namespace llvm {
namespace memprof {
uint64_t StackOffset;
});
-
// A struct describing the information necessary to describe a /proc/maps
// segment entry for a particular binary/library identified by its build id.
PACKED(struct SegmentEntry {
uint64_t Start;
uint64_t End;
uint64_t Offset;
- // This field is unused until sanitizer procmaps support for build ids for
- // Linux-Elf is implemented.
- uint8_t BuildId[32] = {0};
+ uint64_t BuildIdSize;
+ uint8_t BuildId[MEMPROF_BUILDID_MAX_SIZE] = {0};
- SegmentEntry(uint64_t S, uint64_t E, uint64_t O) :
- Start(S), End(E), Offset(O) {}
+ // This constructor is only used in tests so don't set the BuildId.
+ SegmentEntry(uint64_t S, uint64_t E, uint64_t O)
+ : Start(S), End(E), Offset(O), BuildIdSize(0) {}
SegmentEntry(const SegmentEntry& S) {
Start = S.Start;
End = S.End;
Offset = S.Offset;
+ BuildIdSize = S.BuildIdSize;
+ memcpy(BuildId, S.BuildId, S.BuildIdSize);
}
SegmentEntry& operator=(const SegmentEntry& S) {
Start = S.Start;
End = S.End;
Offset = S.Offset;
+ BuildIdSize = S.BuildIdSize;
+ memcpy(BuildId, S.BuildId, S.BuildIdSize);
return *this;
}
bool operator==(const SegmentEntry& S) const {
- return Start == S.Start &&
- End == S.End &&
- Offset == S.Offset;
+ return Start == S.Start && End == S.End && Offset == S.Offset &&
+ BuildIdSize == S.BuildIdSize &&
+ memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0;
}
});
}
std::string getBuildIdString(const SegmentEntry &Entry) {
- constexpr size_t Size = sizeof(Entry.BuildId) / sizeof(uint8_t);
- constexpr uint8_t Zeros[Size] = {0};
// If the build id is unset print a helpful string instead of all zeros.
- if (memcmp(Entry.BuildId, Zeros, Size) == 0)
+ if (Entry.BuildIdSize == 0)
return "<None>";
std::string Str;
raw_string_ostream OS(Str);
- for (size_t I = 0; I < Size; I++) {
+ for (size_t I = 0; I < Entry.BuildIdSize; I++) {
OS << format_hex_no_prefix(Entry.BuildId[I], 2);
}
return OS.str();
INPUTS["inline"]="INLINE"
INPUTS["multi"]="MULTI"
INPUTS["pic"]="BASIC;-pie"
+INPUTS["buildid"]="BASIC;-Wl,-build-id=sha1"
for name in "${!INPUTS[@]}"; do
IFS=";" read -r src flags <<< "${INPUTS[$name]}"
CHECK: MemprofProfile:
CHECK-NEXT: Summary:
-CHECK-NEXT: Version: 2
+CHECK-NEXT: Version: 3
CHECK-NEXT: NumSegments: {{[0-9]+}}
CHECK-NEXT: NumMibInfo: 2
CHECK-NEXT: NumAllocFunctions: 1
CHECK-NEXT: NumStackOffsets: 2
CHECK-NEXT: Segments:
CHECK-NEXT: -
-CHECK-NEXT: BuildId: <None>
-CHECK-NEXT: Start: 0x{{[0-9]+}}
-CHECK-NEXT: End: 0x{{[0-9]+}}
-CHECK-NEXT: Offset: 0x{{[0-9]+}}
+CHECK-NEXT: BuildId: {{[[:xdigit:]]+}}
+CHECK-NEXT: Start: 0x{{[[:xdigit:]]+}}
+CHECK-NEXT: End: 0x{{[[:xdigit:]]+}}
+CHECK-NEXT: Offset: 0x{{[[:xdigit:]]+}}
CHECK-NEXT: -
CHECK: Records:
--- /dev/null
+REQUIRES: x86_64-linux
+
+To update the inputs used below run Inputs/update_memprof_inputs.sh /path/to/updated/clang
+RUN: llvm-readelf --notes %p/Inputs/buildid.memprofexe > %t1.txt
+RUN: llvm-profdata show --memory %p/Inputs/buildid.memprofraw --profiled-binary %p/Inputs/buildid.memprofexe -o - > %t2.txt
+RUN: cat %t1.txt %t2.txt | FileCheck %s
+
+COM: First extract the id from the llvm-readelf output.
+CHECK: Build ID: [[ID:[[:xdigit:]]+]]
+
+COM: Then match it with the profdata output.
+CHECK: BuildId: {{.*}}[[ID]]
CHECK: MemprofProfile:
CHECK-NEXT: Summary:
-CHECK-NEXT: Version: 2
+CHECK-NEXT: Version: 3
CHECK-NEXT: NumSegments: {{[0-9]+}}
CHECK-NEXT: NumMibInfo: 2
CHECK-NEXT: NumAllocFunctions: 2
CHECK-NEXT: NumStackOffsets: 1
CHECK-NEXT: Segments:
CHECK-NEXT: -
-CHECK-NEXT: BuildId: <None>
-CHECK-NEXT: Start: 0x{{[0-9]+}}
-CHECK-NEXT: End: 0x{{[0-9]+}}
-CHECK-NEXT: Offset: 0x{{[0-9]+}}
+CHECK-NEXT: BuildId: {{[[:xdigit:]]+}}
+CHECK-NEXT: Start: 0x{{[[:xdigit:]]+}}
+CHECK-NEXT: End: 0x{{[[:xdigit:]]+}}
+CHECK-NEXT: Offset: 0x{{[[:xdigit:]]+}}
CHECK-NEXT: -
CHECK: Records:
CHECK: MemprofProfile:
CHECK-NEXT: Summary:
-CHECK-NEXT: Version: 2
+CHECK-NEXT: Version: 3
CHECK-NEXT: NumSegments: {{[0-9]+}}
CHECK-NEXT: NumMibInfo: 2
CHECK-NEXT: NumAllocFunctions: 1