202 lines
7.1 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|