File: EnumMemory\Sanitizer.cs
Web Access
Project: 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.Buffers.Binary;
using System.Text;

namespace Microsoft.Diagnostics.DataContractReader.Legacy.EnumMemory;

internal static class Sanitizer
{
    internal static void SanitizePdbPath(Target target, MemoryRegionEmitter emitter, TargetSpan codeView)
    {
        const uint RsdsSignature = 0x53445352;
        const uint PdbPathOffset = 24;
        if (codeView.Size <= PdbPathOffset)
            return;

        Span<byte> buffer = stackalloc byte[256];
        target.ReadBuffer(codeView.Address, buffer[..sizeof(uint)]);
        if (BinaryPrimitives.ReadUInt32LittleEndian(buffer) != RsdsSignature)
            return;

        ulong pathAddress = codeView.Address.Value + PdbPathOffset;
        ulong pathSize = codeView.Size - PdbPathOffset;
        ulong fileNameStart = 0;
        ulong length = 0;
        bool terminated = false;
        while (length < pathSize && !terminated)
        {
            int count = (int)Math.Min((ulong)buffer.Length, pathSize - length);
            target.ReadBuffer(pathAddress + length, buffer[..count]);
            for (int i = 0; i < count; i++, length++)
            {
                if (buffer[i] == 0)
                {
                    terminated = true;
                    break;
                }
                if (buffer[i] is (byte)'\\' or (byte)'/')
                    fileNameStart = length + 1;
            }
        }
        if (!terminated)
            throw new InvalidOperationException("The CodeView PDB path is not null-terminated.");

        ulong fileNameLength = length - fileNameStart;
        ulong written = 0;
        while (written < fileNameLength)
        {
            int count = (int)Math.Min((ulong)buffer.Length, fileNameLength - written);
            target.ReadBuffer(pathAddress + fileNameStart + written, buffer[..count]);
            if (!emitter.Update(pathAddress + written, buffer[..count]))
                return;
            written += (uint)count;
        }
        buffer.Clear();
        while (written < pathSize)
        {
            int count = (int)Math.Min((ulong)buffer.Length, pathSize - written);
            if (!emitter.Update(pathAddress + written, buffer[..count]))
                return;
            written += (uint)count;
        }
    }

    internal static void StripArgumentsFromMethodName(StringBuilder name)
    {
        for (int i = 0; i < name.Length; i++)
        {
            if (name[i] != '(')
                continue;

            // Preserve "()" because !analyze expects an argument list, matching the native DAC.
            name.Length = i + 1;
            name.Append(')');
            break;
        }
    }

    internal static void StripFileInfoFromStackTrace(Span<char> buffer)
    {
        // Mirror StripFileInfoFromStackTrace in vm/excep.cpp, including trailing-text removal.
        int depth = 0;
        int written = 0;
        int lastMethodEnd = 0;
        for (int i = 0; i < buffer.Length; i++)
        {
            char c = buffer[i];
            buffer[written++] = c;
            if (c == '(')
                depth++;
            else if (c == ')')
            {
                if (depth == 1)
                {
                    lastMethodEnd = written;
                    while (i + 1 < buffer.Length && buffer[i + 1] is not '\r' and not '\n')
                        i++;
                }
                depth--;
            }
        }

        buffer[lastMethodEnd..].Clear();
    }
}