編譯時間物件集合討論

本文件將積極討論如何建立編譯時間集合 C++ 中的多個物件以下是編譯時間的例子。 集合非常實用:

  • StringRef - 支援建構字串編譯時間集合的型別 以便追蹤。
  • LockClass - 支援建構狀態編譯時間集合的型別 物件,用於執行階段鎖定驗證。

以下各節將討論各種用途的通用和獨特需求。 目前導入作業面臨的挑戰及建議的解決方案。

StringRef

StringRef 是一種實作字串參照概念的類型。字串 參照是從數字 ID 對應至字元字串。使用 對應可讓您以更符合成本效益的方式使用追蹤記錄緩衝區:(ID、字串) 組合會在追蹤工作階段中發出一次,後續事件可能參照 ,而不要加入完整的字元序列。

以下是 StringRef 的實際運作範例:

#include <lib/ktrace.h>

template <typename Op, typename... Args>
inline DoSomething(Op&& op, Args&&... args) {
    ktrace_probe(TraceAlways, TraceContext::Thread, "DoSomething"_stringref);
    // ...
}

這裡的字串常值運算子 _stringref 會傳回 StringRef 的例項 ,提供功能來對應「DoSomething」字串轉換為數字 ID 追蹤函式所用的名稱

需求條件

  • 在任何追蹤工作階段之前,每個追蹤工作階段最多發出每個 (ID、字串) 對應一次 參照該 ID 的追蹤事件。理想的對應組合是 在追蹤工作階段開始時一次發出,以免造成 不過,在時間敏感程式碼中間發出對應,但這並非 也不是硬性規定
  • 建議使用密集的 ID 空間,讓下游處理程式碼可以使用 線性預先分配的陣列或向量來執行 ID 對字串查詢 但這並非硬性規定
  • 追蹤呼叫必須發出不重複字串參照的部分方法, 範本和內嵌函式及方法支援

目前的導入作業

以下概述目前的 StringRef 實作。

struct StringRef {
    static constexpr int kInvalidId = -1;

    const char* string{nullptr};
    ktl::atomic<int> id{kInvalidId};
    StringRef* next{nullptr};

    int GetId() {
        const int ref_id = id.load(ktl::memory_order_relaxed);
        return ref_id == kInvalidId ? Register(this) : ref_id;
    }

    // Returns the head of the global string ref linked list.
    static StringRef* head() { return head_.load(ktl::memory_order_acquire); }

private:
    // Registers a string ref in the global linked list.
    static int Register(StringRef* string_ref);

    static ktl::atomic<int> id_counter_;
    static ktl::atomic<StringRef*> head_;
};

// Returns an instance of StringRef that corresponds to the given string
// literal.
template <typename T, T... chars>
inline StringRef* operator""_stringref() {
    static const char storage[] = {chars..., '\0'};
    static StringRef string_ref{storage};
    return &string_ref;
}

LockClass

LockClass 是一種類型,會擷取所有使用者通用的鎖定相關資訊 鎖定的例項 (例如,如果鎖定是 struct/類別成員,則包含其類型; 基礎鎖定原始的類型、描述其行為的旗標)。 執行階段鎖定驗證工具會使用 LockClass 類型,藉此判斷排序 規則會套用至每個鎖定,並找出各個鎖定類別的追蹤結構 用來記錄排序觀察結果

以下是 LockClass 的簡化範例:

struct Foo {
    LockClass<Foo, fbl::Mutex> lock;
    // ...
};

struct Bar {
    LockClass<Bar, fbl::Mutex> lock;
};

需求條件

  • 可疊代所有 LockClass 例項化的追蹤狀態, 週期偵測及錯誤回報用途
  • 有些方法可以產生不重複的重複追蹤狀態,因為 視 包含各種類型 (例如 Foo 和 Bar)。
  • 建議使用密集的 ID 空間,以便簡化串流處理程式碼 但其實這並非硬性規定。

目前的導入作業

以下是 LockClass 的簡化實作:

template <typename ContainingType, typename LockType>
class LockClass {
    // ...
private:
    static LockClassState lock_class_state_;
};

每次將 LockClass 執行個體化時,都會建立 LockClassState 的唯一例項 追蹤與類別鎖定相關的線上鎖定順序觀察結果 (ContainingTypeLockType)。目前的 LockClassState 實作 會建立全域 ctor 中所有執行個體的連結清單,以支援 疊代要求

編譯時間陣列解決方案

要滿足這兩種類型的需求,其中一種方法是建立編譯時間 不重複靜態執行個體的陣列,透過 COMDAT 區段和群組。這個 完全不需要在初始化時建構物件連結清單,或 並且支援各類型的所有需求條件。

例如:

// Defined in the linker script mark the beginning and end of the section:
// .data.lock_class_state_table.
extern "C" LockClassState __start_lock_class_state_table[];
extern "C" LockClassState __end_lock_class_state_table[];

template <typename ContainingType, typename LockClass>
class LockClass {
    // ...
private:
    static LockClassState lock_class_state_ __SECTION(".data.lock_class_state_table");
};

// Defined in the linker script to make the beginning and end of the section:
// .rodata.string_ref_table.
extern "C" StringRef __start_string_ref_table[];
extern "C" StringRef __end_string_ref_table[];

struct StringRef {
    const char* const string;
    size_t GetId() const {
        return static_cast<size_t>(this - __start_string_ref_table);
    }
};

template <typename T, T... chars>
inline StringRef* operator""_stringref() {
    static const char storage[] = {chars..., '\0'};
    static StringRef string_ref __SECTION(".rodata.string_ref_table") {storage};
    return &string_ref;
}

使用這個方法的難處在於 GCC 無法正確處理 區段屬性。不過 Clang 能夠正確處理這些類型的版面屬性。