Skip to content
Closed
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
3 changes: 2 additions & 1 deletion docs/design/datacontracts/contract-descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ a JSON integer constant.
"globals":
{
"FEATURE_COMINTEROP": 0,
"s_pThreadStore": [ 0 ] // indirect from pointer data offset 0
"s_pThreadStore": [ 0 ], // indirect from pointer data offset 0
"RuntimeID": "windows-x64" // string value
},
"contracts": {"Thread": 1, "GCHandle": 1, "ThreadStore": 1}
}
Expand Down
17 changes: 10 additions & 7 deletions docs/design/datacontracts/data_descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ The global values will be in an array, with each value described by a dictionary

* `"name": "global value name"` the name of the global value
* `"type": "type name"` the type of the global value
* optional `"value": VALUE | [ int ] | "unknown"` the value of the global value, or an offset in an auxiliary array containing the value or "unknown".
* optional `"value": VALUE | [ int ] ` the value of the global value, or an offset in an auxiliary array containing the value.

The `VALUE` may be either a number of string. JSON numeric constants are always parsed as numbers. JSON strings are always parsed as strings and may additionally parse as a hex (with prefix `0x` or `0X`) or decimal number.
Numeric constants must be within the range of the type of the global value.

The `VALUE` may be a JSON numeric constant integer or a string containing a signed or unsigned
decimal or hex (with prefix `0x` or `0X`) integer constant. The constant must be within the range
of the type of the global value.

**Compact format**:

Expand All @@ -225,7 +225,8 @@ The global values will be in a dictionary, with each key being the name of a glo
* `[VALUE | [int], "type name"]` the type and value of a global
* `VALUE | [int]` just the value of a global

As in the regular format, `VALUE` is a numeric constant or a string containing an integer constant.
`VALUE` may be either a number of string. JSON numeric constants are always parsed as numbers. JSON strings are always parsed as strings and may additionally parse as a hex (with prefix `0x` or `0X`) or decimal number.
Numeric constants must be within the range of the type of the global value.

Note that a two element array is unambiguously "type and value", whereas a one-element array is
unambiguously "indirect value".
Expand Down Expand Up @@ -288,7 +289,7 @@ The baseline is given in the "regular" format.
],
"globals": [
{ "name": "FEATURE_EH_FUNCLETS", "type": "uint8", "value": "0" }, // baseline defaults value to 0
{ "name": "FEATURE_COMINTEROP", "type", "uint8", "value": "1"},
{ "name": "FEATURE_COMINTEROP", "type": "uint8", "value": "1"},
{ "name": "s_pThreadStore", "type": "pointer" } // no baseline value
]
}
Expand All @@ -308,7 +309,8 @@ The following is an example of an in-memory descriptor that references the above
"globals":
{
"FEATURE_COMINTEROP": 0,
"s_pThreadStore": [ 0 ] // indirect from aux data offset 0
"s_pThreadStore": [ 0 ], // indirect from aux data offset 0
"RuntimeID": "windows-x64"
}
}
```
Expand All @@ -332,6 +334,7 @@ And the globals will be:
| FEATURE_COMINTEROP | uint8 | 0 |
| FEATURE_EH_FUNCLETS | uint8 | 0 |
| s_pThreadStore | pointer | 0x0100ffe0 |
| RuntimeID | string |"windows-x64"|

The `FEATURE_EH_FUNCLETS` global's value comes from the baseline - not the in-memory data
descriptor. By contrast, `FEATURE_COMINTEROP` comes from the in-memory data descriptor - with the
Expand Down
32 changes: 32 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ struct GlobalPointerSpec
uint32_t PointerDataIndex;
};

struct GlobalStringSpec
{
uint32_t Name;
uint32_t StringValue;
};

#define CONCAT(token1,token2) token1 ## token2
#define CONCAT4(token1, token2, token3, token4) token1 ## token2 ## token3 ## token4

Expand All @@ -59,6 +65,7 @@ struct GlobalPointerSpec
#define MAKE_FIELDTYPELEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membertypename__, tyname, __, membername)
#define MAKE_GLOBALLEN_NAME(globalname) CONCAT(cdac_string_pool_globalname__, globalname)
#define MAKE_GLOBALTYPELEN_NAME(globalname) CONCAT(cdac_string_pool_globaltypename__, globalname)
#define MAKE_GLOBALVALUELEN_NAME(globalname) CONCAT(cdac_string_pool_globalvalue__, globalname)

// define a struct where the size of each field is the length of some string. we will use offsetof to get
// the offset of each struct element, which will be equal to the offset of the beginning of that string in the
Expand All @@ -71,6 +78,8 @@ struct CDacStringPoolSizes
#define CDAC_TYPE_BEGIN(name) DECL_LEN(MAKE_TYPELEN_NAME(name), sizeof(#name))
#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(MAKE_FIELDLEN_NAME(tyname,membername), sizeof(#membername)) \
DECL_LEN(MAKE_FIELDTYPELEN_NAME(tyname,membername), sizeof(#membertyname))
#define CDAC_GLOBAL_STRING(name, stringval) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \
DECL_LEN(MAKE_GLOBALVALUELEN_NAME(name), sizeof(#stringval))
#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name))
#define CDAC_GLOBAL(name,tyname,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \
DECL_LEN(MAKE_GLOBALTYPELEN_NAME(name), sizeof(#tyname))
Expand All @@ -84,6 +93,7 @@ struct CDacStringPoolSizes
#define GET_FIELDTYPE_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDTYPELEN_NAME(tyname,membername))
#define GET_GLOBAL_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALLEN_NAME(globalname))
#define GET_GLOBALTYPE_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALTYPELEN_NAME(globalname))
#define GET_GLOBALSTRING_VALUE(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALVALUELEN_NAME(globalname))

// count the types
enum
Expand Down Expand Up @@ -123,6 +133,15 @@ enum
#include "datadescriptor.h"
};

// count the global strings
enum
{
CDacBlobGlobalStringsCount =
#define CDAC_GLOBALS_BEGIN() 0
#define CDAC_GLOBAL_STRING(name,value) + 1
#include "datadescriptor.h"
};


#define MAKE_TYPEFIELDS_TYNAME(tyname) CONCAT(CDacFieldsPoolTypeStart__, tyname)

Expand Down Expand Up @@ -197,27 +216,31 @@ struct BinaryBlobDataDescriptor
uint32_t GlobalLiteralValuesStart;

uint32_t GlobalPointersStart;
uint32_t GlobalStringValuesStart;
uint32_t NamesPoolStart;

uint32_t TypeCount;
uint32_t FieldsPoolCount;

uint32_t GlobalLiteralValuesCount;
uint32_t GlobalPointerValuesCount;
uint32_t GlobalStringValuesCount;

uint32_t NamesPoolCount;

uint8_t TypeSpecSize;
uint8_t FieldSpecSize;
uint8_t GlobalLiteralSpecSize;
uint8_t GlobalPointerSpecSize;
uint8_t GlobalStringSpecSize;
} Directory;
uint32_t PlatformFlags;
uint32_t BaselineName;
struct TypeSpec Types[CDacBlobTypesCount];
struct FieldSpec FieldsPool[CDacBlobFieldsPoolCount];
struct GlobalLiteralSpec GlobalLiteralValues[CDacBlobGlobalLiteralsCount];
struct GlobalPointerSpec GlobalPointerValues[CDacBlobGlobalPointersCount];
struct GlobalStringSpec GlobalStringValues[CDacBlobGlobalStringsCount];
uint8_t NamesPool[sizeof(struct CDacStringPoolSizes)];
uint8_t EndMagic[4];
};
Expand All @@ -242,16 +265,19 @@ struct MagicAndBlob BlobDataDescriptor = {
/* .FieldsPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, FieldsPool),
/* .GlobalLiteralValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalLiteralValues),
/* .GlobalPointersStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalPointerValues),
/* .GlobalStringValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalStringValues),
/* .NamesPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, NamesPool),
/* .TypeCount = */ CDacBlobTypesCount,
/* .FieldsPoolCount = */ CDacBlobFieldsPoolCount,
/* .GlobalLiteralValuesCount = */ CDacBlobGlobalLiteralsCount,
/* .GlobalPointerValuesCount = */ CDacBlobGlobalPointersCount,
/* .GlobalStringValuesCount = */ CDacBlobGlobalStringsCount,
/* .NamesPoolCount = */ sizeof(struct CDacStringPoolSizes),
/* .TypeSpecSize = */ sizeof(struct TypeSpec),
/* .FieldSpecSize = */ sizeof(struct FieldSpec),
/* .GlobalLiteralSpecSize = */ sizeof(struct GlobalLiteralSpec),
/* .GlobalPointerSpecSize = */ sizeof(struct GlobalPointerSpec),
/* .GlobalStringSpecSize = */ sizeof(struct GlobalStringSpec)
},
/* .PlatformFlags = */ (sizeof(void*) == 4 ? 0x02 : 0) | 0x01,
/* .BaselineName = */ offsetof(struct CDacStringPoolSizes, cdac_string_pool_baseline_),
Expand Down Expand Up @@ -287,10 +313,16 @@ struct MagicAndBlob BlobDataDescriptor = {
#include "datadescriptor.h"
},

/* .GlobalStringValues = */ {
#define CDAC_GLOBAL_STRING(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .Value = */ GET_GLOBALSTRING_VALUE(name) },
#include "datadescriptor.h"
},

/* .NamesPool = */ ("\0" // starts with a nul
#define CDAC_BASELINE(name) name "\0"
#define CDAC_TYPE_BEGIN(name) #name "\0"
#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) #membername "\0" #membertyname "\0"
#define CDAC_GLOBAL_STRING(name,value) #name "\0" #value "\0"
#define CDAC_GLOBAL_POINTER(name,value) #name "\0"
#define CDAC_GLOBAL(name,tyname,value) #name "\0" #tyname "\0"
#include "datadescriptor.h"
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
#ifndef CDAC_GLOBAL_POINTER
#define CDAC_GLOBAL_POINTER(globalname,addr)
#endif
#ifndef CDAC_GLOBAL_STRING
#define CDAC_GLOBAL_STRING(globalname,stringval)
#endif
#ifndef CDAC_GLOBALS_END
#define CDAC_GLOBALS_END()
#endif
Expand Down Expand Up @@ -757,6 +760,9 @@ CDAC_TYPE_END(CalleeSavedRegisters)
CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()

CDAC_GLOBAL_STRING(Architecture, TEST)

CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
Expand Down Expand Up @@ -831,4 +837,5 @@ CDAC_GLOBALS_END()
#undef CDAC_GLOBALS_BEGIN
#undef CDAC_GLOBAL
#undef CDAC_GLOBAL_POINTER
#undef CDAC_GLOBAL_STRING
#undef CDAC_GLOBALS_END
43 changes: 32 additions & 11 deletions src/coreclr/tools/cdac-build-tool/DataDescriptorModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class DataDescriptorModel
public uint PlatformFlags { get; }
// The number of indirect globals plus 1 for the placeholder at index 0
[JsonIgnore]
public int PointerDataCount => 1 + Globals.Values.Count(g => g.Value.Indirect);
public int PointerDataCount => 1 + Globals.Values.Count(g => g.Value.Kind == GlobalValue.KindEnum.Indirect);

private DataDescriptorModel(string baseline, IReadOnlyDictionary<string, TypeModel> types, IReadOnlyDictionary<string, GlobalModel> globals, IReadOnlyDictionary<string, int> contracts, uint platformFlags)
{
Expand All @@ -36,6 +36,7 @@ private DataDescriptorModel(string baseline, IReadOnlyDictionary<string, TypeMod
}

public const string PointerTypeName = "pointer";
public const string StringTypeName = "string";

internal void DumpModel()
{
Expand Down Expand Up @@ -274,6 +275,7 @@ public GlobalValue? Value
}
}
}

internal sealed class FieldBuilder
{
private string _type = string.Empty;
Expand Down Expand Up @@ -331,19 +333,38 @@ public readonly struct TypeModel
[JsonConverter(typeof(GlobalValueJsonConverter))]
public readonly struct GlobalValue : IEquatable<GlobalValue>
{
public bool Indirect { get; private init; }
public ulong Value { get; }
public static GlobalValue MakeDirect(ulong value) => new GlobalValue(value);
public static GlobalValue MakeIndirect(uint auxDataIdx) => new GlobalValue((ulong)auxDataIdx) { Indirect = true };
private GlobalValue(ulong value) { Value = value; }
public enum KindEnum
{
Direct,
Indirect,
String
}

public KindEnum Kind { get; private init; }
public ulong NumericValue { get; }
public string StringValue { get; }
public static GlobalValue MakeDirect(ulong value) => new GlobalValue(value) { Kind = KindEnum.Direct };
public static GlobalValue MakeIndirect(uint auxDataIdx) => new GlobalValue((ulong)auxDataIdx) { Kind = KindEnum.Indirect };
public static GlobalValue MakeString(string value) => new GlobalValue(value) { Kind = KindEnum.String };
private GlobalValue(ulong value) { NumericValue = value; StringValue = string.Empty;}
private GlobalValue(string value) { StringValue = value; }

public static bool operator ==(GlobalValue left, GlobalValue right) => left.Value == right.Value && left.Indirect == right.Indirect;
public static bool operator ==(GlobalValue left, GlobalValue right) => left.Equals(right);
public static bool operator !=(GlobalValue left, GlobalValue right) => !(left == right);

public bool Equals(GlobalValue other) => this == other;
public override bool Equals(object? obj) => obj is GlobalValue value && this == value;
public override int GetHashCode() => HashCode.Combine(Value, Indirect);
public override string ToString() => Indirect ? $"Indirect({Value})" : $"0x{Value:x}";
public bool Equals(GlobalValue other) => other.Kind == Kind && other.NumericValue == NumericValue && other.StringValue == StringValue;
public override bool Equals(object? obj) => obj is GlobalValue value && Equals(value);
public override int GetHashCode() => HashCode.Combine(Kind, NumericValue, StringValue);
public override string ToString()
{
return Kind switch
{
KindEnum.Direct => $"0x{NumericValue:x}",
KindEnum.Indirect => $"Indirect({NumericValue})",
KindEnum.String => $"'{StringValue}'",
_ => throw new InvalidOperationException("Unknown GlobalValue type")
};
}
}

[JsonConverter(typeof(GlobalModelJsonConverter))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@ public override DataDescriptorModel.GlobalValue Read(ref Utf8JsonReader reader,

public override void Write(Utf8JsonWriter writer, DataDescriptorModel.GlobalValue value, JsonSerializerOptions options)
{
if (!value.Indirect)
switch (value.Kind)
{
// no type: just write value as a number.
// we always write as a string containing a hex number
writer.WriteStringValue($"0x{value.Value:x}");
}
else
{
// pointer data index. write as a 1-element array containing a decimal number
writer.WriteStartArray();
writer.WriteNumberValue(value.Value);
writer.WriteEndArray();
case DataDescriptorModel.GlobalValue.KindEnum.Direct:
// no type: just write value as a number.
// we always write as a string containing a hex number
writer.WriteStringValue($"0x{value.NumericValue:x}");
break;
case DataDescriptorModel.GlobalValue.KindEnum.Indirect:
// pointer data index. write as a 1-element array containing a decimal number
writer.WriteStartArray();
writer.WriteNumberValue(value.NumericValue);
writer.WriteEndArray();
break;
case DataDescriptorModel.GlobalValue.KindEnum.String:
// string data. write as a JSON string value
writer.WriteStringValue(value.StringValue);
break;
default:
throw new InvalidOperationException("Unknown GlobalValue type");
}
}
}
Loading
Loading