If you want to try my script, or even improve it (probably not hard!), it's
attached.
You can run it by first collecting a log of timestamps, which I did with a
clean build:
scons --debug=action-timestamps -j48 build/ARM/gem5.opt | tee log.txt
and then run the script:
./scons_timeline.py log.txt
I would be curious how that looks on our big compile test machine!
Gabe
On Mon, Oct 18, 2021 at 10:20 PM Gabe Black <[email protected]> wrote:
> Wow, now I remember why I don't like GUI programming :-/
>
> Well, I sort of got some results, and the upshot is that linking gem5
> takes a long time :-P. And I am able to at least superficially have 48
> things going during the build, although it is not necessarily 48 times
> faster than doing things one at a time.
>
> Gabe
>
> On Mon, Oct 18, 2021 at 5:45 PM Gabe Black <[email protected]> wrote:
>
>> Hey folks, a while ago some students developed a GUI tool for gem5. While
>> I think that was great and really hasn't gotten the attention (or
>> adoption?) that a tool like that should, I was looking for it for a more
>> practical reason but haven't been able to find it.
>>
>> Does anybody know where it ended up? And additionally/alternatively, does
>> anybody know what guil toolkit that used? I want to write up a quick SCons
>> build performance visualizer and (assuming it goes into util?) I wouldn't
>> want to add a second gui toolkit dependency.
>>
>> Thanks!
>> Gabe
>>
>
#! /usr/bin/env python3
#
# Copyright 2021 Google, Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import argparse
import collections
import re
import wx
import wx.lib.plot
parser = argparse.ArgumentParser(
description='Visualize SCons action timestamps')
parser.add_argument('log_file',
help="file with SCon's log output when run with --debug-action-timestamps")
args = parser.parse_args()
class Bound:
def __init__(self, target, name, value):
self.target = target
self.is_start = (name == 'start')
self.value = float(value)
class Target:
def __init__(self):
self.name = None
self._start = None
self._end = None
@property
def start(self):
assert(self._start is not None)
return self._start
@property
def end(self):
assert(self._end is not None)
return self._end
@start.setter
def start(self, bound):
assert(self._start is None)
self._start = bound
@end.setter
def end(self, bound):
assert(self._end is None)
self._end = bound
targets = collections.defaultdict(Target)
timestamp_re = re.compile(r'Command execution (?P<ttype>start|end) '
r'timestamp: (?P<name>[^:]*): '
r'(?P<seconds>[\d\.]*)')
skipped = 0
with open(args.log_file) as log:
lines = log.readlines()
for line in lines:
match = timestamp_re.match(line)
if not match:
skipped = skipped + 1
continue
name = match.group('name')
target = targets[name]
target.name = name
bound = Bound(target, match.group('ttype'), match.group('seconds'))
if bound.is_start:
target.start = bound
else:
target.end = bound
# Find the bounds for the times for all the targets.
min_start = min(target.start.value for target in targets.values())
max_end = max(target.end.value for target in targets.values())
bounds = []
for target in targets.values():
bounds.extend([target.start, target.end])
bounds = sorted(bounds, key=lambda b: b.value)
class Timeline(wx.Frame):
def __init__(self):
super(Timeline, self).__init__(None, title='SCons timeline')
panel = wx.Panel(self)
in_progress = list()
available_idx = list()
self.canvas = wx.lib.plot.PlotCanvas(panel)
bars = []
for bound in bounds:
if bound.is_start:
target = bound.target
start = target.start
end = target.end
if not available_idx:
idx = len(in_progress)
in_progress.append(target)
else:
idx = available_idx.pop()
in_progress[idx] = target
bars.append(wx.lib.plot.PolyLine(
[(start.value - min_start, idx + 1),
(end.value - min_start, idx + 1)],
colour="orange", width=10))
else:
idx = in_progress.index(bound.target)
in_progress[idx] = None
available_idx.append(idx)
print(f"I have {len(bars)} bars!")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL, 10)
panel.SetSizer(sizer)
graphics = wx.lib.plot.PlotGraphics(bars, "SCons timeline",
"seconds", "job")
self.canvas.Draw(graphics)
self.canvas.showScrollbars = True
self.canvas.enableZoom = True
app = wx.App()
timeline = Timeline()
timeline.Show()
app.MainLoop()
_______________________________________________
gem5-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s