Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[XRay] Draft: Runtime symbol resolution #132416

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler-rt/include/xray/xray_interface.h
Original file line number Diff line number Diff line change
@@ -19,6 +19,23 @@

extern "C" {

// TODO: The following is for a prototype implementation
struct FunctionMapEntry {
int32_t FunctionId;
uint64_t Addr;
FunctionMapEntry* Next;
};

struct XRaySymbolInfo {
int32_t FuncId;
const char* Name;
const char* Module;
const char* File;
int64_t Line;
};

extern int __xray_symbolize(int32_t, XRaySymbolInfo*);

/// Synchronize this with AsmPrinter::SledKind in LLVM.
enum XRayEntryType {
ENTRY = 0,
35 changes: 35 additions & 0 deletions compiler-rt/include/xray/xray_records.h
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ struct alignas(32) XRayFileHeader {
// reading the data in the file.
bool ConstantTSC : 1;
bool NonstopTSC : 1;
bool HasFunctionMap : 1;

// The frequency by which TSC increases per-second.
alignas(8) uint64_t CycleFrequency = 0;
@@ -67,8 +68,42 @@ static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes");
enum RecordTypes {
NORMAL = 0,
ARG_PAYLOAD = 1,
FUNC_INFO = 2,
OBJ_INFO = 3,
FILE_MD = 4
};

struct alignas(32) XRayFunctionMD {
uint16_t RecordType = RecordTypes::FUNC_INFO;
int32_t FuncId = 0;
int32_t Line = 0;
int16_t FileMDIdx = 0;
int16_t NameLen = 0;
// 18 bytes may not be enough to store the full name. In this case, 32 byte
// chunks containing the rest of the name are expected to follow this record
// as needed.
char NameBuffer[18] = {};
} __attribute__((packed));

struct alignas(32) XRayFileMD {
uint16_t RecordType = RecordTypes::FILE_MD;
int16_t FilenameLen = 0;
// The padding bytes may not be enough to store the full name. In this case, 32 byte blocks
// containing the rest of the name are expected to follow this record
// as needed.
char FilenameBuffer[28] = {};
} __attribute__((packed));

struct alignas(32) XRayObjectInfoRecord {
uint16_t RecordType = RecordTypes::OBJ_INFO;
int32_t ObjId = 0;
int16_t FilenameLen = 0;
// 24 may not be enough to store the full name. In this case, 32 byte blocks
// containing the rest of the name are expected to follow this record
// as needed.
char FilenameBuffer[24] = {};
} __attribute__((packed));

struct alignas(32) XRayRecord {
// This is the type of the record being written. We use 16 bits to allow us to
// treat this as a discriminant, and so that the first 4 bytes get packed
7 changes: 5 additions & 2 deletions compiler-rt/lib/xray/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -201,7 +201,8 @@ append_rtti_flag(OFF XRAY_CFLAGS)
set(XRAY_LINK_FLAGS ${COMPILER_RT_COMMON_LINK_FLAGS})
set(XRAY_LINK_LIBS
${COMPILER_RT_UNWINDER_LINK_LIBS}
${COMPILER_RT_CXX_LINK_LIBS})
${COMPILER_RT_CXX_LINK_LIBS}
)

append_list_if(
COMPILER_RT_HAS_XRAY_COMPILER_FLAG XRAY_SUPPORTED=1 XRAY_COMMON_DEFINITIONS)
@@ -212,7 +213,9 @@ add_compiler_rt_component(xray)

set(XRAY_COMMON_RUNTIME_OBJECT_LIBS
RTSanitizerCommon
RTSanitizerCommonLibc)
RTSanitizerCommonLibc
RTSanitizerCommonSymbolizer
RTSanitizerCommonSymbolizerInternal)

# XRay uses C++ standard library headers.
if (TARGET cxx-headers OR HAVE_LIBCXX)
222 changes: 215 additions & 7 deletions compiler-rt/lib/xray/xray_basic_logging.cpp
Original file line number Diff line number Diff line change
@@ -27,7 +27,11 @@

#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_dense_map.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "sanitizer_common/sanitizer_hash.h"
#include "xray/xray_records.h"
#include "xray/xray_allocator.h"
#include "xray_recursion_guard.h"
#include "xray_basic_flags.h"
#include "xray_basic_logging.h"
@@ -37,11 +41,63 @@
#include "xray_tsc.h"
#include "xray_utils.h"

#include <new>

namespace {
struct StringKey {
constexpr StringKey() {}
constexpr StringKey(const char *Key) : Key(Key), EmptyOrTombstone(1) {}

static constexpr StringKey Empty() {
StringKey Key;
Key.EmptyOrTombstone = __sanitizer::DenseMapInfo<char>::getEmptyKey();
return Key;
}

static constexpr StringKey Tombstone() {
StringKey Key;
Key.EmptyOrTombstone = __sanitizer::DenseMapInfo<char>::getTombstoneKey();
return Key;
}

const char *Key{nullptr};
mutable char EmptyOrTombstone{__sanitizer::DenseMapInfo<char>::getEmptyKey()};

};
} // namespace

namespace __sanitizer {
// Provide DenseMapInfo for chars.
template <> struct DenseMapInfo<StringKey> {
static constexpr StringKey getEmptyKey() { return StringKey::Empty(); }
static constexpr StringKey getTombstoneKey() { return StringKey::Tombstone(); }
static unsigned getHashValue(const StringKey &Val) {
if (!Val.Key) {
return 0;
}
MurMur2HashBuilder Hash;
auto Len = internal_strlen(Val.Key);
for (unsigned I = 0; I < Len; I++) {
Hash.add(DenseMapInfo<char>::getHashValue(Val.Key[I]));
}
return Hash.get();
}

static bool isEqual(const StringKey &LHS, const StringKey &RHS) {
if (!LHS.Key || !RHS.Key) {
return !LHS.Key && !RHS.Key;
}
return __sanitizer::internal_strcmp(LHS.Key, RHS.Key) == 0;
}
};
} // namespace __sanitizer

namespace __xray {

static SpinMutex LogMutex;

namespace {

// We use elements of this type to record the entry TSC of every function ID we
// see as we're tracing a particular thread's execution.
struct alignas(16) StackEntry {
@@ -84,6 +140,140 @@ static atomic_uint64_t ThresholdTicks{0};
static atomic_uint64_t TicksPerSec{0};
static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};


struct FunctionRecordBuffer {

// First entry is buffer idx, second states if this entry has been flushed.
using FuncMapEntry = bool;//detail::DenseMapPair<int32_t, bool>;

// DenseMap<int32_t, DataInfo> SymInfo;

// Using this like a set to track which function have been symbolized.
DenseMap<int32_t, FuncMapEntry> FuncMapping;

using MappedAddressInfo = std::pair<int, AddressInfo>;

MappedAddressInfo * AddrInfoBuf{nullptr};
unsigned Size{0};

DenseMap<StringKey, int32_t> FileMDMap;
unsigned FileMDCount{0};

DenseMap<int32_t, bool> ObjWrittenMap;

static constexpr unsigned BufferSize = 1024;

LogWriter* Writer;

FunctionRecordBuffer(LogWriter* LW) XRAY_NEVER_INSTRUMENT : Writer(LW) {
AddrInfoBuf = allocateBuffer<MappedAddressInfo>(BufferSize);
}

~FunctionRecordBuffer() XRAY_NEVER_INSTRUMENT {
Flush();
deallocateBuffer<MappedAddressInfo>(AddrInfoBuf, BufferSize);
}

static FunctionRecordBuffer& Create(LogWriter* LW) XRAY_NEVER_INSTRUMENT {
auto *FRB = allocate<FunctionRecordBuffer>();
new (FRB) FunctionRecordBuffer(LW);
return *FRB;
}

void Destroy() XRAY_NEVER_INSTRUMENT {
this->~FunctionRecordBuffer();
deallocate(this);
}

void StoreFunctionRecord(int32_t FuncId) XRAY_NEVER_INSTRUMENT {

auto& Symbolized = FuncMapping[FuncId];
if (!Symbolized) {
if (Size >= BufferSize) {
Flush();
}
auto&MAI = AddrInfoBuf[Size];
if (!Symbolize(FuncId, &MAI.second)) {
Report("Unable to symbolize function %d\n", FuncId);
return;
}
MAI.first = FuncId;
Symbolized = true;
Size++;
}


}

void Flush() XRAY_NEVER_INSTRUMENT {
if (Verbosity())
Report("Flushing function record buffer\n");
for (unsigned I = 0; I < Size; I++) {
auto& MDI = AddrInfoBuf[I];
auto Id = MDI.first;
AddressInfo& AI = MDI.second;

int FileMDIdx = 0;
if (AI.file) {
if (auto *It = FileMDMap.find(StringKey(AI.file)); It) {
FileMDIdx = It->second;
} else {
XRayFileMD FMD;
FMD.FilenameLen = static_cast<int16_t>(internal_strlen(AI.file));
Writer->WriteAll(reinterpret_cast<char *>(&FMD),
reinterpret_cast<char *>(&FMD.FilenameBuffer));
Writer->WriteAll(AI.file, AI.file + FMD.FilenameLen);
constexpr int FilenameBufferSize = sizeof(FMD.FilenameBuffer);
auto NumPaddingBytes =
FMD.FilenameLen < FilenameBufferSize
? FilenameBufferSize - FMD.FilenameLen
: 32 - (FMD.FilenameLen - FilenameBufferSize) % 32;
Writer->WritePadding(NumPaddingBytes);
FileMDCount++;
FileMDMap[AI.file] = FileMDCount;
FileMDIdx = FileMDCount;
}
}

XRayFunctionMD FIR;
FIR.FuncId = Id;
FIR.FileMDIdx = FileMDIdx;
FIR.Line = AI.line;
FIR.NameLen = static_cast<int16_t>(internal_strlen(AI.function));
// The padding bytes are used for string storage.
Writer->WriteAll(reinterpret_cast<char *>(&FIR),
reinterpret_cast<char *>(&FIR.NameBuffer));
Writer->WriteAll(AI.function, AI.function + FIR.NameLen);
constexpr int NameBufferSize = sizeof(FIR.NameBuffer);
auto NumPaddingBytes = FIR.NameLen < NameBufferSize ? NameBufferSize - FIR.NameLen : 32 - (FIR.NameLen - NameBufferSize) % 32;

Writer->WritePadding(NumPaddingBytes);

// auto ObjId = UnpackId(Id).first;
// auto& ObjWritten = ObjWrittenMap[ObjId];
// if (!ObjWritten) {
// XRayObjectInfoRecord OIR;
// OIR.ObjId = ObjId;
// const char* Filename = AI.module;
// OIR.FilenameLen = static_cast<int16_t>(internal_strlen(Filename));
// Writer->WriteAll(reinterpret_cast<char *>(&OIR),
// reinterpret_cast<char *>(&OIR.FilenameBuffer));
// Writer->WriteAll(Filename, Filename + OIR.FilenameLen);
// NumPaddingBytes = OIR.FilenameLen < 24 ? 24 - OIR.FilenameLen : 32 - (OIR.FilenameLen - 24) % 32;
// Writer->WritePadding(NumPaddingBytes);
// ObjWritten = true;
// }
}
Size = 0;
}
};


struct GlobalLoggingData {
LogWriter* Writer;
FunctionRecordBuffer* RecordBuffer;
};

static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
LogWriter* LW = LogWriter::Open();
if (LW == nullptr)
@@ -114,11 +304,17 @@ static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
return LW;
}

static LogWriter *getGlobalLog() XRAY_NEVER_INSTRUMENT {
static GlobalLoggingData createLoggingData() XRAY_NEVER_INSTRUMENT {
auto* LW = getLog();
auto* FRB = &FunctionRecordBuffer::Create(LW);
return {LW, FRB};
}

static GlobalLoggingData &getGlobalData() XRAY_NEVER_INSTRUMENT {
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
static LogWriter *LW = nullptr;
pthread_once(&OnceInit, +[] { LW = getLog(); });
return LW;
static GlobalLoggingData LD;
pthread_once(&OnceInit, +[] { LD = createLoggingData(); });
return LD;
}

static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
@@ -130,7 +326,7 @@ static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
return false;
}
pthread_setspecific(PThreadKey, &TLD);
TLD.LogWriter = getGlobalLog();
TLD.LogWriter = getGlobalData().Writer;
TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
nullptr, alignof(XRayRecord)));
@@ -154,11 +350,14 @@ static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
return TLD;
}


template <class RDTSC>
void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
LogWriter *LW = getGlobalLog();
auto &GD = getGlobalData();

LogWriter *LW = GD.Writer;
if (LW == nullptr)
return;

@@ -230,6 +429,9 @@ void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
break;
}

// Add function record
GD.RecordBuffer->StoreFunctionRecord(FuncId);

// First determine whether the delta between the function's enter record and
// the exit record is higher than the threshold.
XRayRecord R;
@@ -258,7 +460,7 @@ void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
auto FirstEntry =
reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
const auto &BuffLen = TLD.BufferSize;
LogWriter *LW = getGlobalLog();
LogWriter *LW = getGlobalData().Writer;
if (LW == nullptr)
return;

@@ -367,6 +569,7 @@ static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
// sync instead and hope that the pending writes are flushed as the
// thread exits.
TLD.LogWriter->Flush();
getGlobalData().RecordBuffer->Flush();
}

XRayLogInitStatus basicLoggingInit(UNUSED size_t BufferSize,
@@ -449,6 +652,11 @@ XRayLogInitStatus basicLoggingFinalize() XRAY_NEVER_INSTRUMENT {
// Nothing really to do aside from marking state of the global to be
// uninitialized.

if (Verbosity())
Report("Finalizing basic mode\n");

getGlobalData().RecordBuffer->Flush();

return XRayLogInitStatus::XRAY_LOG_FINALIZED;
}

Loading
Oops, something went wrong.
Loading
Oops, something went wrong.