Skip to content

Commit 5371f13

Browse files
authored
[cDAC] Refactor datadescriptor infrastructure (#118453)
* modify datadescriptors to build using a cmake function * move datadescriptor from src/coreclr/debug/runtimeinfo -> src/coreclr/vm/datadescriptor/
1 parent f1be1df commit 5371f13

17 files changed

+338
-234
lines changed

src/coreclr/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ endif(CLR_CMAKE_HOST_WIN32)
178178
#----------------------------------
179179
include(clrdefinitions.cmake)
180180

181+
#--------------------------------
182+
# Data descriptors mechanics
183+
# - all clr specific data descriptor helpers should be included in this file
184+
#----------------------------------
185+
include(clrdatadescriptors.cmake)
186+
181187
if(FEATURE_STANDALONE_GC)
182188
add_definitions(-DFEATURE_STANDALONE_GC)
183189
endif(FEATURE_STANDALONE_GC)

src/coreclr/clrdatadescriptors.cmake

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# cDAC contract descriptor
2+
3+
function(generate_data_descriptors)
4+
set(options EXPORT_VISIBLE)
5+
set(oneValueArgs LIBRARY_NAME CONTRACT_FILE CONTRACT_NAME INTERFACE_TARGET)
6+
set(multiValueArgs "")
7+
cmake_parse_arguments(DATA_DESCRIPTORS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV})
8+
9+
# INTERMEDIARY_LIBRARY is used as part of the build and not linked into the final product.
10+
set(INTERMEDIARY_LIBRARY ${DATA_DESCRIPTORS_LIBRARY_NAME}_temp)
11+
set(LIBRARY ${DATA_DESCRIPTORS_LIBRARY_NAME})
12+
13+
set(DATA_DESCRIPTOR_SHARED_SOURCE_DIR "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/debug/datadescriptor-shared")
14+
set(GENERATED_CDAC_DESCRIPTOR_DIR "${CMAKE_CURRENT_BINARY_DIR}/cdac-${LIBRARY}")
15+
16+
# configure contract export name
17+
set(POINTER_DATA_NAME ${DATA_DESCRIPTORS_CONTRACT_NAME}PointerData)
18+
set(CONTRACT_NAME ${DATA_DESCRIPTORS_CONTRACT_NAME})
19+
if (DATA_DESCRIPTORS_EXPORT_VISIBLE)
20+
set(EXPORT_CONTRACT 1)
21+
else()
22+
set(EXPORT_CONTRACT 0)
23+
endif()
24+
configure_file("${DATA_DESCRIPTOR_SHARED_SOURCE_DIR}/contractconfiguration.h.in" "${GENERATED_CDAC_DESCRIPTOR_DIR}/contractconfiguration.h")
25+
26+
if (NOT CDAC_BUILD_TOOL_BINARY_PATH)
27+
# if CDAC_BUILD_TOOL_BINARY_PATH is unspecified (for example for a build without a .NET SDK or msbuild),
28+
# link a stub contract descriptor into the runtime
29+
add_library_clr(${LIBRARY} OBJECT "${DATA_DESCRIPTOR_SHARED_SOURCE_DIR}/contractdescriptorstub.c")
30+
target_include_directories(${LIBRARY} PRIVATE ${GENERATED_CDAC_DESCRIPTOR_DIR})
31+
message(STATUS "Using a stub cDAC contract descriptor")
32+
else()
33+
# generate a contract descriptor using cdac-build-tool from a data descriptor and contract json file
34+
35+
if(NOT EXISTS "${CDAC_BUILD_TOOL_BINARY_PATH}")
36+
message(FATAL_ERROR "${CDAC_BUILD_TOOL_BINARY_PATH} does not exist")
37+
endif()
38+
39+
add_library(${INTERMEDIARY_LIBRARY} OBJECT "${DATA_DESCRIPTOR_SHARED_SOURCE_DIR}/datadescriptor.cpp")
40+
41+
if(CLR_CMAKE_TARGET_WIN32)
42+
# turn off whole program optimization:
43+
# 1. it creates object files that cdac-build-tool can't read
44+
# 2. we never link INTERMEDIARY_LIBRARY into the final product - it's only job is to be scraped
45+
set_target_properties(${INTERMEDIARY_LIBRARY} PROPERTIES
46+
INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF
47+
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)
48+
endif()
49+
50+
# inherit definitions, include directories, and dependencies from the INTERFACE target
51+
target_link_libraries(${INTERMEDIARY_LIBRARY} PRIVATE ${DATA_DESCRIPTORS_INTERFACE_TARGET})
52+
53+
set(CONTRACT_BASELINE_DIR "${CLR_REPO_ROOT_DIR}/docs/design/datacontracts/data")
54+
set(CONTRACT_DESCRIPTOR_INPUT "${DATA_DESCRIPTOR_SHARED_SOURCE_DIR}/contract-descriptor.c.in")
55+
set(CONTRACT_DESCRIPTOR_OUTPUT "${GENERATED_CDAC_DESCRIPTOR_DIR}/contract-descriptor.c")
56+
set(CONTRACT_FILE "${DATA_DESCRIPTORS_CONTRACT_FILE}")
57+
58+
# generate the contract descriptor by running cdac-build-tool
59+
# n.b. this just uses `dotnet` from the PATH. InitializeDotNetCli adds the appropriate directory
60+
add_custom_command(
61+
OUTPUT "${CONTRACT_DESCRIPTOR_OUTPUT}"
62+
VERBATIM
63+
COMMAND ${CLR_DOTNET_HOST_PATH} ${CDAC_BUILD_TOOL_BINARY_PATH} compose -i "${CONTRACT_DESCRIPTOR_INPUT}" -o "${CONTRACT_DESCRIPTOR_OUTPUT}" -b "${CONTRACT_BASELINE_DIR}" -c "${CONTRACT_FILE}" $<TARGET_OBJECTS:${INTERMEDIARY_LIBRARY}>
64+
DEPENDS ${INTERMEDIARY_LIBRARY} ${DATA_DESCRIPTORS_DEPENDENCIES} $<TARGET_OBJECTS:${INTERMEDIARY_LIBRARY}> "${CONTRACT_FILE}" "${CONTRACT_DESCRIPTOR_INPUT}"
65+
USES_TERMINAL
66+
)
67+
68+
# It is important that LIBRARY is an object library;
69+
# if it was static, linking it into the final dll would not export
70+
# ${CONTRACT_NAME} since it is not referenced anywhere.
71+
add_library_clr(${LIBRARY} OBJECT
72+
"${CONTRACT_DESCRIPTOR_OUTPUT}"
73+
"${DATA_DESCRIPTOR_SHARED_SOURCE_DIR}/contractpointerdata.cpp"
74+
)
75+
add_dependencies(${LIBRARY} ${INTERMEDIARY_LIBRARY})
76+
77+
target_include_directories(${LIBRARY} PRIVATE ${GENERATED_CDAC_DESCRIPTOR_DIR})
78+
79+
# inherit definitions, include directories, and dependencies from the INTERFACE target
80+
target_link_libraries(${LIBRARY} PRIVATE ${DATA_DESCRIPTORS_INTERFACE_TARGET})
81+
endif()
82+
endfunction(generate_data_descriptors)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Datadescriptor Implementation Infrastructure
2+
3+
This folder contains infrastructure to create data descriptors as defined in the [data_descriptor.md](../../../../docs/design/datacontracts/data_descriptor.md). Data descriptors enable diagnostic tooling (debuggers, profilers, etc.) to understand the internal layout and structure of .NET runtime objects without requiring intimate knowledge of implementation details.
4+
5+
## CMake Integration and Build System
6+
7+
### Function Parameters
8+
9+
The `generate_data_descriptors` function defined in `clrdatadescriptors.cmake` takes the following arguments:
10+
11+
* **`LIBRARY_NAME`** (Required) - Sets the name of the target object being created
12+
* **`CONTRACT_FILE`** (Required) - Path to the contract JSON file defining supported contracts
13+
* **`CONTRACT_NAME`** (Required) - Name of the `ContractDescriptor` export symbol
14+
* **`INTERFACE_TARGET`** (Required) - Interface target providing dependencies, include directories, and definitions
15+
* **`EXPORT_VISIBLE`** (Optional) - Controls if the `CONTRACT_NAME` will be exported from the DLL
16+
17+
### Two-Phase Build Process
18+
19+
The build system uses a two-phase approach:
20+
21+
**Phase 1: Intermediary Library**
22+
- Compiles `datadescriptor.cpp` with your `datadescriptor.h` and `datadescriptor.inc`
23+
- Creates object files that the `cdac-build-tool` can analyze
24+
- Extracts type layout information and generates string pools
25+
26+
**Phase 2: Contract Descriptor Generation**
27+
- Runs `cdac-build-tool` to process the intermediary object files
28+
- Generates the final contract descriptor C source file
29+
- Compiles this into the final library that gets linked into the runtime
30+
31+
32+
## Macro Reference
33+
34+
### Structure Definition Macros
35+
36+
**`CDAC_BASELINE("identifier")`**
37+
- Specifies the baseline data contract version
38+
- Use `"empty"` for new descriptors
39+
- Must appear before any other content
40+
41+
**`CDAC_TYPES_BEGIN()` / `CDAC_TYPES_END()`**
42+
- Delimits the type definitions section
43+
- Must contain all `CDAC_TYPE_*` macros
44+
45+
**`CDAC_TYPE_BEGIN(typeName)`**
46+
- Starts a new type definition
47+
- `typeName` must be globally unique within the descriptor
48+
49+
**`CDAC_TYPE_SIZE(sizeInBytes)`**
50+
- Specifies the type has a determinate size
51+
- Usually `sizeof(YourNativeType)`
52+
53+
**`CDAC_TYPE_INDETERMINATE(typeName)`**
54+
- Specifies the type has indeterminate size
55+
- Alternative to `CDAC_TYPE_SIZE`
56+
57+
**`CDAC_TYPE_FIELD(typeName, fieldType, fieldName, offset)`**
58+
- Defines a field within the type
59+
- `fieldType`: primitive type or another defined type
60+
- `fieldName`: diagnostic-friendly name (use managed names for managed types)
61+
- `offset`: byte offset, usually `offsetof()` or `cdac_data<T>::FieldName`
62+
63+
**`CDAC_TYPE_END(typeName)`**
64+
- Closes the type definition
65+
- `typeName` must match the corresponding `CDAC_TYPE_BEGIN`
66+
67+
### Global Value Macros
68+
69+
**`CDAC_GLOBALS_BEGIN()` / `CDAC_GLOBALS_END()`**
70+
- Delimits the global values section
71+
72+
**`CDAC_GLOBAL(globalName, typeName, value)`**
73+
- Defines a global literal value
74+
- `value` must be a compile-time constant
75+
- `typeName` can be a primitive type or defined type
76+
77+
**`CDAC_GLOBAL_POINTER(globalName, address)`**
78+
- Defines a global pointer value
79+
- `address` must be a compile-time constant pointer or `uintptr_t`
80+
81+
**`CDAC_GLOBAL_STRING(globalName, stringValue)`**
82+
- Defines a global string value
83+
- `stringValue` must be a compile-time string literal
84+
85+
86+
## Current Implementation
87+
88+
For reference, see the current implementation in:
89+
- **`src/coreclr/vm/datadescriptor/`** - Complete real-world implementation
90+
- `datadescriptor.h` - Headers and includes
91+
- `datadescriptor.inc` - Full type definitions for runtime objects
92+
- `contracts.jsonc` - Contract definitions
93+
- `CMakeLists.txt` - Build integration
94+
95+
## Related Documentation
96+
97+
- **[Data Contracts Design](../../../../docs/design/datacontracts/datacontracts_design.md)** - Overall design and motivation
98+
- **[Contract Descriptor](../../../../docs/design/datacontracts/contract-descriptor.md)** - Binary format specification
99+
- **[Data Descriptor](../../../../docs/design/datacontracts/data_descriptor.md)** - Logical format specification

src/coreclr/debug/runtimeinfo/contract-descriptor.c.in renamed to src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
#include <stdint.h>
5+
#include "contractconfiguration.h"
56

67
#ifdef _MSC_VER
78
#define DLLEXPORT __declspec(dllexport)
89
#else
910
#define DLLEXPORT __attribute__((visibility("default")))
1011
#endif
1112

12-
struct DotNetRuntimeContractDescriptor
13+
struct ContractDescriptor
1314
{
1415
uint64_t magic;
1516
uint32_t flags;
@@ -20,15 +21,18 @@ struct DotNetRuntimeContractDescriptor
2021
const uintptr_t *pointer_data;
2122
};
2223

23-
extern const uintptr_t contractDescriptorPointerData[];
24+
// POINTER_DATA_NAME and CONTRACT_NAME are macros provided by
25+
// contractconfiguration.h which is configured by CMake
26+
extern const uintptr_t POINTER_DATA_NAME[];
2427

25-
DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor;
26-
27-
DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = {
28+
#if EXPORT_CONTRACT
29+
DLLEXPORT
30+
#endif // EXPORT_CONTRACT
31+
struct ContractDescriptor CONTRACT_NAME = {
2832
.magic = 0x0043414443434e44ull, // "DNCCDAC\0"
2933
.flags = %%platformFlags%%,
3034
.descriptor_size = %%jsonDescriptorSize%%,
3135
.descriptor = "%%jsonDescriptor%%",
3236
.pointer_data_count = %%pointerDataCount%%,
33-
.pointer_data = &contractDescriptorPointerData[0],
37+
.pointer_data = &POINTER_DATA_NAME[0],
3438
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#pragma once
2+
3+
#define POINTER_DATA_NAME @POINTER_DATA_NAME@
4+
#define CONTRACT_NAME @CONTRACT_NAME@
5+
6+
#define EXPORT_CONTRACT @EXPORT_CONTRACT@

src/coreclr/debug/runtimeinfo/contractdescriptorstub.c renamed to src/coreclr/debug/datadescriptor-shared/contractdescriptorstub.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
#include <stdint.h>
5+
#include "contractconfiguration.h"
56

67
#ifdef _MSC_VER
78
#define DLLEXPORT __declspec(dllexport)
89
#else
910
#define DLLEXPORT __attribute__((visibility("default")))
1011
#endif
1112

12-
struct DotNetRuntimeContractDescriptor
13+
struct ContractDescriptor
1314
{
1415
uint64_t magic;
1516
uint32_t flags;
@@ -20,20 +21,22 @@ struct DotNetRuntimeContractDescriptor
2021
const uintptr_t *pointer_data;
2122
};
2223

23-
extern const uintptr_t contractDescriptorPointerData[];
24+
// POINTER_DATA_NAME and CONTRACT_NAME are macros provided by
25+
// contractconfiguration.h which is configured by CMake
26+
extern const uintptr_t POINTER_DATA_NAME[];
2427

2528
// just the placeholder pointer
26-
const uintptr_t contractDescriptorPointerData[] = { (uintptr_t)0 };
29+
const uintptr_t POINTER_DATA_NAME[] = { (uintptr_t)0 };
2730

28-
DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor;
31+
DLLEXPORT struct ContractDescriptor CONTRACT_NAME;
2932

3033
#define STUB_DESCRIPTOR "{\"version\":0,\"baseline\":\"empty\",\"contracts\":{},\"types\":{},\"globals\":{}}"
3134

32-
DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = {
35+
DLLEXPORT struct ContractDescriptor CONTRACT_NAME = {
3336
.magic = 0x0043414443434e44ull, // "DNCCDAC\0"
3437
.flags = 0x1u & (sizeof(void*) == 4 ? 0x02u : 0x00u),
3538
.descriptor_size = sizeof(STUB_DESCRIPTOR),
3639
.descriptor = STUB_DESCRIPTOR,
3740
.pointer_data_count = 1,
38-
.pointer_data = &contractDescriptorPointerData[0],
41+
.pointer_data = &POINTER_DATA_NAME[0],
3942
};
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
#include "common.h"
5-
6-
#include <stddef.h>
7-
#include <stdint.h>
8-
9-
#include "cdacplatformmetadata.hpp"
10-
#include "threads.h"
11-
#include "vars.hpp"
4+
#include "datadescriptor.h"
5+
#include "contractconfiguration.h"
126

137
extern "C"
148
{
159
// without an extern declaration, clang does not emit this global into the object file
16-
extern const uintptr_t contractDescriptorPointerData[];
10+
extern const uintptr_t POINTER_DATA_NAME[];
1711

18-
const uintptr_t contractDescriptorPointerData[] = {
12+
const uintptr_t POINTER_DATA_NAME[] = {
1913
(uintptr_t)0, // placeholder
2014
#define CDAC_GLOBAL_POINTER(name,value) (uintptr_t)(value),
21-
#include "datadescriptor.inc"
15+
#define CDAC_GLOBAL_SUB_DESCRIPTOR(name,value) (uintptr_t)(value),
16+
#include "wrappeddatadescriptor.inc"
2217
};
23-
2418
}

0 commit comments

Comments
 (0)