本文件將積極討論如何建立編譯時間集合 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
的唯一例項
追蹤與類別鎖定相關的線上鎖定順序觀察結果
(ContainingType
、LockType
)。目前的 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 能夠正確處理這些類型的版面屬性。