- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency. - Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling. - Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies. - Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification. - Create validation script for CI/CD templates ensuring all required files and structures are present.
405 lines
9.7 KiB
C#
405 lines
9.7 KiB
C#
namespace StellaOps.Signals.Services;
|
|
|
|
/// <summary>
|
|
/// Symbol canonicalization service interface.
|
|
/// Sprint: SPRINT_20251226_010_SIGNALS_runtime_stack
|
|
/// Task: STACK-06
|
|
///
|
|
/// Resolves program counter addresses to canonical (Build-ID, function, offset) tuples.
|
|
/// </summary>
|
|
public interface ISymbolCanonicalizationService
|
|
{
|
|
/// <summary>
|
|
/// Resolves a program counter address to a canonical symbol.
|
|
/// </summary>
|
|
/// <param name="address">The program counter address.</param>
|
|
/// <param name="buildId">The ELF Build-ID of the binary.</param>
|
|
/// <param name="binaryPath">Optional path to the binary for symbol lookup.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The resolved symbol, or null if resolution failed.</returns>
|
|
Task<CanonicalSymbol?> ResolveAsync(
|
|
ulong address,
|
|
string? buildId,
|
|
string? binaryPath = null,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Resolves multiple addresses in batch for efficiency.
|
|
/// </summary>
|
|
Task<IReadOnlyList<CanonicalSymbol?>> ResolveBatchAsync(
|
|
IReadOnlyList<SymbolResolutionRequest> requests,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Resolves a Java frame using JVMTI metadata.
|
|
/// </summary>
|
|
Task<CanonicalSymbol?> ResolveJavaFrameAsync(
|
|
ulong address,
|
|
JavaFrameMetadata metadata,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Resolves a .NET frame using DAC (Data Access Component).
|
|
/// </summary>
|
|
Task<CanonicalSymbol?> ResolveDotNetFrameAsync(
|
|
ulong address,
|
|
DotNetFrameMetadata metadata,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Resolves a Python frame using interpreter symbols.
|
|
/// </summary>
|
|
Task<CanonicalSymbol?> ResolvePythonFrameAsync(
|
|
ulong address,
|
|
PythonFrameMetadata metadata,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Checks if a Build-ID is in the local symbol cache.
|
|
/// </summary>
|
|
Task<bool> IsInCacheAsync(string buildId, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Adds symbols for a Build-ID to the cache.
|
|
/// </summary>
|
|
Task CacheSymbolsAsync(
|
|
string buildId,
|
|
IReadOnlyList<SymbolEntry> symbols,
|
|
CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Request for symbol resolution.
|
|
/// </summary>
|
|
public sealed record SymbolResolutionRequest
|
|
{
|
|
/// <summary>
|
|
/// Program counter address.
|
|
/// </summary>
|
|
public required ulong Address { get; init; }
|
|
|
|
/// <summary>
|
|
/// ELF Build-ID of the binary.
|
|
/// </summary>
|
|
public string? BuildId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Path to the binary file.
|
|
/// </summary>
|
|
public string? BinaryPath { get; init; }
|
|
|
|
/// <summary>
|
|
/// Runtime type hint for managed runtimes.
|
|
/// </summary>
|
|
public RuntimeType RuntimeType { get; init; } = RuntimeType.Native;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runtime type for symbol resolution hints.
|
|
/// </summary>
|
|
public enum RuntimeType
|
|
{
|
|
/// <summary>
|
|
/// Native code (C, C++, Rust, Go, etc.).
|
|
/// </summary>
|
|
Native,
|
|
|
|
/// <summary>
|
|
/// Java Virtual Machine.
|
|
/// </summary>
|
|
Java,
|
|
|
|
/// <summary>
|
|
/// .NET Common Language Runtime.
|
|
/// </summary>
|
|
DotNet,
|
|
|
|
/// <summary>
|
|
/// Python interpreter.
|
|
/// </summary>
|
|
Python,
|
|
|
|
/// <summary>
|
|
/// Node.js / V8.
|
|
/// </summary>
|
|
NodeJs,
|
|
|
|
/// <summary>
|
|
/// Ruby interpreter.
|
|
/// </summary>
|
|
Ruby,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Canonical symbol representation.
|
|
/// </summary>
|
|
public sealed record CanonicalSymbol
|
|
{
|
|
/// <summary>
|
|
/// Original address that was resolved.
|
|
/// </summary>
|
|
public required ulong Address { get; init; }
|
|
|
|
/// <summary>
|
|
/// ELF Build-ID of the containing binary.
|
|
/// </summary>
|
|
public required string BuildId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Demangled function name.
|
|
/// </summary>
|
|
public required string FunctionName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Offset within the function.
|
|
/// </summary>
|
|
public required ulong Offset { get; init; }
|
|
|
|
/// <summary>
|
|
/// Module or binary name.
|
|
/// </summary>
|
|
public string? ModuleName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Source file path (if debug info available).
|
|
/// </summary>
|
|
public string? SourceFile { get; init; }
|
|
|
|
/// <summary>
|
|
/// Source line number.
|
|
/// </summary>
|
|
public int? SourceLine { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether this symbol is from a trusted source.
|
|
/// </summary>
|
|
public bool IsTrusted { get; init; }
|
|
|
|
/// <summary>
|
|
/// Resolution method used.
|
|
/// </summary>
|
|
public SymbolResolutionMethod ResolutionMethod { get; init; }
|
|
|
|
/// <summary>
|
|
/// Returns the canonical string format.
|
|
/// </summary>
|
|
public string ToCanonicalString()
|
|
{
|
|
return $"{BuildId[..Math.Min(16, BuildId.Length)]}:{FunctionName}+0x{Offset:x}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a canonical string format.
|
|
/// </summary>
|
|
public static CanonicalSymbol? Parse(string canonical)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(canonical))
|
|
return null;
|
|
|
|
// Format: "buildid:function+0xoffset"
|
|
var colonIdx = canonical.IndexOf(':');
|
|
if (colonIdx < 0)
|
|
return null;
|
|
|
|
var buildId = canonical[..colonIdx];
|
|
var rest = canonical[(colonIdx + 1)..];
|
|
|
|
var plusIdx = rest.LastIndexOf('+');
|
|
if (plusIdx < 0)
|
|
return null;
|
|
|
|
var functionName = rest[..plusIdx];
|
|
var offsetStr = rest[(plusIdx + 1)..];
|
|
|
|
if (!offsetStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
|
return null;
|
|
|
|
if (!ulong.TryParse(offsetStr[2..], System.Globalization.NumberStyles.HexNumber, null, out var offset))
|
|
return null;
|
|
|
|
return new CanonicalSymbol
|
|
{
|
|
Address = 0, // Not recoverable from canonical string
|
|
BuildId = buildId,
|
|
FunctionName = functionName,
|
|
Offset = offset,
|
|
ResolutionMethod = SymbolResolutionMethod.Parsed,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method used to resolve the symbol.
|
|
/// </summary>
|
|
public enum SymbolResolutionMethod
|
|
{
|
|
/// <summary>
|
|
/// Resolved from ELF symbol table.
|
|
/// </summary>
|
|
ElfSymtab,
|
|
|
|
/// <summary>
|
|
/// Resolved from DWARF debug info.
|
|
/// </summary>
|
|
DwarfDebugInfo,
|
|
|
|
/// <summary>
|
|
/// Resolved from local symbol cache.
|
|
/// </summary>
|
|
LocalCache,
|
|
|
|
/// <summary>
|
|
/// Resolved from debuginfod server.
|
|
/// </summary>
|
|
Debuginfod,
|
|
|
|
/// <summary>
|
|
/// Resolved from JIT metadata (Java/V8/etc).
|
|
/// </summary>
|
|
JitMetadata,
|
|
|
|
/// <summary>
|
|
/// Resolved from runtime-specific mechanism.
|
|
/// </summary>
|
|
RuntimeSpecific,
|
|
|
|
/// <summary>
|
|
/// Parsed from canonical string format.
|
|
/// </summary>
|
|
Parsed,
|
|
|
|
/// <summary>
|
|
/// Could not resolve, using address only.
|
|
/// </summary>
|
|
Unresolved,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Symbol entry for cache storage.
|
|
/// </summary>
|
|
public sealed record SymbolEntry
|
|
{
|
|
/// <summary>
|
|
/// Start address of the symbol.
|
|
/// </summary>
|
|
public required ulong StartAddress { get; init; }
|
|
|
|
/// <summary>
|
|
/// Size of the symbol in bytes.
|
|
/// </summary>
|
|
public required ulong Size { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol name (demangled).
|
|
/// </summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol type.
|
|
/// </summary>
|
|
public SymbolType Type { get; init; } = SymbolType.Function;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of symbol.
|
|
/// </summary>
|
|
public enum SymbolType
|
|
{
|
|
Function,
|
|
Object,
|
|
Unknown,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metadata for Java frame resolution.
|
|
/// </summary>
|
|
public sealed record JavaFrameMetadata
|
|
{
|
|
/// <summary>
|
|
/// Class name.
|
|
/// </summary>
|
|
public string? ClassName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Method name.
|
|
/// </summary>
|
|
public string? MethodName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Method signature.
|
|
/// </summary>
|
|
public string? Signature { get; init; }
|
|
|
|
/// <summary>
|
|
/// Bytecode index.
|
|
/// </summary>
|
|
public int? BytecodeIndex { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether this is a JIT-compiled frame.
|
|
/// </summary>
|
|
public bool IsJit { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metadata for .NET frame resolution.
|
|
/// </summary>
|
|
public sealed record DotNetFrameMetadata
|
|
{
|
|
/// <summary>
|
|
/// Type name.
|
|
/// </summary>
|
|
public string? TypeName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Method name.
|
|
/// </summary>
|
|
public string? MethodName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Method token.
|
|
/// </summary>
|
|
public uint? MethodToken { get; init; }
|
|
|
|
/// <summary>
|
|
/// IL offset.
|
|
/// </summary>
|
|
public int? IlOffset { get; init; }
|
|
|
|
/// <summary>
|
|
/// Assembly name.
|
|
/// </summary>
|
|
public string? AssemblyName { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metadata for Python frame resolution.
|
|
/// </summary>
|
|
public sealed record PythonFrameMetadata
|
|
{
|
|
/// <summary>
|
|
/// Module name.
|
|
/// </summary>
|
|
public string? ModuleName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Function name.
|
|
/// </summary>
|
|
public string? FunctionName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Source file path.
|
|
/// </summary>
|
|
public string? SourceFile { get; init; }
|
|
|
|
/// <summary>
|
|
/// Line number.
|
|
/// </summary>
|
|
public int? LineNumber { get; init; }
|
|
|
|
/// <summary>
|
|
/// Python version.
|
|
/// </summary>
|
|
public string? PythonVersion { get; init; }
|
|
}
|