108 lines
3.1 KiB
Python
108 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
performance-log-expand.py -- Delta-decodes PIKA performance logs
|
|
Copyright (C) 2018 Ell
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
Usage: performance-log-expand.py < infile > outfile
|
|
"""
|
|
|
|
from xml.etree import ElementTree
|
|
import sys
|
|
|
|
empty_element = ElementTree.Element ("")
|
|
|
|
# Read performance log from STDIN
|
|
log = ElementTree.fromstring (sys.stdin.buffer.read ())
|
|
|
|
try:
|
|
has_backtrace = bool (int (log.find ("params").find ("backtrace").text))
|
|
except:
|
|
has_backtrace = False
|
|
|
|
def expand_simple (element, last_values):
|
|
last_values.update ({value.tag: value.text for value in element})
|
|
|
|
for value in list (element):
|
|
element.remove (value)
|
|
|
|
for tag, text in last_values.items ():
|
|
value = ElementTree.SubElement (element, tag)
|
|
value.text = text
|
|
value.tail = "\n"
|
|
|
|
# Expand samples
|
|
last_vars = {}
|
|
last_backtrace = {}
|
|
|
|
for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
|
|
# Expand variables
|
|
vars = sample.find ("vars") or \
|
|
ElementTree.SubElement (sample, "vars")
|
|
|
|
expand_simple (vars, last_vars)
|
|
|
|
# Expand backtrace
|
|
if has_backtrace:
|
|
backtrace = sample.find ("backtrace") or \
|
|
ElementTree.SubElement (sample, "backtrace")
|
|
|
|
for thread in backtrace:
|
|
id = thread.get ("id")
|
|
head = thread.get ("head")
|
|
tail = thread.get ("tail")
|
|
attrib = dict (thread.attrib)
|
|
frames = list (thread)
|
|
|
|
last_thread = last_backtrace.setdefault (id, [{}, []])
|
|
last_frames = last_thread[1]
|
|
|
|
if head:
|
|
frames = last_frames[:int (head)] + frames
|
|
|
|
del attrib["head"]
|
|
|
|
if tail:
|
|
frames = frames + last_frames[-int (tail):]
|
|
|
|
del attrib["tail"]
|
|
|
|
last_thread[0] = attrib
|
|
last_thread[1] = frames
|
|
|
|
if not frames and thread.text is None:
|
|
del last_backtrace[id]
|
|
|
|
for thread in list (backtrace):
|
|
backtrace.remove (thread)
|
|
|
|
for id, (attrib, frames) in last_backtrace.items ():
|
|
thread = ElementTree.SubElement (backtrace, "thread", attrib)
|
|
thread.text = "\n"
|
|
thread.tail = "\n"
|
|
|
|
thread.extend (frames)
|
|
|
|
# Expand address map
|
|
last_address = {}
|
|
|
|
for address in (log.find ("address-map") or empty_element).iterfind ("address"):
|
|
expand_simple (address, last_address)
|
|
|
|
# Write performance log to STDOUT
|
|
sys.stdout.buffer.write (ElementTree.tostring (log))
|