diff --git a/compiler-rt/include/profile/MemProfData.inc b/compiler-rt/include/profile/MemProfData.inc --- a/compiler-rt/include/profile/MemProfData.inc +++ b/compiler-rt/include/profile/MemProfData.inc @@ -19,6 +19,7 @@ * synced up. * \*===----------------------------------------------------------------------===*/ +#include #ifdef _MSC_VER #define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop)) @@ -32,7 +33,9 @@ (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 { @@ -46,37 +49,42 @@ 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}; + // 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) {} + 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; + Offset == S.Offset && + BuildIdSize == S.BuildIdSize && + memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0; } }); diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp --- a/compiler-rt/lib/memprof/memprof_allocator.cpp +++ b/compiler-rt/lib/memprof/memprof_allocator.cpp @@ -23,11 +23,11 @@ #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 @@ -295,8 +295,9 @@ // memprof_rawprofile.h. char *Buffer = nullptr; - MemoryMappingLayout Layout(/*cache_enabled=*/true); - u64 BytesSerialized = SerializeToRawProfile(MIBMap, Layout, Buffer); + __sanitizer::ListOfModules Modules; + Modules.init(); + u64 BytesSerialized = SerializeToRawProfile(MIBMap, Modules, Buffer); CHECK(Buffer && BytesSerialized && "could not serialize to buffer"); report_file.Write(Buffer, BytesSerialized); } diff --git a/compiler-rt/lib/memprof/memprof_rawprofile.h b/compiler-rt/lib/memprof/memprof_rawprofile.h --- a/compiler-rt/lib/memprof/memprof_rawprofile.h +++ b/compiler-rt/lib/memprof/memprof_rawprofile.h @@ -2,13 +2,13 @@ #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, - char *&Buffer); +u64 SerializeToRawProfile(MIBMapTy &BlockCache, + __sanitizer::ListOfModules &Modules, char *&Buffer); } // namespace __memprof #endif // MEMPROF_RAWPROFILE_H_ diff --git a/compiler-rt/lib/memprof/memprof_rawprofile.cpp b/compiler-rt/lib/memprof/memprof_rawprofile.cpp --- a/compiler-rt/lib/memprof/memprof_rawprofile.cpp +++ b/compiler-rt/lib/memprof/memprof_rawprofile.cpp @@ -33,12 +33,14 @@ } } // namespace -u64 SegmentSizeBytes(MemoryMappingLayoutBase &Layout) { +u64 SegmentSizeBytes(__sanitizer::ListOfModules &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; @@ -51,28 +53,31 @@ // Start // End // Offset -// BuildID 32B +// UuidSize +// Uuid 32B // ---------- // ... -void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout, +void SerializeSegmentsToBuffer(__sanitizer::ListOfModules &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(Ptr - Buffer) && @@ -198,11 +203,11 @@ // ---------- // Optional Padding Bytes // ... -u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout, +u64 SerializeToRawProfile(MIBMapTy &MIBMap, __sanitizer::ListOfModules &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 StackIds; MIBMap.ForEach(RecordStackId, reinterpret_cast(&StackIds)); @@ -232,7 +237,7 @@ sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes}; Ptr = WriteBytes(header, Ptr); - SerializeSegmentsToBuffer(Layout, NumSegmentBytes, Ptr); + SerializeSegmentsToBuffer(Modules, NumSegmentBytes, Ptr); Ptr += NumSegmentBytes; SerializeMIBInfoToBuffer(MIBMap, StackIds, NumMIBInfoBytes, Ptr); diff --git a/compiler-rt/test/memprof/TestCases/memprof_basic.c b/compiler-rt/test/memprof/TestCases/memprof_basic.c --- a/compiler-rt/test/memprof/TestCases/memprof_basic.c +++ b/compiler-rt/test/memprof/TestCases/memprof_basic.c @@ -21,17 +21,17 @@ // 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: -// 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: diff --git a/compiler-rt/test/memprof/TestCases/memprof_inline.c b/compiler-rt/test/memprof/TestCases/memprof_inline.c --- a/compiler-rt/test/memprof/TestCases/memprof_inline.c +++ b/compiler-rt/test/memprof/TestCases/memprof_inline.c @@ -26,17 +26,17 @@ // 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: -// 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: diff --git a/compiler-rt/test/memprof/TestCases/memprof_multi.c b/compiler-rt/test/memprof/TestCases/memprof_multi.c --- a/compiler-rt/test/memprof/TestCases/memprof_multi.c +++ b/compiler-rt/test/memprof/TestCases/memprof_multi.c @@ -21,7 +21,7 @@ // COM: We expect 2 MIB entries, 1 each for the malloc calls in the program. // 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 diff --git a/llvm/include/llvm/ProfileData/MemProfData.inc b/llvm/include/llvm/ProfileData/MemProfData.inc --- a/llvm/include/llvm/ProfileData/MemProfData.inc +++ b/llvm/include/llvm/ProfileData/MemProfData.inc @@ -19,6 +19,7 @@ * synced up. * \*===----------------------------------------------------------------------===*/ +#include #ifdef _MSC_VER #define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop)) @@ -32,7 +33,9 @@ (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 { @@ -46,37 +49,42 @@ 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}; + // 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) {} + 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; + Offset == S.Offset && + BuildIdSize == S.BuildIdSize && + memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0; } }); diff --git a/llvm/lib/ProfileData/RawMemProfReader.cpp b/llvm/lib/ProfileData/RawMemProfReader.cpp --- a/llvm/lib/ProfileData/RawMemProfReader.cpp +++ b/llvm/lib/ProfileData/RawMemProfReader.cpp @@ -154,15 +154,13 @@ } 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 ""; 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();