Skip to content

[XRay] Draft: Runtime symbol resolution #132416

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
Expand Up @@ -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,
Expand Down
35 changes: 35 additions & 0 deletions compiler-rt/include/xray/xray_records.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
7 changes: 5 additions & 2 deletions compiler-rt/lib/xray/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
222 changes: 215 additions & 7 deletions compiler-rt/lib/xray/xray_basic_logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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)));
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
Loading
Loading