| File: StressTestApi\CdacStressApi.cs | Web Access |
| Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Legacy\Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader.Legacy.StressTestApi; // Handlers for the private DACSTRESSPRIV_REQUEST_* opcodes that the // in-proc cDAC stress harness (src/coreclr/vm/cdacstress.cpp) issues // through IXCLRDataProcess::Request. Kept out of SOSDacImpl so the // stress-only surface is grouped in one place; SOSDacImpl just // delegates when it sees one of these reqCodes. internal static unsafe class CdacStressApi { public const uint RequestFlushTargetState = 0xf2000000; public const uint RequestComputeArgGCRefMap = 0xf2000001; // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER). private const int HResultErrorInsufficientBuffer = unchecked((int)0x8007007A); public static bool IsStressRequest(uint reqCode) => reqCode == RequestFlushTargetState || reqCode == RequestComputeArgGCRefMap; public static int HandleRequest(Target target, uint reqCode, uint inSize, byte* inBuffer, uint outSize, byte* outBuffer) { return reqCode switch { RequestFlushTargetState => HandleFlushTargetState(target, inSize, inBuffer, outSize, outBuffer), RequestComputeArgGCRefMap => HandleComputeArgGCRefMap(target, inSize, inBuffer, outSize, outBuffer), _ => HResults.E_INVALIDARG, }; } private static int HandleFlushTargetState(Target target, uint inSize, byte* inBuffer, uint outSize, byte* outBuffer) { if (inSize != 0 || inBuffer is not null || outSize != 0 || outBuffer is not null) return HResults.E_INVALIDARG; target.Flush(FlushScope.ForwardExecution); return HResults.S_OK; } // Mirrors DacStressArgGCRefMapRequest in src/coreclr/inc/dacprivate.h. // The caller hands us an [in,out] descriptor with the MethodDesc plus a // caller-allocated destination buffer; we write the blob there and // populate cbFilled / cbNeeded. The COM `outBuffer` channel is unused. [StructLayout(LayoutKind.Sequential)] private struct DacStressArgGCRefMapRequest { public ulong MethodDesc; public ulong BlobBuffer; public uint BlobBufferLen; public uint cbFilled; public uint cbNeeded; } private static int HandleComputeArgGCRefMap(Target target, uint inSize, byte* inBuffer, uint outSize, byte* outBuffer) { _ = outSize; _ = outBuffer; if (inBuffer is null || inSize < (uint)sizeof(DacStressArgGCRefMapRequest)) return HResults.E_INVALIDARG; // Alignment-safe view of the [in,out] descriptor. The cDAC ABI hands // us a `byte*` from a COM marshaller with no guaranteed alignment. DacStressArgGCRefMapRequest req = Unsafe.ReadUnaligned<DacStressArgGCRefMapRequest>(inBuffer); byte[] blob; bool encoded; try { IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; MethodDescHandle mdh = rts.GetMethodDescHandle( new ClrDataAddress(req.MethodDesc).ToTargetPointer(target)); encoded = target.Contracts.CallingConvention.TryComputeArgGCRefMapBlob(mdh, out blob); } catch { req.cbFilled = 0; req.cbNeeded = 0; Unsafe.WriteUnaligned(inBuffer, req); return HResults.E_FAIL; } if (!encoded) { req.cbFilled = 0; req.cbNeeded = 0; Unsafe.WriteUnaligned(inBuffer, req); return HResults.E_NOTIMPL; } uint needed = (uint)blob.Length; req.cbNeeded = needed; if (req.BlobBuffer == 0 || req.BlobBufferLen < needed) { req.cbFilled = 0; Unsafe.WriteUnaligned(inBuffer, req); return HResultErrorInsufficientBuffer; } byte* dest = (byte*)(nuint)req.BlobBuffer; blob.AsSpan().CopyTo(new Span<byte>(dest, (int)req.BlobBufferLen)); req.cbFilled = needed; Unsafe.WriteUnaligned(inBuffer, req); return HResults.S_OK; } }