|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// Tracer.cs
//
// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Diagnostics;
using System.IO;
using System.Xml;
using Mono.Cecil;
namespace Mono.Linker
{
/// <summary>
/// Class which implements IDependencyRecorder and writes the dependencies into an XML file.
/// </summary>
public class XmlDependencyRecorder : IDependencyRecorder, IDisposable
{
public const string DefaultDependenciesFileName = "linker-dependencies.xml";
private readonly LinkContext context;
private XmlWriter? writer;
private Stream? stream;
public XmlDependencyRecorder (LinkContext context, string? fileName = null)
{
this.context = context;
XmlWriterSettings settings = new XmlWriterSettings {
Indent = true,
IndentChars = "\t"
};
fileName ??= DefaultDependenciesFileName;
if (string.IsNullOrEmpty (Path.GetDirectoryName (fileName)) && !string.IsNullOrEmpty (context.OutputDirectory)) {
fileName = Path.Combine (context.OutputDirectory, fileName);
Directory.CreateDirectory (context.OutputDirectory);
}
var depsFile = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
stream = depsFile;
writer = XmlWriter.Create (stream, settings);
writer.WriteStartDocument ();
writer.WriteStartElement ("dependencies");
writer.WriteStartAttribute ("version");
writer.WriteString ("1.2");
writer.WriteEndAttribute ();
}
public void FinishRecording ()
{
Debug.Assert (writer != null);
writer.WriteEndElement ();
writer.WriteEndDocument ();
writer.Flush ();
}
public void Dispose ()
{
if (writer == null)
return;
writer.Dispose ();
stream?.Dispose ();
writer = null;
stream = null;
}
public void RecordDependency (object target, in DependencyInfo reason, bool marked)
{
if (writer == null)
throw new InvalidOperationException ();
if (reason.Kind == DependencyKind.Unspecified)
return;
// For now, just report a dependency from source to target without noting the DependencyKind.
RecordDependency (reason.Source, target, marked);
}
public void RecordDependency (object? source, object target, bool marked)
{
if (writer == null)
throw new InvalidOperationException ();
if (!DependencyRecorderHelper.ShouldRecord (context, source) && !DependencyRecorderHelper.ShouldRecord (context, target))
return;
// We use a few hacks to work around MarkStep outputting thousands of edges even
// with the above ShouldRecord checks. Ideally we would format these into a meaningful format
// however I don't think that is worth the effort at the moment.
// Prevent useless logging of attributes like `e="Other:Mono.Cecil.CustomAttribute"`.
if (source is CustomAttribute || target is CustomAttribute)
return;
// Prevent useless logging of interface implementations like `e="InterfaceImpl:Mono.Cecil.InterfaceImplementation"`.
if (source is InterfaceImplementation || target is InterfaceImplementation)
return;
if (source != target) {
writer.WriteStartElement ("edge");
if (marked)
writer.WriteAttributeString ("mark", "1");
writer.WriteAttributeString ("b", DependencyRecorderHelper.TokenString (context, source));
writer.WriteAttributeString ("e", DependencyRecorderHelper.TokenString (context, target));
writer.WriteEndElement ();
}
}
}
}
|