diff --git a/tools/Makefile.am b/tools/Makefile.am index 2dbcb89fd6..4c5420f4e5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -96,6 +96,8 @@ AM_LDFLAGS = \ $(xnone) EXTRA_DIST = \ - defcheck.py \ - gimp-mkenums \ - gimppath2svg.py + defcheck.py \ + gimp-mkenums \ + gimppath2svg.py \ + performance-log-expand.py \ + performance-log-resolve.py diff --git a/tools/performance-log-expand.py b/tools/performance-log-expand.py new file mode 100755 index 0000000000..c0f7cc9471 --- /dev/null +++ b/tools/performance-log-expand.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +""" +performance-log-expand.py -- Delta-decodes GIMP 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 . + + +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") + frames = list (thread) + + if not frames: + last_backtrace.pop (id, None) + else: + last_thread = last_backtrace.setdefault (id, [None, []]) + last_frames = last_thread[1] + + name = thread.get ("name") + head = thread.get ("head") + tail = thread.get ("tail") + + if head: + frames = last_frames[:int (head)] + frames + if tail: + frames = frames + last_frames[-int (tail):] + + last_thread[0] = name + last_thread[1] = frames + + for thread in list (backtrace): + backtrace.remove (thread) + + for id, (name, frames) in last_backtrace.items (): + thread = ElementTree.SubElement (backtrace, "thread", id=id) + thread.text = "\n" + thread.tail = "\n" + + if name: + thread.set ("name", name) + + 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)) diff --git a/tools/performance-log-resolve.py b/tools/performance-log-resolve.py new file mode 100755 index 0000000000..272f0ebb24 --- /dev/null +++ b/tools/performance-log-resolve.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +""" +performance-log-resolve.py -- Resolve GIMP performance log backtrace symbols +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 . + + +Usage: performance-log-resolve.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 ()) + +address_map = log.find ("address-map") + +if address_map: + # Create address dictionary + addresses = {} + + for address in address_map.iterfind ("address"): + addresses[address.get ("value")] = list (address) + + # Resolve addresses in backtraces + for sample in (log.find ("samples") or empty_element).iterfind ("sample"): + for thread in sample.find ("backtrace") or (): + for frame in thread: + address = addresses.get (frame.get ("address")) + + if address: + frame.text = "\n" + frame.extend (address) + + # Remove address map + log.remove (address_map) + +# Write performance log to STDOUT +sys.stdout.buffer.write (ElementTree.tostring (log))