foundationdb/contrib/TraceLogHelper/XmlParser.cs

202 lines
7.1 KiB
C#

/*
* XmlParser.cs
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace Magnesium
{
public static class XmlParser
{
static Random r = new Random();
public static IEnumerable<Event> Parse(System.IO.Stream stream, string file,
bool keepOriginalElement = false, double startTime = -1, double endTime = Double.MaxValue,
double samplingFactor = 1.0, Action<string> nonFatalErrorMessage = null)
{
using (var reader = XmlReader.Create(stream))
{
reader.ReadToDescendant("Trace");
reader.Read();
// foreach (var xev in StreamElements(reader))
// need to be able to catch and save non-fatal exceptions in StreamElements, so use explicit iterator instead of foreach
var iter = StreamElements(reader).GetEnumerator();
while (true)
{
try {
if (!iter.MoveNext()) {
break;
}
} catch (Exception e) {
if (nonFatalErrorMessage != null) {
nonFatalErrorMessage(e.Message);
}
break;
}
var xev = iter.Current;
Event ev = null;
try
{
if (xev.Name == "Event")
ev = ParseEvent(xev, file, keepOriginalElement, startTime, endTime, samplingFactor);
else if (xev.Name == "Test")
ev = ParseTest(xev, file, keepOriginalElement);
else if (xev.Name == "TestPlan")
ev = ParseTestPlan(xev, file, keepOriginalElement);
}
catch (Exception e)
{
throw new Exception(string.Format("Failed to parse XML {0}", xev), e);
}
if (ev != null) yield return ev;
}
}
}
private static Event ParseEvent(XElement xEvent, string file, bool keepOriginalElement, double startTime, double endTime, double samplingFactor)
{
if (samplingFactor != 1.0 && r.NextDouble() > samplingFactor)
return null;
XAttribute trackLatestAttribute = xEvent.Attribute("TrackLatestType");
bool rolledEvent = trackLatestAttribute != null && trackLatestAttribute.Value.Equals("Rolled");
String timeAttribute = (rolledEvent) ? "OriginalTime" : "Time";
double eventTime = double.Parse(xEvent.Attribute(timeAttribute).Value);
if (eventTime < startTime || eventTime > endTime)
return null;
return new Event {
Severity = (Severity)int.Parse(xEvent.Attribute("Severity").ValueOrDefault("40")),
Type = string.Intern(xEvent.Attribute("Type").Value),
Time = eventTime,
Machine = string.Intern(xEvent.Attribute("Machine").Value),
ID = string.Intern(xEvent.Attribute("ID").ValueOrDefault("0")),
TraceFile = file,
DDetails = xEvent.Attributes()
.Where(a=>a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "ID" && a.Name != "Severity" && (!rolledEvent || a.Name != "OriginalTime"))
.ToDictionary(a=>string.Intern(a.Name.LocalName), a=>(object)a.Value),
original = keepOriginalElement ? xEvent : null,
};
}
private static string ValueOrDefault( this XAttribute attr, string def ) {
if (attr == null) return def;
else return attr.Value;
}
private static TestPlan ParseTestPlan(XElement xTP, string file, bool keepOriginalElement)
{
var time = double.Parse(xTP.Attribute("Time").ValueOrDefault("0"));
var machine = xTP.Attribute("Machine").ValueOrDefault("");
return new TestPlan
{
TraceFile = file,
Type = "TestPlan",
Time = time,
Machine = machine,
TestUID = xTP.Attribute("TestUID").ValueOrDefault(""),
TestFile = xTP.Attribute("TestFile").ValueOrDefault(""),
randomSeed = int.Parse(xTP.Attribute("RandomSeed").ValueOrDefault("0")),
Buggify = xTP.Attribute("BuggifyEnabled").ValueOrDefault("1") != "0",
DeterminismCheck = xTP.Attribute("DeterminismCheck").ValueOrDefault("1") != "0",
OldBinary = xTP.Attribute("OldBinary").ValueOrDefault(""),
original = keepOriginalElement ? xTP : null,
};
}
private static Test ParseTest(XElement xTest, string file, bool keepOriginalElement)
{
var time = double.Parse(xTest.Attribute("Time").ValueOrDefault("0"));
var machine = xTest.Attribute("Machine").ValueOrDefault("");
return new Test
{
TraceFile = file,
Type = "Test",
Time = time,
Machine = machine,
TestUID = xTest.Attribute("TestUID").ValueOrDefault(""),
TestFile = xTest.Attribute("TestFile").ValueOrDefault(""),
SourceVersion = xTest.Attribute("SourceVersion").ValueOrDefault(""),
ok = bool.Parse(xTest.Attribute("OK").ValueOrDefault("false")),
randomSeed = int.Parse(xTest.Attribute("RandomSeed").ValueOrDefault("0")),
randomUnseed = int.Parse(xTest.Attribute("RandomUnseed").ValueOrDefault("0")),
SimElapsedTime = double.Parse(xTest.Attribute("SimElapsedTime").ValueOrDefault("0")),
RealElapsedTime = double.Parse(xTest.Attribute("RealElapsedTime").ValueOrDefault("0")),
passed = int.Parse(xTest.Attribute("Passed").ValueOrDefault("0")),
failed = int.Parse(xTest.Attribute("Failed").ValueOrDefault("0")),
peakMemUsage = long.Parse(xTest.Attribute("PeakMemory").ValueOrDefault("0")),
Buggify = xTest.Attribute("BuggifyEnabled").ValueOrDefault("1") != "0",
DeterminismCheck = xTest.Attribute("DeterminismCheck").ValueOrDefault("1") != "0",
OldBinary = xTest.Attribute("OldBinary").ValueOrDefault(""),
original = keepOriginalElement ? xTest : null,
events = xTest.Elements().Select(e =>
new Event {
Severity = (Severity)int.Parse(e.Attribute("Severity").ValueOrDefault("0")),
Type = e.Name.LocalName,
Time = time,
Machine = machine,
DDetails = e.Attributes()
.Where(a => a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "Severity")
.ToDictionary(a => a.Name.LocalName, a => (object)a.Value)
}).ToArray()
};
}
private static T Try<T>(Func<T> action, Func<Exception, T> onError, Func<bool> isEOF)
{
try
{
return action();
}
catch (Exception e)
{
if (isEOF())
return onError(e);
else
throw e;
}
}
// throws exceptions if xml is invalid
private static IEnumerable<XElement> StreamElements(this XmlReader reader)
{
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
XElement node = XElement.ReadFrom(reader) as XElement;
if (node != null)
yield return node;
}
else
{
reader.Read();
}
}
}
}
}