forked from OSchip/llvm-project
[XRay][compiler-rt] Profiling Mode: Include file header in buffers
Summary: This change provides access to the file header even in the in-memory buffer processing. This allows in-memory processing of the buffers to also check the version, and the format, of the profile data. Reviewers: eizan, kpw Reviewed By: eizan Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D50037 llvm-svn: 338347
This commit is contained in:
parent
3587150fcb
commit
3bd20d4605
|
@ -15,6 +15,8 @@
|
||||||
#include "xray_profile_collector.h"
|
#include "xray_profile_collector.h"
|
||||||
#include "xray_profiling_flags.h"
|
#include "xray_profiling_flags.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -24,6 +26,29 @@ namespace {
|
||||||
|
|
||||||
static constexpr auto kHeaderSize = 16u;
|
static constexpr auto kHeaderSize = 16u;
|
||||||
|
|
||||||
|
constexpr uptr ExpectedProfilingVersion = 0x20180424;
|
||||||
|
|
||||||
|
struct ExpectedProfilingFileHeader {
|
||||||
|
const u64 MagicBytes = 0x7872617970726f66; // Identifier for XRay profiling
|
||||||
|
// files 'xrayprof' in hex.
|
||||||
|
const uptr Version = ExpectedProfilingVersion;
|
||||||
|
uptr Timestamp = 0;
|
||||||
|
uptr PID = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ValidateFileHeaderBlock(XRayBuffer B) {
|
||||||
|
ASSERT_NE(static_cast<const void *>(B.Data), nullptr);
|
||||||
|
ASSERT_EQ(B.Size, sizeof(ExpectedProfilingFileHeader));
|
||||||
|
typename std::aligned_storage<sizeof(ExpectedProfilingFileHeader)>::type
|
||||||
|
FileHeaderStorage;
|
||||||
|
ExpectedProfilingFileHeader ExpectedHeader;
|
||||||
|
std::memcpy(&FileHeaderStorage, B.Data, B.Size);
|
||||||
|
auto &FileHeader =
|
||||||
|
*reinterpret_cast<ExpectedProfilingFileHeader *>(&FileHeaderStorage);
|
||||||
|
ASSERT_EQ(ExpectedHeader.MagicBytes, FileHeader.MagicBytes);
|
||||||
|
ASSERT_EQ(ExpectedHeader.Version, FileHeader.Version);
|
||||||
|
}
|
||||||
|
|
||||||
void ValidateBlock(XRayBuffer B) {
|
void ValidateBlock(XRayBuffer B) {
|
||||||
profilingFlags()->setDefaults();
|
profilingFlags()->setDefaults();
|
||||||
ASSERT_NE(static_cast<const void *>(B.Data), nullptr);
|
ASSERT_NE(static_cast<const void *>(B.Data), nullptr);
|
||||||
|
@ -107,9 +132,13 @@ TEST(profileCollectorServiceTest, PostSerializeCollect) {
|
||||||
// Then we serialize the data.
|
// Then we serialize the data.
|
||||||
profileCollectorService::serialize();
|
profileCollectorService::serialize();
|
||||||
|
|
||||||
// Then we go through a single buffer to see whether we're getting the data we
|
// Then we go through two buffers to see whether we're getting the data we
|
||||||
// expect.
|
// expect. The first block must always be as large as a file header, which
|
||||||
|
// will have a fixed size.
|
||||||
auto B = profileCollectorService::nextBuffer({nullptr, 0});
|
auto B = profileCollectorService::nextBuffer({nullptr, 0});
|
||||||
|
ValidateFileHeaderBlock(B);
|
||||||
|
|
||||||
|
B = profileCollectorService::nextBuffer(B);
|
||||||
ValidateBlock(B);
|
ValidateBlock(B);
|
||||||
u32 BlockSize;
|
u32 BlockSize;
|
||||||
u32 BlockNum;
|
u32 BlockNum;
|
||||||
|
@ -169,6 +198,9 @@ TEST(profileCollectorServiceTest, PostSerializeCollectMultipleThread) {
|
||||||
|
|
||||||
// Ensure that we see two buffers.
|
// Ensure that we see two buffers.
|
||||||
auto B = profileCollectorService::nextBuffer({nullptr, 0});
|
auto B = profileCollectorService::nextBuffer({nullptr, 0});
|
||||||
|
ValidateFileHeaderBlock(B);
|
||||||
|
|
||||||
|
B = profileCollectorService::nextBuffer(B);
|
||||||
ValidateBlock(B);
|
ValidateBlock(B);
|
||||||
|
|
||||||
B = profileCollectorService::nextBuffer(B);
|
B = profileCollectorService::nextBuffer(B);
|
||||||
|
|
|
@ -37,6 +37,19 @@ struct ProfileBuffer {
|
||||||
size_t Size;
|
size_t Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Current version of the profile format.
|
||||||
|
constexpr u64 XRayProfilingVersion = 0x20180424;
|
||||||
|
|
||||||
|
// Identifier for XRay profiling files 'xrayprof' in hex.
|
||||||
|
constexpr u64 XRayMagicBytes = 0x7872617970726f66;
|
||||||
|
|
||||||
|
struct XRayProfilingFileHeader {
|
||||||
|
const u64 MagicBytes = XRayMagicBytes;
|
||||||
|
const u64 Version = XRayProfilingVersion;
|
||||||
|
u64 Timestamp = 0; // System time in nanoseconds.
|
||||||
|
u64 PID = 0; // Process ID.
|
||||||
|
};
|
||||||
|
|
||||||
struct BlockHeader {
|
struct BlockHeader {
|
||||||
u32 BlockSize;
|
u32 BlockSize;
|
||||||
u32 BlockNum;
|
u32 BlockNum;
|
||||||
|
@ -302,7 +315,22 @@ XRayBuffer nextBuffer(XRayBuffer B) {
|
||||||
if (ProfileBuffers == nullptr || ProfileBuffers->Size() == 0)
|
if (ProfileBuffers == nullptr || ProfileBuffers->Size() == 0)
|
||||||
return {nullptr, 0};
|
return {nullptr, 0};
|
||||||
|
|
||||||
if (B.Data == nullptr)
|
static pthread_once_t Once = PTHREAD_ONCE_INIT;
|
||||||
|
static typename std::aligned_storage<sizeof(XRayProfilingFileHeader)>::type
|
||||||
|
FileHeaderStorage;
|
||||||
|
pthread_once(&Once,
|
||||||
|
+[] { new (&FileHeaderStorage) XRayProfilingFileHeader{}; });
|
||||||
|
|
||||||
|
if (UNLIKELY(B.Data == nullptr)) {
|
||||||
|
// The first buffer should always contain the file header information.
|
||||||
|
auto &FileHeader =
|
||||||
|
*reinterpret_cast<XRayProfilingFileHeader *>(&FileHeaderStorage);
|
||||||
|
FileHeader.Timestamp = NanoTime();
|
||||||
|
FileHeader.PID = internal_getpid();
|
||||||
|
return {&FileHeaderStorage, sizeof(XRayProfilingFileHeader)};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNLIKELY(B.Data == &FileHeaderStorage))
|
||||||
return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size};
|
return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size};
|
||||||
|
|
||||||
BlockHeader Header;
|
BlockHeader Header;
|
||||||
|
|
|
@ -32,16 +32,6 @@ namespace __xray {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uptr XRayProfilingVersion = 0x20180424;
|
|
||||||
|
|
||||||
struct XRayProfilingFileHeader {
|
|
||||||
const u64 MagicBytes = 0x7872617970726f66; // Identifier for XRay profiling
|
|
||||||
// files 'xrayprof' in hex.
|
|
||||||
const uptr Version = XRayProfilingVersion;
|
|
||||||
uptr Timestamp = 0; // System time in nanoseconds.
|
|
||||||
uptr PID = 0; // Process ID.
|
|
||||||
};
|
|
||||||
|
|
||||||
atomic_sint32_t ProfilerLogFlushStatus = {
|
atomic_sint32_t ProfilerLogFlushStatus = {
|
||||||
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
|
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
|
||||||
|
|
||||||
|
@ -144,14 +134,7 @@ XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
|
||||||
if (Verbosity())
|
if (Verbosity())
|
||||||
Report("profiling: Failed to flush to file, dropping data.\n");
|
Report("profiling: Failed to flush to file, dropping data.\n");
|
||||||
} else {
|
} else {
|
||||||
XRayProfilingFileHeader Header;
|
// Now for each of the buffers, write out the profile data as we would
|
||||||
Header.Timestamp = NanoTime();
|
|
||||||
Header.PID = internal_getpid();
|
|
||||||
retryingWriteAll(Fd, reinterpret_cast<const char *>(&Header),
|
|
||||||
reinterpret_cast<const char *>(&Header) +
|
|
||||||
sizeof(Header));
|
|
||||||
|
|
||||||
// Now for each of the threads, write out the profile data as we would
|
|
||||||
// see it in memory, verbatim.
|
// see it in memory, verbatim.
|
||||||
while (B.Data != nullptr && B.Size != 0) {
|
while (B.Data != nullptr && B.Size != 0) {
|
||||||
retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
|
retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
|
||||||
|
|
|
@ -51,7 +51,8 @@ volatile int buffer_counter = 0;
|
||||||
assert(__xray_log_finalize() == XRayLogInitStatus::XRAY_LOG_FINALIZED);
|
assert(__xray_log_finalize() == XRayLogInitStatus::XRAY_LOG_FINALIZED);
|
||||||
assert(__xray_log_process_buffers(process_buffer) ==
|
assert(__xray_log_process_buffers(process_buffer) ==
|
||||||
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
// We're running three threds, so we expect three buffers.
|
// We're running three threads, so we expect four buffers (including the file
|
||||||
assert(buffer_counter == 3);
|
// header buffer).
|
||||||
|
assert(buffer_counter == 4);
|
||||||
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,10 @@ volatile int buffer_counter = 0;
|
||||||
f0();
|
f0();
|
||||||
assert(__xray_log_process_buffers(process_buffer) ==
|
assert(__xray_log_process_buffers(process_buffer) ==
|
||||||
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
assert(buffer_counter == 1);
|
// There's always at least one buffer, containing the profile file header. We
|
||||||
|
// assert that we have two, to indicate that we're expecting exactly one
|
||||||
|
// thread's worth of data.
|
||||||
|
assert(buffer_counter == 2);
|
||||||
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
|
|
||||||
// Let's reset the counter.
|
// Let's reset the counter.
|
||||||
|
@ -60,6 +63,6 @@ volatile int buffer_counter = 0;
|
||||||
f0();
|
f0();
|
||||||
assert(__xray_log_process_buffers(process_buffer) ==
|
assert(__xray_log_process_buffers(process_buffer) ==
|
||||||
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
assert(buffer_counter == 1);
|
assert(buffer_counter == 2);
|
||||||
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
assert(__xray_log_flushLog() == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue