perf, sched migration: Make the GUI class client agnostic

Make the perf migration GUI generic so that it can be reused for
other kinds of trace painting. No more notion of CPUs or runqueue
from the GUI class, it's now used as a library by the trace parser.

Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Nikhil Rao <ncrao@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
This commit is contained in:
Frederic Weisbecker 2010-07-26 02:02:39 +02:00
parent 70d815a3de
commit 699b6d922c

View file

@ -28,11 +28,11 @@ from Core import *
class RootFrame(wx.Frame): class RootFrame(wx.Frame):
Y_OFFSET = 100 Y_OFFSET = 100
CPU_HEIGHT = 100 RECT_HEIGHT = 100
CPU_SPACE = 50 RECT_SPACE = 50
EVENT_MARKING_WIDTH = 5 EVENT_MARKING_WIDTH = 5
def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): def __init__(self, sched_tracer, title, parent = None, id = -1):
wx.Frame.__init__(self, parent, id, title) wx.Frame.__init__(self, parent, id, title)
(self.screen_width, self.screen_height) = wx.GetDisplaySize() (self.screen_width, self.screen_height) = wx.GetDisplaySize()
@ -40,11 +40,12 @@ class RootFrame(wx.Frame):
self.screen_height -= 10 self.screen_height -= 10
self.zoom = 0.5 self.zoom = 0.5
self.scroll_scale = 20 self.scroll_scale = 20
self.timeslices = timeslices self.sched_tracer = sched_tracer
(self.ts_start, self.ts_end) = timeslices.interval() self.sched_tracer.set_root_win(self)
(self.ts_start, self.ts_end) = sched_tracer.interval()
self.update_width_virtual() self.update_width_virtual()
self.nr_cpus = timeslices.max_cpu() + 1 self.nr_rects = sched_tracer.nr_rectangles() + 1
self.height_virtual = RootFrame.Y_OFFSET + (self.nr_cpus * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
# whole window panel # whole window panel
self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height))
@ -87,69 +88,38 @@ class RootFrame(wx.Frame):
(x, y) = self.scroll_start() (x, y) = self.scroll_start()
return self.px_to_us(x) return self.px_to_us(x)
def update_rectangle_cpu(self, dc, slice, cpu, offset_time): def paint_rectangle_zone(self, nr, color, top_color, start, end):
rq = slice.rqs[cpu] offset_px = self.us_to_px(start - self.ts_start)
width_px = self.us_to_px(end - self.ts_start)
if slice.total_load != 0: offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
load_rate = rq.load() / float(slice.total_load) width_py = RootFrame.RECT_HEIGHT
else:
load_rate = 0
dc = self.dc
offset_px = self.us_to_px(slice.start - offset_time) if top_color is not None:
width_px = self.us_to_px(slice.end - slice.start) (r, g, b) = top_color
(x, y) = self.scroll_start() top_color = wx.Colour(r, g, b)
brush = wx.Brush(top_color, wx.SOLID)
dc.SetBrush(brush)
dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH)
width_py -= RootFrame.EVENT_MARKING_WIDTH
offset_py += RootFrame.EVENT_MARKING_WIDTH
if width_px == 0: (r ,g, b) = color
return color = wx.Colour(r, g, b)
offset_py = RootFrame.Y_OFFSET + (cpu * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE))
width_py = RootFrame.CPU_HEIGHT
if cpu in slice.event_cpus:
rgb = rq.event.color()
if rgb is not None:
(r, g, b) = rgb
color = wx.Colour(r, g, b)
brush = wx.Brush(color, wx.SOLID)
dc.SetBrush(brush)
dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH)
width_py -= RootFrame.EVENT_MARKING_WIDTH
offset_py += RootFrame.EVENT_MARKING_WIDTH
red_power = int(0xff - (0xff * load_rate))
color = wx.Colour(0xff, red_power, red_power)
brush = wx.Brush(color, wx.SOLID) brush = wx.Brush(color, wx.SOLID)
dc.SetBrush(brush) dc.SetBrush(brush)
dc.DrawRectangle(offset_px, offset_py, width_px, width_py) dc.DrawRectangle(offset_px, offset_py, width_px, width_py)
def update_rectangles(self, dc, start, end): def update_rectangles(self, dc, start, end):
if len(self.timeslices) == 0: start += self.ts_start
return end += self.ts_start
start += self.timeslices[0].start self.sched_tracer.fill_zone(start, end)
end += self.timeslices[0].start
color = wx.Colour(0, 0, 0)
brush = wx.Brush(color, wx.SOLID)
dc.SetBrush(brush)
i = self.timeslices.find_time_slice(start)
if i == -1:
return
for i in xrange(i, len(self.timeslices)):
timeslice = self.timeslices[i]
if timeslice.start > end:
return
for cpu in timeslice.rqs:
self.update_rectangle_cpu(dc, timeslice, cpu, self.timeslices[0].start)
def on_paint(self, event): def on_paint(self, event):
color = wx.Colour(0xff, 0xff, 0xff)
brush = wx.Brush(color, wx.SOLID)
dc = wx.PaintDC(self.scroll_panel) dc = wx.PaintDC(self.scroll_panel)
dc.SetBrush(brush) self.dc = dc
width = min(self.width_virtual, self.screen_width) width = min(self.width_virtual, self.screen_width)
(x, y) = self.scroll_start() (x, y) = self.scroll_start()
@ -157,45 +127,31 @@ class RootFrame(wx.Frame):
end = self.px_to_us(x + width) end = self.px_to_us(x + width)
self.update_rectangles(dc, start, end) self.update_rectangles(dc, start, end)
def cpu_from_ypixel(self, y): def rect_from_ypixel(self, y):
y -= RootFrame.Y_OFFSET y -= RootFrame.Y_OFFSET
cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
if cpu < 0 or cpu > self.nr_cpus - 1 or height > RootFrame.CPU_HEIGHT: if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT:
return -1 return -1
return cpu return rect
def update_summary(self, cpu, t):
idx = self.timeslices.find_time_slice(t)
if idx == -1:
return
ts = self.timeslices[idx]
rq = ts.rqs[cpu]
raw = "CPU: %d\n" % cpu
raw += "Last event : %s\n" % rq.event.__repr__()
raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
raw += "Load = %d\n" % rq.load()
for t in rq.tasks:
raw += "%s \n" % thread_name(t)
def update_summary(self, txt):
if self.txt: if self.txt:
self.txt.Destroy() self.txt.Destroy()
self.txt = wx.StaticText(self.panel, -1, raw, (0, (self.screen_height / 2) + 50)) self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50))
def on_mouse_down(self, event): def on_mouse_down(self, event):
(x, y) = event.GetPositionTuple() (x, y) = event.GetPositionTuple()
cpu = self.cpu_from_ypixel(y) rect = self.rect_from_ypixel(y)
if cpu == -1: if rect == -1:
return return
t = self.px_to_us(x) + self.timeslices[0].start t = self.px_to_us(x) + self.ts_start
self.update_summary(cpu, t) self.sched_tracer.mouse_down(rect, t)
def update_width_virtual(self): def update_width_virtual(self):
@ -501,13 +457,64 @@ class TimeSliceList(UserList):
return found return found
def set_root_win(self, win):
self.root_win = win
def mouse_down(self, cpu, t):
idx = self.find_time_slice(t)
if idx == -1:
return
ts = self[idx]
rq = ts.rqs[cpu]
raw = "CPU: %d\n" % cpu
raw += "Last event : %s\n" % rq.event.__repr__()
raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
raw += "Load = %d\n" % rq.load()
for t in rq.tasks:
raw += "%s \n" % thread_name(t)
self.root_win.update_summary(raw)
def update_rectangle_cpu(self, slice, cpu):
rq = slice.rqs[cpu]
if slice.total_load != 0:
load_rate = rq.load() / float(slice.total_load)
else:
load_rate = 0
red_power = int(0xff - (0xff * load_rate))
color = (0xff, red_power, red_power)
top_color = None
if cpu in slice.event_cpus:
top_color = rq.event.color()
self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end)
def fill_zone(self, start, end):
i = self.find_time_slice(start)
if i == -1:
return
for i in xrange(i, len(self.data)):
timeslice = self.data[i]
if timeslice.start > end:
return
for cpu in timeslice.rqs:
self.update_rectangle_cpu(timeslice, cpu)
def interval(self): def interval(self):
if len(self.data) == 0: if len(self.data) == 0:
return (0, 0) return (0, 0)
return (self.data[0].start, self.data[-1].end) return (self.data[0].start, self.data[-1].end)
def max_cpu(self): def nr_rectangles(self):
last_ts = self.data[-1] last_ts = self.data[-1]
max_cpu = 0 max_cpu = 0
for cpu in last_ts.rqs: for cpu in last_ts.rqs:
@ -557,7 +564,7 @@ def trace_begin():
def trace_end(): def trace_end():
app = wx.App(False) app = wx.App(False)
timeslices = parser.timeslices timeslices = parser.timeslices
frame = RootFrame(timeslices) frame = RootFrame(timeslices, "Migration")
app.MainLoop() app.MainLoop()
def sched__sched_stat_runtime(event_name, context, common_cpu, def sched__sched_stat_runtime(event_name, context, common_cpu,