Skip to content
Open
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
11 changes: 3 additions & 8 deletions docs/design/datacontracts/Debugger.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Contract Debugger

This contract is for reading debugger state from the target process, including initialization status, configuration flags, metadata update state, and JIT attach state.
This contract is for reading debugger state from the target process, including initialization status, metadata update state, and JIT attach state.

## APIs of contract

```csharp
record struct DebuggerData(bool IsLeftSideInitialized, uint DefinesBitField, uint MDStructuresVersion);
record struct DebuggerData(bool IsLeftSideInitialized);
```

```csharp
Expand Down Expand Up @@ -48,8 +48,6 @@ The contract additionally depends on these data descriptors
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `Debugger` | `LeftSideInitialized` | Whether the left-side debugger infrastructure is initialized |
| `Debugger` | `Defines` | Bitfield of compile-time debugger feature defines |
| `Debugger` | `MDStructuresVersion` | Version of metadata data structures |
| `Debugger` | `RCThread` | Pointer to `DebuggerRCThread` |
| `Debugger` | `RSRequestedSync` | Sync-at-event request flag |
| `Debugger` | `SendExceptionsOutsideOfJMC` | Exception delivery policy flag |
Expand Down Expand Up @@ -84,10 +82,7 @@ bool TryGetDebuggerData(out DebuggerData data)
if (debuggerPtr == TargetPointer.Null)
return false;
bool leftSideInitialized = target.Read<int>(debuggerPtr + /* Debugger::LeftSideInitialized offset */) != 0;
data = new DebuggerData(
IsLeftSideInitialized: leftSideInitialized,
DefinesBitField: target.Read<uint>(debuggerPtr + /* Debugger::Defines offset */),
MDStructuresVersion: target.Read<uint>(debuggerPtr + /* Debugger::MDStructuresVersion offset */));
data = new DebuggerData(IsLeftSideInitialized: leftSideInitialized);
return true;
}

Expand Down
113 changes: 64 additions & 49 deletions docs/design/datacontracts/EcmaMetadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This contract provides methods to get a view of the ECMA-335 metadata for a give
TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle);
TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle);
System.Reflection.Metadata.MetadataReader? GetMetadata(ModuleHandle handle);
byte[] GetReadWriteMetadata(ModuleHandle handle);
```

Types from other contracts:
Expand Down Expand Up @@ -109,58 +110,72 @@ MetadataReader? GetMetadata(ModuleHandle handle)
}
case AvailableMetadataType.ReadWrite:
{
// Get the module's PEAssembly from the Loader contract.
// Read PEAssembly::MDImport as an MDInternalRW.
// Read MDInternalRW::Stgdb as a CLiteWeightStgdbRW.
// Read the embedded CLiteWeightStgdbRW::MiniMd as a CMiniMdRW.
// Read CMiniMdRW::Schema as a CMiniMdSchema.
//
// Validate that CMiniMdRW::TableCount does not exceed the ECMA-335 table count.
// For each table, read its row count from CMiniMdSchema::RecordCounts.
// For each table, test its bit in CMiniMdSchema::Sorted to determine whether it is sorted.
// Decode CMiniMdSchema::Heaps to determine whether the string, GUID, and blob heaps use large indexes.
// Record CMiniMdRW::All4ByteColumns so the reconstructed image can preserve fixed-width variable columns.
//
// To read a storage pool:
// Read the pool head using the StgPool descriptor.
// Record the head segment's SegData and DataSize.
// Follow NextSegment until it is null, reading each remaining node as a StgPoolSeg.
// Record each non-empty segment's SegData and DataSize.
// Allocate one byte array large enough for all recorded segments.
// Read each segment into the array in chain order to produce one contiguous blob.
//
// Read CMiniMdRW::StringHeap as a storage pool.
// Read CMiniMdRW::BlobHeap as a storage pool.
// Read CMiniMdRW::UserStringHeap as a storage pool.
// Read CMiniMdRW::GuidHeap as a storage pool.
// For each table, read CMiniMdRW::Tables[i] as a storage pool containing that table's records.
// Read the metadata version string from CLiteWeightStgdbRW::MetadataAddress.
// Combine the schema, heaps, and table record blobs into a TargetEcmaMetadata value.
//
// Create a builder for a new contiguous ECMA-335 metadata image.
// Write the metadata root header and version string.
// Add stream headers for #Strings, #Blob, #GUID, #US, and the uncompressed tables stream #-.
// If all variable-width columns are 4 bytes, also add the #JTD marker
// stream. The official ECMA-335 metadata format doesn't encode columns this
// way but System.Reflection.Metadata does support this encoding variation
// when it observes the #JTD marker stream.
// See [MetadataReader](https://github.com/dotnet/runtime/blob/1b945942604aa94b4717243b6d301a17b7ae41f1/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.cs#L166)
// Append the string, blob, GUID, and user-string heap data and fill in their stream offsets.
//
// Begin the #- tables stream.
// Write the tables stream header and the heap-size flags from the reconstructed schema.
// Build the valid-table mask from tables with non-zero row counts.
// Build the sorted-table mask from the schema's per-table sorted flags.
// Write the valid and sorted masks.
// Write the row count for each valid table.
// Append each table's contiguous record blob in table-number order.
// Fill in the final tables stream offset and size.
//
// Create a MetadataReaderProvider over the reconstructed image.
// Return its MetadataReader.
// Reconstruct a contiguous ECMA-335 image from the module's writable
// (MDInternalRW) metadata and return a reader over it.
byte[] data = GetReadWriteMetadata(handle);
return MetadataReaderProvider.FromMetadataImage(ImmutableCollectionsMarshal.AsImmutableArray(data)).GetMetadataReader();
}
}
}

// Reconstructs the module's writable (MDInternalRW) metadata as a single contiguous
// ECMA-335 metadata image. The result is cached per module and reused until the
// module's metadata generation counter (Module::MetadataGeneration) changes.
byte[] GetReadWriteMetadata(ModuleHandle handle)
{
// If a blob was previously built for this handle and the module's metadata
// generation counter is unchanged, return the cached blob.

// Get the module's PEAssembly from the Loader contract.
// Read PEAssembly::MDImport as an MDInternalRW.
// Read MDInternalRW::Stgdb as a CLiteWeightStgdbRW.
// Read the embedded CLiteWeightStgdbRW::MiniMd as a CMiniMdRW.
// Read CMiniMdRW::Schema as a CMiniMdSchema.
//
// Validate that CMiniMdRW::TableCount does not exceed the ECMA-335 table count.
// For each table, read its row count from CMiniMdSchema::RecordCounts.
// For each table, test its bit in CMiniMdSchema::Sorted to determine whether it is sorted.
// Decode CMiniMdSchema::Heaps to determine whether the string, GUID, and blob heaps use large indexes.
// Record CMiniMdRW::All4ByteColumns so the reconstructed image can preserve fixed-width variable columns.
//
// To read a storage pool:
// Read the pool head using the StgPool descriptor.
// Record the head segment's SegData and DataSize.
// Follow NextSegment until it is null, reading each remaining node as a StgPoolSeg.
// Record each non-empty segment's SegData and DataSize.
// Allocate one byte array large enough for all recorded segments.
// Read each segment into the array in chain order to produce one contiguous blob.
//
// Read CMiniMdRW::StringHeap as a storage pool.
// Read CMiniMdRW::BlobHeap as a storage pool.
// Read CMiniMdRW::UserStringHeap as a storage pool.
// Read CMiniMdRW::GuidHeap as a storage pool.
// For each table, read CMiniMdRW::Tables[i] as a storage pool containing that table's records.
// Read the metadata version string from CLiteWeightStgdbRW::MetadataAddress.
// Combine the schema, heaps, and table record blobs into a TargetEcmaMetadata value.
//
// Create a builder for a new contiguous ECMA-335 metadata image.
// Write the metadata root header and version string.
// Add stream headers for #Strings, #Blob, #GUID, #US, and the uncompressed tables stream #-.
// If all variable-width columns are 4 bytes, also add the #JTD marker
// stream. The official ECMA-335 metadata format doesn't encode columns this
// way but System.Reflection.Metadata does support this encoding variation
// when it observes the #JTD marker stream.
// See [MetadataReader](https://github.com/dotnet/runtime/blob/1b945942604aa94b4717243b6d301a17b7ae41f1/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.cs#L166)
// Append the string, blob, GUID, and user-string heap data and fill in their stream offsets.
//
// Begin the #- tables stream.
// Write the tables stream header and the heap-size flags from the reconstructed schema.
// Build the valid-table mask from tables with non-zero row counts.
// Build the sorted-table mask from the schema's per-table sorted flags.
// Write the valid and sorted masks.
// Write the row count for each valid table.
// Append each table's contiguous record blob in table-number order.
// Fill in the final tables stream offset and size.
//
// Cache the reconstructed image against the module's current metadata generation
// counter and return it.
}
```

### Helper Methods
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/debug/daccess/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ convert_to_absolute_path(DACCESS_SOURCES ${DACCESS_SOURCES})

add_library_clr(daccess ${DACCESS_SOURCES})
set_target_properties(daccess PROPERTIES DAC_COMPONENT TRUE)
# dacdbiimpl.cpp reconstructs read-write (MDInternalRW) metadata using the internal metadata types.
target_compile_definitions(daccess PRIVATE FEATURE_METADATA_INTERNAL_APIS)
target_precompile_headers(daccess PRIVATE [["stdafx.h"]])
target_link_libraries(daccess PRIVATE cdac_api)

Expand Down
Loading
Loading