From c02514850d67be8db6b2b6658cbc23ac1fbf8bc7 Mon Sep 17 00:00:00 2001 From: Pierre Tardy Date: Mon, 31 May 2010 23:12:09 +0200 Subject: perf scripts python: Give field dict to unhandled callback trace_unhandled() callback does not allow to access event fields, this patch resolves the problem. It can also been used as a more pythonic and flexible way for script writters to demux event types This will for example greatly simplify pytimechart event demux. Acked-by: Frederic Weisbecker Acked-by: Tom Zanussi Cc: Ingo Molnar , Cc: Frederic Weisbecker Cc: Tom Zanussi LKML-Reference: <1275340329-2397-1-git-send-email-tardyp@gmail.com> Signed-off-by: Pierre Tardy Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/check-perf-trace.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py index 964d934395f..d9f7893e315 100644 --- a/tools/perf/scripts/python/check-perf-trace.py +++ b/tools/perf/scripts/python/check-perf-trace.py @@ -51,8 +51,7 @@ def kmem__kmalloc(event_name, context, common_cpu, flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)), -def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, - common_pid, common_comm): +def trace_unhandled(event_name, context, event_fields_dict): try: unhandled[event_name] += 1 except TypeError: -- cgit v1.2.3-70-g09d2 From 880d22f2470af6037715b7f6eb083b6ec5561d92 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 20 Jul 2010 21:55:33 +0200 Subject: perf: New migration tool overview This brings a GUI tool that displays an overview of the load of tasks proportion in each CPUs. The CPUs forward progress is cut in timeslices. A new timeslice is created for every runqueue event: a task gets pushed out or pulled in the runqueue. For each timeslice, every CPUs rectangle is colored with a red power that describes the local load against the total load. This more red is the rectangle, the higher is the given CPU load. This load is the number of tasks running on the CPU, without any distinction against the scheduler policy of the tasks, for now. Also for each timeslice, the event origin is depicted on the CPUs that triggered it using a thin colored line on top of the rectangle timeslice. These events are: * sleep: a task went to sleep and has then been pulled out the runqueue. The origin color in the thin line is dark blue. * wake up: a task woke up and has then been pushed in the runqueue. The origin color is yellow. * wake up new: a new task woke up and has then been pushed in the runqueue. The origin color is green. * migrate in: a task migrated in the runqueue due to a load balancing operation. The origin color is violet. * migrate out: reverse of the previous one. Migrate in events usually have paired migrate out events in another runqueue. The origin color is light blue. Clicking on a timeslice provides the runqueue event details and the runqueue state. The CPU rectangles can be navigated using the usual arrow controls. Horizontal zooming in/out is possible with the "+" and "-" buttons. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Li Zefan Cc: Steven Rostedt Cc: Tom Zanussi Cc: Mike Galbraith Cc: Venkatesh Pallipadi Cc: Pierre Tardy Cc: Nikhil Rao Cc: Li Zefan --- .../perf/scripts/python/bin/sched-migration-record | 2 + .../perf/scripts/python/bin/sched-migration-report | 3 + tools/perf/scripts/python/sched-migration.py | 634 +++++++++++++++++++++ 3 files changed, 639 insertions(+) create mode 100644 tools/perf/scripts/python/bin/sched-migration-record create mode 100644 tools/perf/scripts/python/bin/sched-migration-report create mode 100644 tools/perf/scripts/python/sched-migration.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/perf/scripts/python/bin/sched-migration-record new file mode 100644 index 00000000000..17a3e9bd9e8 --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report new file mode 100644 index 00000000000..61d05f72e44 --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-report @@ -0,0 +1,3 @@ +#!/bin/bash +# description: sched migration overview +perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py new file mode 100644 index 00000000000..f73e1c736a3 --- /dev/null +++ b/tools/perf/scripts/python/sched-migration.py @@ -0,0 +1,634 @@ +#!/usr/bin/python +# +# Cpu task migration overview toy +# +# Copyright (C) 2010 Frederic Weisbecker +# +# perf trace event handlers have been generated by perf trace -g python +# +# The whole is licensed under the terms of the GNU GPL License version 2 + + +try: + import wx +except ImportError: + raise ImportError, "You need to install the wxpython lib for this script" + +import os +import sys + +from collections import defaultdict +from UserList import UserList + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * + +class RootFrame(wx.Frame): + def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): + wx.Frame.__init__(self, parent, id, title) + + (self.screen_width, self.screen_height) = wx.GetDisplaySize() + self.screen_width -= 10 + self.screen_height -= 10 + self.zoom = 0.5 + self.scroll_scale = 20 + self.timeslices = timeslices + (self.ts_start, self.ts_end) = timeslices.interval() + self.update_width_virtual() + + # whole window panel + self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) + + # scrollable container + self.scroll = wx.ScrolledWindow(self.panel) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10) + self.scroll.EnableScrolling(True, True) + self.scroll.SetFocus() + + # scrollable drawing area + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width, self.screen_height / 2)) + self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + + self.scroll.Fit() + self.Fit() + + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, -1, wx.SIZE_USE_EXISTING) + + self.max_cpu = -1 + self.txt = None + + self.Show(True) + + def us_to_px(self, val): + return val / (10 ** 3) * self.zoom + + def px_to_us(self, val): + return (val / self.zoom) * (10 ** 3) + + def scroll_start(self): + (x, y) = self.scroll.GetViewStart() + return (x * self.scroll_scale, y * self.scroll_scale) + + def scroll_start_us(self): + (x, y) = self.scroll_start() + return self.px_to_us(x) + + def update_rectangle_cpu(self, dc, slice, cpu, offset_time): + rq = slice.rqs[cpu] + + if slice.total_load != 0: + load_rate = rq.load() / float(slice.total_load) + else: + load_rate = 0 + + + offset_px = self.us_to_px(slice.start - offset_time) + width_px = self.us_to_px(slice.end - slice.start) + (x, y) = self.scroll_start() + + if width_px == 0: + return + + offset_py = 100 + (cpu * 150) + width_py = 100 + + 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, 5) + width_py -= 5 + offset_py += 5 + + red_power = int(0xff - (0xff * load_rate)) + color = wx.Colour(0xff, red_power, red_power) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, width_py) + + def update_rectangles(self, dc, start, end): + if len(self.timeslices) == 0: + return + start += self.timeslices[0].start + 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) + if cpu > self.max_cpu: + self.max_cpu = cpu + + def on_paint(self, event): + color = wx.Colour(0xff, 0xff, 0xff) + brush = wx.Brush(color, wx.SOLID) + dc = wx.PaintDC(self.scroll_panel) + dc.SetBrush(brush) + + width = min(self.width_virtual, self.screen_width) + (x, y) = self.scroll_start() + start = self.px_to_us(x) + end = self.px_to_us(x + width) + self.update_rectangles(dc, start, end) + + def cpu_from_ypixel(self, y): + y -= 100 + cpu = y / 150 + height = y % 150 + + if cpu < 0 or cpu > self.max_cpu or height > 100: + return -1 + + return cpu + + 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) + + if self.txt: + self.txt.Destroy() + self.txt = wx.StaticText(self.panel, -1, raw, (0, (self.screen_height / 2) + 50)) + + + def on_mouse_down(self, event): + (x, y) = event.GetPositionTuple() + cpu = self.cpu_from_ypixel(y) + if cpu == -1: + return + + t = self.px_to_us(x) + self.timeslices[0].start + + self.update_summary(cpu, t) + + + def update_width_virtual(self): + self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) + + def __zoom(self, x): + self.update_width_virtual() + (xpos, ypos) = self.scroll.GetViewStart() + xpos = self.us_to_px(x) / self.scroll_scale + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10, xpos, ypos) + self.Refresh() + + def zoom_in(self): + x = self.scroll_start_us() + self.zoom *= 2 + self.__zoom(x) + + def zoom_out(self): + x = self.scroll_start_us() + self.zoom /= 2 + self.__zoom(x) + + + def on_key_press(self, event): + key = event.GetRawKeyCode() + if key == ord("+"): + self.zoom_in() + return + if key == ord("-"): + self.zoom_out() + return + + key = event.GetKeyCode() + (x, y) = self.scroll.GetViewStart() + if key == wx.WXK_RIGHT: + self.scroll.Scroll(x + 1, y) + elif key == wx.WXK_LEFT: + self.scroll.Scroll(x -1, y) + + +threads = { 0 : "idle"} + +def thread_name(pid): + return "%s:%d" % (threads[pid], pid) + +class EventHeaders: + def __init__(self, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + self.cpu = common_cpu + self.secs = common_secs + self.nsecs = common_nsecs + self.pid = common_pid + self.comm = common_comm + + def ts(self): + return (self.secs * (10 ** 9)) + self.nsecs + + def ts_format(self): + return "%d.%d" % (self.secs, int(self.nsecs / 1000)) + + +def taskState(state): + states = { + 0 : "R", + 1 : "S", + 2 : "D", + 64: "DEAD" + } + + if state not in states: + print "Unhandled task state %d" % state + return "" + + return states[state] + + +class RunqueueEventUnknown: + @staticmethod + def color(): + return None + + def __repr__(self): + return "unknown" + +class RunqueueEventSleep: + @staticmethod + def color(): + return (0, 0, 0xff) + + def __init__(self, sleeper): + self.sleeper = sleeper + + def __repr__(self): + return "%s gone to sleep" % thread_name(self.sleeper) + +class RunqueueEventWakeup: + @staticmethod + def color(): + return (0xff, 0xff, 0) + + def __init__(self, wakee): + self.wakee = wakee + + def __repr__(self): + return "%s woke up" % thread_name(self.wakee) + +class RunqueueEventFork: + @staticmethod + def color(): + return (0, 0xff, 0) + + def __init__(self, child): + self.child = child + + def __repr__(self): + return "new forked task %s" % thread_name(self.child) + +class RunqueueMigrateIn: + @staticmethod + def color(): + return (0, 0xf0, 0xff) + + def __init__(self, new): + self.new = new + + def __repr__(self): + return "task migrated in %s" % thread_name(self.new) + +class RunqueueMigrateOut: + @staticmethod + def color(): + return (0xff, 0, 0xff) + + def __init__(self, old): + self.old = old + + def __repr__(self): + return "task migrated out %s" % thread_name(self.old) + +class RunqueueSnapshot: + def __init__(self, tasks = [0], event = RunqueueEventUnknown()): + self.tasks = tuple(tasks) + self.event = event + + def sched_switch(self, prev, prev_state, next): + event = RunqueueEventUnknown() + + if taskState(prev_state) == "R" and next in self.tasks \ + and prev in self.tasks: + return self + + if taskState(prev_state) != "R": + event = RunqueueEventSleep(prev) + + next_tasks = list(self.tasks[:]) + if prev in self.tasks: + if taskState(prev_state) != "R": + next_tasks.remove(prev) + elif taskState(prev_state) == "R": + next_tasks.append(prev) + + if next not in next_tasks: + next_tasks.append(next) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_out(self, old): + if old not in self.tasks: + return self + next_tasks = [task for task in self.tasks if task != old] + + return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) + + def __migrate_in(self, new, event): + if new in self.tasks: + self.event = event + return self + next_tasks = self.tasks[:] + tuple([new]) + + return RunqueueSnapshot(next_tasks, event) + + def migrate_in(self, new): + return self.__migrate_in(new, RunqueueMigrateIn(new)) + + def wake_up(self, new): + return self.__migrate_in(new, RunqueueEventWakeup(new)) + + def wake_up_new(self, new): + return self.__migrate_in(new, RunqueueEventFork(new)) + + def load(self): + """ Provide the number of tasks on the runqueue. + Don't count idle""" + return len(self.tasks) - 1 + + def __repr__(self): + ret = self.tasks.__repr__() + ret += self.origin_tostring() + + return ret + +class TimeSlice: + def __init__(self, start, prev): + self.start = start + self.prev = prev + self.end = start + # cpus that triggered the event + self.event_cpus = [] + if prev is not None: + self.total_load = prev.total_load + self.rqs = prev.rqs.copy() + else: + self.rqs = defaultdict(RunqueueSnapshot) + self.total_load = 0 + + def __update_total_load(self, old_rq, new_rq): + diff = new_rq.load() - old_rq.load() + self.total_load += diff + + def sched_switch(self, ts_list, prev, prev_state, next, cpu): + old_rq = self.prev.rqs[cpu] + new_rq = old_rq.sched_switch(prev, prev_state, next) + + if old_rq is new_rq: + return + + self.rqs[cpu] = new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus = [cpu] + + def migrate(self, ts_list, new, old_cpu, new_cpu): + if old_cpu == new_cpu: + return + old_rq = self.prev.rqs[old_cpu] + out_rq = old_rq.migrate_out(new) + self.rqs[old_cpu] = out_rq + self.__update_total_load(old_rq, out_rq) + + new_rq = self.prev.rqs[new_cpu] + in_rq = new_rq.migrate_in(new) + self.rqs[new_cpu] = in_rq + self.__update_total_load(new_rq, in_rq) + + ts_list.append(self) + self.event_cpus = [old_cpu, new_cpu] + + def wake_up(self, ts_list, pid, cpu, fork): + old_rq = self.prev.rqs[cpu] + if fork: + new_rq = old_rq.wake_up_new(pid) + else: + new_rq = old_rq.wake_up(pid) + + if new_rq is old_rq: + return + self.rqs[cpu] = new_rq + self.__update_total_load(old_rq, new_rq) + ts_list.append(self) + self.event_cpus = [cpu] + + def next(self, t): + self.end = t + return TimeSlice(t, self) + +class TimeSliceList(UserList): + def __init__(self, arg = []): + self.data = arg + + def get_time_slice(self, ts): + if len(self.data) == 0: + slice = TimeSlice(ts, TimeSlice(-1, None)) + else: + slice = self.data[-1].next(ts) + return slice + + def find_time_slice(self, ts): + start = 0 + end = len(self.data) + found = -1 + searching = True + while searching: + if start == end or start == end - 1: + searching = False + + i = (end + start) / 2 + if self.data[i].start <= ts and self.data[i].end >= ts: + found = i + end = i + continue + + if self.data[i].end < ts: + start = i + + elif self.data[i].start > ts: + end = i + + return found + + def interval(self): + if len(self.data) == 0: + return (0, 0) + + return (self.data[0].start, self.data[-1].end) + + +class SchedEventProxy: + def __init__(self): + self.current_tsk = defaultdict(lambda : -1) + self.timeslices = TimeSliceList() + + def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio): + """ Ensure the task we sched out this cpu is really the one + we logged. Otherwise we may have missed traces """ + + on_cpu_task = self.current_tsk[headers.cpu] + + if on_cpu_task != -1 and on_cpu_task != prev_pid: + print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \ + (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid) + + threads[prev_pid] = prev_comm + threads[next_pid] = next_comm + self.current_tsk[headers.cpu] = next_pid + + ts = self.timeslices.get_time_slice(headers.ts()) + ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu) + + def migrate(self, headers, pid, prio, orig_cpu, dest_cpu): + ts = self.timeslices.get_time_slice(headers.ts()) + ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu) + + def wake_up(self, headers, comm, pid, success, target_cpu, fork): + if success == 0: + return + ts = self.timeslices.get_time_slice(headers.ts()) + ts.wake_up(self.timeslices, pid, target_cpu, fork) + + +def trace_begin(): + global parser + parser = SchedEventProxy() + +def trace_end(): + app = wx.App(False) + timeslices = parser.timeslices + frame = RootFrame(timeslices) + app.MainLoop() + +def sched__sched_stat_runtime(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, runtime, vruntime): + pass + +def sched__sched_stat_iowait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_stat_sleep(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_stat_wait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, delay): + pass + +def sched__sched_process_fork(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + parent_comm, parent_pid, child_comm, child_pid): + pass + +def sched__sched_process_wait(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_process_exit(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_process_free(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_migrate_task(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, orig_cpu, + dest_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) + +def sched__sched_switch(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio): + + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, + next_comm, next_pid, next_prio) + +def sched__sched_wakeup_new(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, success, + target_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.wake_up(headers, comm, pid, success, target_cpu, 1) + +def sched__sched_wakeup(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio, success, + target_cpu): + headers = EventHeaders(common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + parser.wake_up(headers, comm, pid, success, target_cpu, 0) + +def sched__sched_wait_task(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid, prio): + pass + +def sched__sched_kthread_stop_ret(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + ret): + pass + +def sched__sched_kthread_stop(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + comm, pid): + pass + +def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + pass -- cgit v1.2.3-70-g09d2 From 749e507411b17ad686783b6d1183befd846fb81b Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 21 Jul 2010 22:45:51 +0200 Subject: perf, sched migration: Handle ignored migrate out events Migrate out events may happen on tasks that are not in the runqueue, for example this is the case for tasks that are sleeping. In this case, we don't want to log the migrate out event in the source runqueue because the task is not eventually in the runqueue and we have already logged its sleep event. This fixes timeslices that spuriously propagate a sleep event from the previous timeslice. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index f73e1c736a3..7304d86c76c 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -435,7 +435,10 @@ class TimeSlice: self.__update_total_load(new_rq, in_rq) ts_list.append(self) - self.event_cpus = [old_cpu, new_cpu] + + if old_rq is not out_rq: + self.event_cpus.append(old_cpu) + self.event_cpus.append(new_cpu) def wake_up(self, ts_list, pid, cpu, fork): old_rq = self.prev.rqs[cpu] -- cgit v1.2.3-70-g09d2 From 207f90fc4757adc732d5ac23ad11bb90dd078754 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 21 Jul 2010 23:10:38 +0200 Subject: perf, sched migration: Ignore unhandled task states Stop printing an error message when we don't have the letter for a given task state. All we need to know is if the task is in the TASK_RUNNING state. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 7304d86c76c..e9898c5dbcc 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -260,8 +260,7 @@ def taskState(state): } if state not in states: - print "Unhandled task state %d" % state - return "" + return "Unknown" return states[state] -- cgit v1.2.3-70-g09d2 From be6d947691376218e788418e2656fc9a3e43b9bc Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Wed, 21 Jul 2010 19:46:11 -0700 Subject: perf, sched migration: Fix key bindings EVT_KEY_DOWN and EVT_LEFT_DOWN events are not bound to the RootFrame event handler. As a result, zoom/scroll via keyboard events do not work. This patch adds the missing bindings. Signed-off-by: Nikhil Rao Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Signed-off-by: Frederic Weisbecker --- tools/perf/scripts/python/sched-migration.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index e9898c5dbcc..8b8fb7c6099 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -54,6 +54,8 @@ class RootFrame(wx.Frame): self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) self.scroll.Fit() self.Fit() -- cgit v1.2.3-70-g09d2 From 0cddf56aa841713b37c10c5ab673d6164fce9833 Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Wed, 21 Jul 2010 19:46:27 -0700 Subject: perf, sched migration: Parameterize cpu height and spacing Without vertical zoom, it is not possible to see all CPUs in a trace taken on a larger machine. This patch parameterizes the height and spacing of CPUs so that you can fit more cpus into the screen. Ideally we should dynamically size/space the CPU rectangles with some minimum threshold. Until then, this patch is a stop-gap. Signed-off-by: Nikhil Rao Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Signed-off-by: Frederic Weisbecker --- tools/perf/scripts/python/sched-migration.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 8b8fb7c6099..d9026683027 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -27,6 +27,11 @@ from perf_trace_context import * from Core import * class RootFrame(wx.Frame): + Y_OFFSET = 100 + CPU_HEIGHT = 100 + CPU_SPACE = 50 + EVENT_MARKING_WIDTH = 5 + def __init__(self, timeslices, parent = None, id = -1, title = "Migration"): wx.Frame.__init__(self, parent, id, title) @@ -97,8 +102,8 @@ class RootFrame(wx.Frame): if width_px == 0: return - offset_py = 100 + (cpu * 150) - width_py = 100 + 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() @@ -107,9 +112,9 @@ class RootFrame(wx.Frame): color = wx.Colour(r, g, b) brush = wx.Brush(color, wx.SOLID) dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, 5) - width_py -= 5 - offset_py += 5 + 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) @@ -154,11 +159,11 @@ class RootFrame(wx.Frame): self.update_rectangles(dc, start, end) def cpu_from_ypixel(self, y): - y -= 100 - cpu = y / 150 - height = y % 150 + y -= RootFrame.Y_OFFSET + cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) + height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - if cpu < 0 or cpu > self.max_cpu or height > 100: + if cpu < 0 or cpu > self.max_cpu or height > RootFrame.CPU_HEIGHT: return -1 return cpu -- cgit v1.2.3-70-g09d2 From 70d815a3decc57c482e5384a623a859e3371e680 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 25 Jul 2010 23:11:51 +0200 Subject: perf, sched migration: Make it vertically scrollable With scheduler traces covering more than two cpus, rectangles of the CPUs 3 and more are not visibles. This makes the vertical navigation scrollable so that all of the CPUs rectangles are available. We also want to be able to zoom vertically, so that we can fit at best the screen with CPU rectangles, but that's for later. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index d9026683027..9d46377f793 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -43,18 +43,20 @@ class RootFrame(wx.Frame): self.timeslices = timeslices (self.ts_start, self.ts_end) = timeslices.interval() self.update_width_virtual() + self.nr_cpus = timeslices.max_cpu() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_cpus * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) # whole window panel self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) # scrollable container self.scroll = wx.ScrolledWindow(self.panel) - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) self.scroll.EnableScrolling(True, True) self.scroll.SetFocus() # scrollable drawing area - self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width, self.screen_height / 2)) + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) @@ -65,9 +67,8 @@ class RootFrame(wx.Frame): self.scroll.Fit() self.Fit() - self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, -1, wx.SIZE_USE_EXISTING) + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) - self.max_cpu = -1 self.txt = None self.Show(True) @@ -143,8 +144,6 @@ class RootFrame(wx.Frame): for cpu in timeslice.rqs: self.update_rectangle_cpu(dc, timeslice, cpu, self.timeslices[0].start) - if cpu > self.max_cpu: - self.max_cpu = cpu def on_paint(self, event): color = wx.Colour(0xff, 0xff, 0xff) @@ -163,7 +162,7 @@ class RootFrame(wx.Frame): cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - if cpu < 0 or cpu > self.max_cpu or height > RootFrame.CPU_HEIGHT: + if cpu < 0 or cpu > self.nr_cpus - 1 or height > RootFrame.CPU_HEIGHT: return -1 return cpu @@ -206,7 +205,7 @@ class RootFrame(wx.Frame): self.update_width_virtual() (xpos, ypos) = self.scroll.GetViewStart() xpos = self.us_to_px(x) / self.scroll_scale - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, 100 / 10, xpos, ypos) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) self.Refresh() def zoom_in(self): @@ -234,7 +233,11 @@ class RootFrame(wx.Frame): if key == wx.WXK_RIGHT: self.scroll.Scroll(x + 1, y) elif key == wx.WXK_LEFT: - self.scroll.Scroll(x -1, y) + self.scroll.Scroll(x - 1, y) + elif key == wx.WXK_DOWN: + self.scroll.Scroll(x, y + 1) + elif key == wx.WXK_UP: + self.scroll.Scroll(x, y - 1) threads = { 0 : "idle"} @@ -504,6 +507,14 @@ class TimeSliceList(UserList): return (self.data[0].start, self.data[-1].end) + def max_cpu(self): + last_ts = self.data[-1] + max_cpu = 0 + for cpu in last_ts.rqs: + if cpu > max_cpu: + max_cpu = cpu + return max_cpu + class SchedEventProxy: def __init__(self): -- cgit v1.2.3-70-g09d2 From 699b6d922c7d07f0c1c9041b489e884b5dd5fee5 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 26 Jul 2010 02:02:39 +0200 Subject: 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 Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- tools/perf/scripts/python/sched-migration.py | 177 ++++++++++++++------------- 1 file changed, 92 insertions(+), 85 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 9d46377f793..6d7281a7de3 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -28,11 +28,11 @@ from Core import * class RootFrame(wx.Frame): Y_OFFSET = 100 - CPU_HEIGHT = 100 - CPU_SPACE = 50 + RECT_HEIGHT = 100 + RECT_SPACE = 50 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) (self.screen_width, self.screen_height) = wx.GetDisplaySize() @@ -40,11 +40,12 @@ class RootFrame(wx.Frame): self.screen_height -= 10 self.zoom = 0.5 self.scroll_scale = 20 - self.timeslices = timeslices - (self.ts_start, self.ts_end) = timeslices.interval() + self.sched_tracer = sched_tracer + self.sched_tracer.set_root_win(self) + (self.ts_start, self.ts_end) = sched_tracer.interval() self.update_width_virtual() - self.nr_cpus = timeslices.max_cpu() + 1 - self.height_virtual = RootFrame.Y_OFFSET + (self.nr_cpus * (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE)) + self.nr_rects = sched_tracer.nr_rectangles() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) # whole window panel 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() return self.px_to_us(x) - def update_rectangle_cpu(self, dc, slice, cpu, offset_time): - rq = slice.rqs[cpu] - - if slice.total_load != 0: - load_rate = rq.load() / float(slice.total_load) - else: - load_rate = 0 + def paint_rectangle_zone(self, nr, color, top_color, start, end): + offset_px = self.us_to_px(start - self.ts_start) + width_px = self.us_to_px(end - self.ts_start) + offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + width_py = RootFrame.RECT_HEIGHT - offset_px = self.us_to_px(slice.start - offset_time) - width_px = self.us_to_px(slice.end - slice.start) - (x, y) = self.scroll_start() + dc = self.dc - if width_px == 0: - return + if top_color is not None: + (r, g, b) = top_color + 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 - 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) + (r ,g, b) = color + color = wx.Colour(r, g, b) brush = wx.Brush(color, wx.SOLID) dc.SetBrush(brush) dc.DrawRectangle(offset_px, offset_py, width_px, width_py) def update_rectangles(self, dc, start, end): - if len(self.timeslices) == 0: - return - start += self.timeslices[0].start - 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) + start += self.ts_start + end += self.ts_start + self.sched_tracer.fill_zone(start, end) def on_paint(self, event): - color = wx.Colour(0xff, 0xff, 0xff) - brush = wx.Brush(color, wx.SOLID) dc = wx.PaintDC(self.scroll_panel) - dc.SetBrush(brush) + self.dc = dc width = min(self.width_virtual, self.screen_width) (x, y) = self.scroll_start() @@ -157,45 +127,31 @@ class RootFrame(wx.Frame): end = self.px_to_us(x + width) self.update_rectangles(dc, start, end) - def cpu_from_ypixel(self, y): + def rect_from_ypixel(self, y): y -= RootFrame.Y_OFFSET - cpu = y / (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) - height = y % (RootFrame.CPU_HEIGHT + RootFrame.CPU_SPACE) + rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_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 cpu - - 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) + return rect + def update_summary(self, txt): if self.txt: 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): (x, y) = event.GetPositionTuple() - cpu = self.cpu_from_ypixel(y) - if cpu == -1: + rect = self.rect_from_ypixel(y) + if rect == -1: 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): @@ -501,13 +457,64 @@ class TimeSliceList(UserList): 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): if len(self.data) == 0: return (0, 0) return (self.data[0].start, self.data[-1].end) - def max_cpu(self): + def nr_rectangles(self): last_ts = self.data[-1] max_cpu = 0 for cpu in last_ts.rqs: @@ -557,7 +564,7 @@ def trace_begin(): def trace_end(): app = wx.App(False) timeslices = parser.timeslices - frame = RootFrame(timeslices) + frame = RootFrame(timeslices, "Migration") app.MainLoop() def sched__sched_stat_runtime(event_name, context, common_cpu, -- cgit v1.2.3-70-g09d2 From df92b40848616596c50b3b9e6d6ce8252af606ee Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 26 Jul 2010 02:29:44 +0200 Subject: perf, sched migration: Librarize the GUI class Export the GUI facility in the common library path. It is going to be useful for other scheduler views. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- .../Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | 184 +++++++++++++++++++++ tools/perf/scripts/python/sched-migration.py | 180 +------------------- 2 files changed, 189 insertions(+), 175 deletions(-) create mode 100644 tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py new file mode 100644 index 00000000000..ae9a56e43e0 --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py @@ -0,0 +1,184 @@ +# SchedGui.py - Python extension for perf trace, basic GUI code for +# traces drawing and overview. +# +# Copyright (C) 2010 by Frederic Weisbecker +# +# This software is distributed under the terms of the GNU General +# Public License ("GPL") version 2 as published by the Free Software +# Foundation. + + +try: + import wx +except ImportError: + raise ImportError, "You need to install the wxpython lib for this script" + + +class RootFrame(wx.Frame): + Y_OFFSET = 100 + RECT_HEIGHT = 100 + RECT_SPACE = 50 + EVENT_MARKING_WIDTH = 5 + + def __init__(self, sched_tracer, title, parent = None, id = -1): + wx.Frame.__init__(self, parent, id, title) + + (self.screen_width, self.screen_height) = wx.GetDisplaySize() + self.screen_width -= 10 + self.screen_height -= 10 + self.zoom = 0.5 + self.scroll_scale = 20 + self.sched_tracer = sched_tracer + self.sched_tracer.set_root_win(self) + (self.ts_start, self.ts_end) = sched_tracer.interval() + self.update_width_virtual() + self.nr_rects = sched_tracer.nr_rectangles() + 1 + self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + + # whole window panel + self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) + + # scrollable container + self.scroll = wx.ScrolledWindow(self.panel) + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) + self.scroll.EnableScrolling(True, True) + self.scroll.SetFocus() + + # scrollable drawing area + self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) + self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + self.scroll.Bind(wx.EVT_PAINT, self.on_paint) + self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) + self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) + + self.scroll.Fit() + self.Fit() + + self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) + + self.txt = None + + self.Show(True) + + def us_to_px(self, val): + return val / (10 ** 3) * self.zoom + + def px_to_us(self, val): + return (val / self.zoom) * (10 ** 3) + + def scroll_start(self): + (x, y) = self.scroll.GetViewStart() + return (x * self.scroll_scale, y * self.scroll_scale) + + def scroll_start_us(self): + (x, y) = self.scroll_start() + return self.px_to_us(x) + + def paint_rectangle_zone(self, nr, color, top_color, start, end): + offset_px = self.us_to_px(start - self.ts_start) + width_px = self.us_to_px(end - self.ts_start) + + offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) + width_py = RootFrame.RECT_HEIGHT + + dc = self.dc + + if top_color is not None: + (r, g, b) = top_color + 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 + + (r ,g, b) = color + color = wx.Colour(r, g, b) + brush = wx.Brush(color, wx.SOLID) + dc.SetBrush(brush) + dc.DrawRectangle(offset_px, offset_py, width_px, width_py) + + def update_rectangles(self, dc, start, end): + start += self.ts_start + end += self.ts_start + self.sched_tracer.fill_zone(start, end) + + def on_paint(self, event): + dc = wx.PaintDC(self.scroll_panel) + self.dc = dc + + width = min(self.width_virtual, self.screen_width) + (x, y) = self.scroll_start() + start = self.px_to_us(x) + end = self.px_to_us(x + width) + self.update_rectangles(dc, start, end) + + def rect_from_ypixel(self, y): + y -= RootFrame.Y_OFFSET + rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) + + if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: + return -1 + + return rect + + def update_summary(self, txt): + if self.txt: + self.txt.Destroy() + self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) + + + def on_mouse_down(self, event): + (x, y) = event.GetPositionTuple() + rect = self.rect_from_ypixel(y) + if rect == -1: + return + + t = self.px_to_us(x) + self.ts_start + + self.sched_tracer.mouse_down(rect, t) + + + def update_width_virtual(self): + self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) + + def __zoom(self, x): + self.update_width_virtual() + (xpos, ypos) = self.scroll.GetViewStart() + xpos = self.us_to_px(x) / self.scroll_scale + self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) + self.Refresh() + + def zoom_in(self): + x = self.scroll_start_us() + self.zoom *= 2 + self.__zoom(x) + + def zoom_out(self): + x = self.scroll_start_us() + self.zoom /= 2 + self.__zoom(x) + + + def on_key_press(self, event): + key = event.GetRawKeyCode() + if key == ord("+"): + self.zoom_in() + return + if key == ord("-"): + self.zoom_out() + return + + key = event.GetKeyCode() + (x, y) = self.scroll.GetViewStart() + if key == wx.WXK_RIGHT: + self.scroll.Scroll(x + 1, y) + elif key == wx.WXK_LEFT: + self.scroll.Scroll(x - 1, y) + elif key == wx.WXK_DOWN: + self.scroll.Scroll(x, y + 1) + elif key == wx.WXK_UP: + self.scroll.Scroll(x, y - 1) diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 6d7281a7de3..983463050f0 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -6,14 +6,11 @@ # # perf trace event handlers have been generated by perf trace -g python # -# The whole is licensed under the terms of the GNU GPL License version 2 +# This software is distributed under the terms of the GNU General +# Public License ("GPL") version 2 as published by the Free Software +# Foundation. -try: - import wx -except ImportError: - raise ImportError, "You need to install the wxpython lib for this script" - import os import sys @@ -22,178 +19,11 @@ from UserList import UserList sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') +sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') from perf_trace_context import * from Core import * - -class RootFrame(wx.Frame): - Y_OFFSET = 100 - RECT_HEIGHT = 100 - RECT_SPACE = 50 - EVENT_MARKING_WIDTH = 5 - - def __init__(self, sched_tracer, title, parent = None, id = -1): - wx.Frame.__init__(self, parent, id, title) - - (self.screen_width, self.screen_height) = wx.GetDisplaySize() - self.screen_width -= 10 - self.screen_height -= 10 - self.zoom = 0.5 - self.scroll_scale = 20 - self.sched_tracer = sched_tracer - self.sched_tracer.set_root_win(self) - (self.ts_start, self.ts_end) = sched_tracer.interval() - self.update_width_virtual() - self.nr_rects = sched_tracer.nr_rectangles() + 1 - self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) - - # whole window panel - self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) - - # scrollable container - self.scroll = wx.ScrolledWindow(self.panel) - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) - self.scroll.EnableScrolling(True, True) - self.scroll.SetFocus() - - # scrollable drawing area - self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) - self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - self.scroll.Bind(wx.EVT_PAINT, self.on_paint) - self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) - self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) - - self.scroll.Fit() - self.Fit() - - self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) - - self.txt = None - - self.Show(True) - - def us_to_px(self, val): - return val / (10 ** 3) * self.zoom - - def px_to_us(self, val): - return (val / self.zoom) * (10 ** 3) - - def scroll_start(self): - (x, y) = self.scroll.GetViewStart() - return (x * self.scroll_scale, y * self.scroll_scale) - - def scroll_start_us(self): - (x, y) = self.scroll_start() - return self.px_to_us(x) - - def paint_rectangle_zone(self, nr, color, top_color, start, end): - offset_px = self.us_to_px(start - self.ts_start) - width_px = self.us_to_px(end - self.ts_start) - - offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) - width_py = RootFrame.RECT_HEIGHT - - dc = self.dc - - if top_color is not None: - (r, g, b) = top_color - 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 - - (r ,g, b) = color - color = wx.Colour(r, g, b) - brush = wx.Brush(color, wx.SOLID) - dc.SetBrush(brush) - dc.DrawRectangle(offset_px, offset_py, width_px, width_py) - - def update_rectangles(self, dc, start, end): - start += self.ts_start - end += self.ts_start - self.sched_tracer.fill_zone(start, end) - - def on_paint(self, event): - dc = wx.PaintDC(self.scroll_panel) - self.dc = dc - - width = min(self.width_virtual, self.screen_width) - (x, y) = self.scroll_start() - start = self.px_to_us(x) - end = self.px_to_us(x + width) - self.update_rectangles(dc, start, end) - - def rect_from_ypixel(self, y): - y -= RootFrame.Y_OFFSET - rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) - - if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: - return -1 - - return rect - - def update_summary(self, txt): - if self.txt: - self.txt.Destroy() - self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) - - - def on_mouse_down(self, event): - (x, y) = event.GetPositionTuple() - rect = self.rect_from_ypixel(y) - if rect == -1: - return - - t = self.px_to_us(x) + self.ts_start - - self.sched_tracer.mouse_down(rect, t) - - - def update_width_virtual(self): - self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) - - def __zoom(self, x): - self.update_width_virtual() - (xpos, ypos) = self.scroll.GetViewStart() - xpos = self.us_to_px(x) / self.scroll_scale - self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) - self.Refresh() - - def zoom_in(self): - x = self.scroll_start_us() - self.zoom *= 2 - self.__zoom(x) - - def zoom_out(self): - x = self.scroll_start_us() - self.zoom /= 2 - self.__zoom(x) - - - def on_key_press(self, event): - key = event.GetRawKeyCode() - if key == ord("+"): - self.zoom_in() - return - if key == ord("-"): - self.zoom_out() - return - - key = event.GetKeyCode() - (x, y) = self.scroll.GetViewStart() - if key == wx.WXK_RIGHT: - self.scroll.Scroll(x + 1, y) - elif key == wx.WXK_LEFT: - self.scroll.Scroll(x - 1, y) - elif key == wx.WXK_DOWN: - self.scroll.Scroll(x, y + 1) - elif key == wx.WXK_UP: - self.scroll.Scroll(x, y - 1) +from SchedGui import * threads = { 0 : "idle"} -- cgit v1.2.3-70-g09d2 From 1b0ff06e68155de606f86e7e69eb238f14e05ba0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 1 Aug 2010 14:55:45 +0200 Subject: perf, sched migration: Librarize task states and event headers helpers Librarize the task state and event headers helpers as they can be generally useful. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Nikhil Rao Cc: Tom Zanussi --- .../python/Perf-Trace-Util/lib/Perf/Trace/Core.py | 30 ++++++++++++++++++++++ tools/perf/scripts/python/sched-migration.py | 30 ---------------------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py index 1dc464ee2ca..aad7525bca1 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py @@ -89,3 +89,33 @@ def trace_flag_str(value): value &= ~idx return string + + +def taskState(state): + states = { + 0 : "R", + 1 : "S", + 2 : "D", + 64: "DEAD" + } + + if state not in states: + return "Unknown" + + return states[state] + + +class EventHeaders: + def __init__(self, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + self.cpu = common_cpu + self.secs = common_secs + self.nsecs = common_nsecs + self.pid = common_pid + self.comm = common_comm + + def ts(self): + return (self.secs * (10 ** 9)) + self.nsecs + + def ts_format(self): + return "%d.%d" % (self.secs, int(self.nsecs / 1000)) diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 983463050f0..b934383c336 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -31,36 +31,6 @@ threads = { 0 : "idle"} def thread_name(pid): return "%s:%d" % (threads[pid], pid) -class EventHeaders: - def __init__(self, common_cpu, common_secs, common_nsecs, - common_pid, common_comm): - self.cpu = common_cpu - self.secs = common_secs - self.nsecs = common_nsecs - self.pid = common_pid - self.comm = common_comm - - def ts(self): - return (self.secs * (10 ** 9)) + self.nsecs - - def ts_format(self): - return "%d.%d" % (self.secs, int(self.nsecs / 1000)) - - -def taskState(state): - states = { - 0 : "R", - 1 : "S", - 2 : "D", - 64: "DEAD" - } - - if state not in states: - return "Unknown" - - return states[state] - - class RunqueueEventUnknown: @staticmethod def color(): -- cgit v1.2.3-70-g09d2 From 359d5106a2ff4ffa2ba129ec8f54743c341dabfc Mon Sep 17 00:00:00 2001 From: Koki Sanagi Date: Mon, 23 Aug 2010 18:47:09 +0900 Subject: perf: Add a script to show packets processing Add a perf script which shows packets processing and processed time. It helps us to investigate networking or network devices. If you want to use it, install perf and record perf.data like following. If you set script, perf gathers records until it ends. If not, you must Ctrl-C to stop recording. And if you want a report from record, If you use some options, you can limit the output. Option is below. tx: show only tx packets processing rx: show only rx packets processing dev=: show processing on this device debug: work with debug mode. It shows buffer status. For example, if you want to show received packets processing associated with eth4, 106133.171439sec cpu=0 irq_entry(+0.000msec irq=24:eth4) | softirq_entry(+0.006msec) | |---netif_receive_skb(+0.010msec skb=f2d15900 len=100) | | | skb_copy_datagram_iovec(+0.039msec 10291::10291) | napi_poll_exit(+0.022msec eth4) This perf script helps us to analyze the processing time of a transmit/receive sequence. Signed-off-by: Koki Sanagi Acked-by: David S. Miller Cc: Neil Horman Cc: Mathieu Desnoyers Cc: Kaneshige Kenji Cc: Izumo Taku Cc: Kosaki Motohiro Cc: Lai Jiangshan Cc: Scott Mcmillan Cc: Steven Rostedt Cc: Eric Dumazet Cc: Tom Zanussi LKML-Reference: <4C72439D.3040001@jp.fujitsu.com> Signed-off-by: Frederic Weisbecker --- tools/perf/scripts/python/bin/netdev-times-record | 8 + tools/perf/scripts/python/bin/netdev-times-report | 5 + tools/perf/scripts/python/netdev-times.py | 464 ++++++++++++++++++++++ 3 files changed, 477 insertions(+) create mode 100644 tools/perf/scripts/python/bin/netdev-times-record create mode 100644 tools/perf/scripts/python/bin/netdev-times-report create mode 100644 tools/perf/scripts/python/netdev-times.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/bin/netdev-times-record b/tools/perf/scripts/python/bin/netdev-times-record new file mode 100644 index 00000000000..d931a828126 --- /dev/null +++ b/tools/perf/scripts/python/bin/netdev-times-record @@ -0,0 +1,8 @@ +#!/bin/bash +perf record -a -e net:net_dev_xmit -e net:net_dev_queue \ + -e net:netif_receive_skb -e net:netif_rx \ + -e skb:consume_skb -e skb:kfree_skb \ + -e skb:skb_copy_datagram_iovec -e napi:napi_poll \ + -e irq:irq_handler_entry -e irq:irq_handler_exit \ + -e irq:softirq_entry -e irq:softirq_exit \ + -e irq:softirq_raise $@ diff --git a/tools/perf/scripts/python/bin/netdev-times-report b/tools/perf/scripts/python/bin/netdev-times-report new file mode 100644 index 00000000000..c3d0a638123 --- /dev/null +++ b/tools/perf/scripts/python/bin/netdev-times-report @@ -0,0 +1,5 @@ +#!/bin/bash +# description: display a process of packet and processing time +# args: [tx] [rx] [dev=] [debug] + +perf trace -s ~/libexec/perf-core/scripts/python/netdev-times.py $@ diff --git a/tools/perf/scripts/python/netdev-times.py b/tools/perf/scripts/python/netdev-times.py new file mode 100644 index 00000000000..9aa0a32972e --- /dev/null +++ b/tools/perf/scripts/python/netdev-times.py @@ -0,0 +1,464 @@ +# Display a process of packets and processed time. +# It helps us to investigate networking or network device. +# +# options +# tx: show only tx chart +# rx: show only rx chart +# dev=: show only thing related to specified device +# debug: work with debug mode. It shows buffer status. + +import os +import sys + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * +from Util import * + +all_event_list = []; # insert all tracepoint event related with this script +irq_dic = {}; # key is cpu and value is a list which stacks irqs + # which raise NET_RX softirq +net_rx_dic = {}; # key is cpu and value include time of NET_RX softirq-entry + # and a list which stacks receive +receive_hunk_list = []; # a list which include a sequence of receive events +rx_skb_list = []; # received packet list for matching + # skb_copy_datagram_iovec + +buffer_budget = 65536; # the budget of rx_skb_list, tx_queue_list and + # tx_xmit_list +of_count_rx_skb_list = 0; # overflow count + +tx_queue_list = []; # list of packets which pass through dev_queue_xmit +of_count_tx_queue_list = 0; # overflow count + +tx_xmit_list = []; # list of packets which pass through dev_hard_start_xmit +of_count_tx_xmit_list = 0; # overflow count + +tx_free_list = []; # list of packets which is freed + +# options +show_tx = 0; +show_rx = 0; +dev = 0; # store a name of device specified by option "dev=" +debug = 0; + +# indices of event_info tuple +EINFO_IDX_NAME= 0 +EINFO_IDX_CONTEXT=1 +EINFO_IDX_CPU= 2 +EINFO_IDX_TIME= 3 +EINFO_IDX_PID= 4 +EINFO_IDX_COMM= 5 + +# Calculate a time interval(msec) from src(nsec) to dst(nsec) +def diff_msec(src, dst): + return (dst - src) / 1000000.0 + +# Display a process of transmitting a packet +def print_transmit(hunk): + if dev != 0 and hunk['dev'].find(dev) < 0: + return + print "%7s %5d %6d.%06dsec %12.3fmsec %12.3fmsec" % \ + (hunk['dev'], hunk['len'], + nsecs_secs(hunk['queue_t']), + nsecs_nsecs(hunk['queue_t'])/1000, + diff_msec(hunk['queue_t'], hunk['xmit_t']), + diff_msec(hunk['xmit_t'], hunk['free_t'])) + +# Format for displaying rx packet processing +PF_IRQ_ENTRY= " irq_entry(+%.3fmsec irq=%d:%s)" +PF_SOFT_ENTRY=" softirq_entry(+%.3fmsec)" +PF_NAPI_POLL= " napi_poll_exit(+%.3fmsec %s)" +PF_JOINT= " |" +PF_WJOINT= " | |" +PF_NET_RECV= " |---netif_receive_skb(+%.3fmsec skb=%x len=%d)" +PF_NET_RX= " |---netif_rx(+%.3fmsec skb=%x)" +PF_CPY_DGRAM= " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)" +PF_KFREE_SKB= " | kfree_skb(+%.3fmsec location=%x)" +PF_CONS_SKB= " | consume_skb(+%.3fmsec)" + +# Display a process of received packets and interrputs associated with +# a NET_RX softirq +def print_receive(hunk): + show_hunk = 0 + irq_list = hunk['irq_list'] + cpu = irq_list[0]['cpu'] + base_t = irq_list[0]['irq_ent_t'] + # check if this hunk should be showed + if dev != 0: + for i in range(len(irq_list)): + if irq_list[i]['name'].find(dev) >= 0: + show_hunk = 1 + break + else: + show_hunk = 1 + if show_hunk == 0: + return + + print "%d.%06dsec cpu=%d" % \ + (nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu) + for i in range(len(irq_list)): + print PF_IRQ_ENTRY % \ + (diff_msec(base_t, irq_list[i]['irq_ent_t']), + irq_list[i]['irq'], irq_list[i]['name']) + print PF_JOINT + irq_event_list = irq_list[i]['event_list'] + for j in range(len(irq_event_list)): + irq_event = irq_event_list[j] + if irq_event['event'] == 'netif_rx': + print PF_NET_RX % \ + (diff_msec(base_t, irq_event['time']), + irq_event['skbaddr']) + print PF_JOINT + print PF_SOFT_ENTRY % \ + diff_msec(base_t, hunk['sirq_ent_t']) + print PF_JOINT + event_list = hunk['event_list'] + for i in range(len(event_list)): + event = event_list[i] + if event['event_name'] == 'napi_poll': + print PF_NAPI_POLL % \ + (diff_msec(base_t, event['event_t']), event['dev']) + if i == len(event_list) - 1: + print "" + else: + print PF_JOINT + else: + print PF_NET_RECV % \ + (diff_msec(base_t, event['event_t']), event['skbaddr'], + event['len']) + if 'comm' in event.keys(): + print PF_WJOINT + print PF_CPY_DGRAM % \ + (diff_msec(base_t, event['comm_t']), + event['pid'], event['comm']) + elif 'handle' in event.keys(): + print PF_WJOINT + if event['handle'] == "kfree_skb": + print PF_KFREE_SKB % \ + (diff_msec(base_t, + event['comm_t']), + event['location']) + elif event['handle'] == "consume_skb": + print PF_CONS_SKB % \ + diff_msec(base_t, + event['comm_t']) + print PF_JOINT + +def trace_begin(): + global show_tx + global show_rx + global dev + global debug + + for i in range(len(sys.argv)): + if i == 0: + continue + arg = sys.argv[i] + if arg == 'tx': + show_tx = 1 + elif arg =='rx': + show_rx = 1 + elif arg.find('dev=',0, 4) >= 0: + dev = arg[4:] + elif arg == 'debug': + debug = 1 + if show_tx == 0 and show_rx == 0: + show_tx = 1 + show_rx = 1 + +def trace_end(): + # order all events in time + all_event_list.sort(lambda a,b :cmp(a[EINFO_IDX_TIME], + b[EINFO_IDX_TIME])) + # process all events + for i in range(len(all_event_list)): + event_info = all_event_list[i] + name = event_info[EINFO_IDX_NAME] + if name == 'irq__softirq_exit': + handle_irq_softirq_exit(event_info) + elif name == 'irq__softirq_entry': + handle_irq_softirq_entry(event_info) + elif name == 'irq__softirq_raise': + handle_irq_softirq_raise(event_info) + elif name == 'irq__irq_handler_entry': + handle_irq_handler_entry(event_info) + elif name == 'irq__irq_handler_exit': + handle_irq_handler_exit(event_info) + elif name == 'napi__napi_poll': + handle_napi_poll(event_info) + elif name == 'net__netif_receive_skb': + handle_netif_receive_skb(event_info) + elif name == 'net__netif_rx': + handle_netif_rx(event_info) + elif name == 'skb__skb_copy_datagram_iovec': + handle_skb_copy_datagram_iovec(event_info) + elif name == 'net__net_dev_queue': + handle_net_dev_queue(event_info) + elif name == 'net__net_dev_xmit': + handle_net_dev_xmit(event_info) + elif name == 'skb__kfree_skb': + handle_kfree_skb(event_info) + elif name == 'skb__consume_skb': + handle_consume_skb(event_info) + # display receive hunks + if show_rx: + for i in range(len(receive_hunk_list)): + print_receive(receive_hunk_list[i]) + # display transmit hunks + if show_tx: + print " dev len Qdisc " \ + " netdevice free" + for i in range(len(tx_free_list)): + print_transmit(tx_free_list[i]) + if debug: + print "debug buffer status" + print "----------------------------" + print "xmit Qdisc:remain:%d overflow:%d" % \ + (len(tx_queue_list), of_count_tx_queue_list) + print "xmit netdevice:remain:%d overflow:%d" % \ + (len(tx_xmit_list), of_count_tx_xmit_list) + print "receive:remain:%d overflow:%d" % \ + (len(rx_skb_list), of_count_rx_skb_list) + +# called from perf, when it finds a correspoinding event +def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec): + if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": + return + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) + all_event_list.append(event_info) + +def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec): + if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": + return + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) + all_event_list.append(event_info) + +def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec): + if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": + return + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) + all_event_list.append(event_info) + +def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm, + irq, irq_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + irq, irq_name) + all_event_list.append(event_info) + +def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret) + all_event_list.append(event_info) + +def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + napi, dev_name) + all_event_list.append(event_info) + +def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr, + skblen, dev_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, skblen, dev_name) + all_event_list.append(event_info) + +def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr, + skblen, dev_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, skblen, dev_name) + all_event_list.append(event_info) + +def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm, + skbaddr, skblen, dev_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, skblen, dev_name) + all_event_list.append(event_info) + +def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm, + skbaddr, skblen, rc, dev_name): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, skblen, rc ,dev_name) + all_event_list.append(event_info) + +def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, + skbaddr, protocol, location): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, protocol, location) + all_event_list.append(event_info) + +def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr) + all_event_list.append(event_info) + +def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm, + skbaddr, skblen): + event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, + skbaddr, skblen) + all_event_list.append(event_info) + +def handle_irq_handler_entry(event_info): + (name, context, cpu, time, pid, comm, irq, irq_name) = event_info + if cpu not in irq_dic.keys(): + irq_dic[cpu] = [] + irq_record = {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time} + irq_dic[cpu].append(irq_record) + +def handle_irq_handler_exit(event_info): + (name, context, cpu, time, pid, comm, irq, ret) = event_info + if cpu not in irq_dic.keys(): + return + irq_record = irq_dic[cpu].pop() + if irq != irq_record['irq']: + return + irq_record.update({'irq_ext_t':time}) + # if an irq doesn't include NET_RX softirq, drop. + if 'event_list' in irq_record.keys(): + irq_dic[cpu].append(irq_record) + +def handle_irq_softirq_raise(event_info): + (name, context, cpu, time, pid, comm, vec) = event_info + if cpu not in irq_dic.keys() \ + or len(irq_dic[cpu]) == 0: + return + irq_record = irq_dic[cpu].pop() + if 'event_list' in irq_record.keys(): + irq_event_list = irq_record['event_list'] + else: + irq_event_list = [] + irq_event_list.append({'time':time, 'event':'sirq_raise'}) + irq_record.update({'event_list':irq_event_list}) + irq_dic[cpu].append(irq_record) + +def handle_irq_softirq_entry(event_info): + (name, context, cpu, time, pid, comm, vec) = event_info + net_rx_dic[cpu] = {'sirq_ent_t':time, 'event_list':[]} + +def handle_irq_softirq_exit(event_info): + (name, context, cpu, time, pid, comm, vec) = event_info + irq_list = [] + event_list = 0 + if cpu in irq_dic.keys(): + irq_list = irq_dic[cpu] + del irq_dic[cpu] + if cpu in net_rx_dic.keys(): + sirq_ent_t = net_rx_dic[cpu]['sirq_ent_t'] + event_list = net_rx_dic[cpu]['event_list'] + del net_rx_dic[cpu] + if irq_list == [] or event_list == 0: + return + rec_data = {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time, + 'irq_list':irq_list, 'event_list':event_list} + # merge information realted to a NET_RX softirq + receive_hunk_list.append(rec_data) + +def handle_napi_poll(event_info): + (name, context, cpu, time, pid, comm, napi, dev_name) = event_info + if cpu in net_rx_dic.keys(): + event_list = net_rx_dic[cpu]['event_list'] + rec_data = {'event_name':'napi_poll', + 'dev':dev_name, 'event_t':time} + event_list.append(rec_data) + +def handle_netif_rx(event_info): + (name, context, cpu, time, pid, comm, + skbaddr, skblen, dev_name) = event_info + if cpu not in irq_dic.keys() \ + or len(irq_dic[cpu]) == 0: + return + irq_record = irq_dic[cpu].pop() + if 'event_list' in irq_record.keys(): + irq_event_list = irq_record['event_list'] + else: + irq_event_list = [] + irq_event_list.append({'time':time, 'event':'netif_rx', + 'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name}) + irq_record.update({'event_list':irq_event_list}) + irq_dic[cpu].append(irq_record) + +def handle_netif_receive_skb(event_info): + global of_count_rx_skb_list + + (name, context, cpu, time, pid, comm, + skbaddr, skblen, dev_name) = event_info + if cpu in net_rx_dic.keys(): + rec_data = {'event_name':'netif_receive_skb', + 'event_t':time, 'skbaddr':skbaddr, 'len':skblen} + event_list = net_rx_dic[cpu]['event_list'] + event_list.append(rec_data) + rx_skb_list.insert(0, rec_data) + if len(rx_skb_list) > buffer_budget: + rx_skb_list.pop() + of_count_rx_skb_list += 1 + +def handle_net_dev_queue(event_info): + global of_count_tx_queue_list + + (name, context, cpu, time, pid, comm, + skbaddr, skblen, dev_name) = event_info + skb = {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time} + tx_queue_list.insert(0, skb) + if len(tx_queue_list) > buffer_budget: + tx_queue_list.pop() + of_count_tx_queue_list += 1 + +def handle_net_dev_xmit(event_info): + global of_count_tx_xmit_list + + (name, context, cpu, time, pid, comm, + skbaddr, skblen, rc, dev_name) = event_info + if rc == 0: # NETDEV_TX_OK + for i in range(len(tx_queue_list)): + skb = tx_queue_list[i] + if skb['skbaddr'] == skbaddr: + skb['xmit_t'] = time + tx_xmit_list.insert(0, skb) + del tx_queue_list[i] + if len(tx_xmit_list) > buffer_budget: + tx_xmit_list.pop() + of_count_tx_xmit_list += 1 + return + +def handle_kfree_skb(event_info): + (name, context, cpu, time, pid, comm, + skbaddr, protocol, location) = event_info + for i in range(len(tx_queue_list)): + skb = tx_queue_list[i] + if skb['skbaddr'] == skbaddr: + del tx_queue_list[i] + return + for i in range(len(tx_xmit_list)): + skb = tx_xmit_list[i] + if skb['skbaddr'] == skbaddr: + skb['free_t'] = time + tx_free_list.append(skb) + del tx_xmit_list[i] + return + for i in range(len(rx_skb_list)): + rec_data = rx_skb_list[i] + if rec_data['skbaddr'] == skbaddr: + rec_data.update({'handle':"kfree_skb", + 'comm':comm, 'pid':pid, 'comm_t':time}) + del rx_skb_list[i] + return + +def handle_consume_skb(event_info): + (name, context, cpu, time, pid, comm, skbaddr) = event_info + for i in range(len(tx_xmit_list)): + skb = tx_xmit_list[i] + if skb['skbaddr'] == skbaddr: + skb['free_t'] = time + tx_free_list.append(skb) + del tx_xmit_list[i] + return + +def handle_skb_copy_datagram_iovec(event_info): + (name, context, cpu, time, pid, comm, skbaddr, skblen) = event_info + for i in range(len(rx_skb_list)): + rec_data = rx_skb_list[i] + if skbaddr == rec_data['skbaddr']: + rec_data.update({'handle':"skb_copy_datagram_iovec", + 'comm':comm, 'pid':pid, 'comm_t':time}) + del rx_skb_list[i] + return -- cgit v1.2.3-70-g09d2 From 44e668c6faa9a6c477a32788e7e88f0754c54a4e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 10 Oct 2010 16:10:03 +0100 Subject: perf trace: Use $PERF_EXEC_PATH in canned report scripts Set $PERF_EXEC_PATH before starting the record and report scripts, and make them use it where necessary. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <1286723403.2955.205.camel@localhost> Signed-off-by: Ben Hutchings Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 +++--- tools/perf/scripts/perl/bin/failed-syscalls-report | 2 +- tools/perf/scripts/perl/bin/rw-by-file-report | 2 +- tools/perf/scripts/perl/bin/rw-by-pid-report | 2 +- tools/perf/scripts/perl/bin/rwtop-report | 2 +- tools/perf/scripts/perl/bin/wakeup-latency-report | 2 +- tools/perf/scripts/perl/bin/workqueue-stats-report | 2 +- tools/perf/scripts/python/bin/failed-syscalls-by-pid-report | 2 +- tools/perf/scripts/python/bin/netdev-times-report | 2 +- tools/perf/scripts/python/bin/sched-migration-report | 2 +- tools/perf/scripts/python/bin/sctop-report | 2 +- tools/perf/scripts/python/bin/syscall-counts-by-pid-report | 2 +- tools/perf/scripts/python/bin/syscall-counts-report | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 40a6a2992d1..3ea62199ffe 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -46,9 +46,6 @@ static struct scripting_ops *scripting_ops; static void setup_scripting(void) { - /* make sure PERF_EXEC_PATH is set for scripts */ - perf_set_argv_exec_path(perf_exec_path()); - setup_perl_scripting(); setup_python_scripting(); @@ -593,6 +590,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) suffix = REPORT_SUFFIX; } + /* make sure PERF_EXEC_PATH is set for scripts */ + perf_set_argv_exec_path(perf_exec_path()); + if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) { char *record_script_path, *report_script_path; int live_pipe[2]; diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report index e3a5e55d54f..4028d92dc4a 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-report +++ b/tools/perf/scripts/perl/bin/failed-syscalls-report @@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then shift fi fi -perf trace $@ -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $comm +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report index d83070b7eeb..ba25f4d41fb 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-report +++ b/tools/perf/scripts/perl/bin/rw-by-file-report @@ -7,7 +7,7 @@ if [ $# -lt 1 ] ; then fi comm=$1 shift -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $comm +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report index 7ef46983f62..641a3f5d085 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-report +++ b/tools/perf/scripts/perl/bin/rw-by-pid-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide r/w activity -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report index 93e698cd3f3..4918dba7702 100644 --- a/tools/perf/scripts/perl/bin/rwtop-report +++ b/tools/perf/scripts/perl/bin/rwtop-report @@ -17,7 +17,7 @@ if [ "$n_args" -gt 0 ] ; then interval=$1 shift fi -perf trace $@ -s ~/libexec/perf-core/scripts/perl/rwtop.pl $interval +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report index a0d898f9ca1..49052ebcb63 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-report +++ b/tools/perf/scripts/perl/bin/wakeup-latency-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide min/max/avg wakeup latency -perf trace $@ -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report index 35081132ef9..df0c65f4ca9 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ b/tools/perf/scripts/perl/bin/workqueue-stats-report @@ -1,6 +1,6 @@ #!/bin/bash # description: workqueue stats (ins/exe/create/destroy) -perf trace $@ -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report index 30293545fcc..03587021463 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report @@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then shift fi fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $comm +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.py $comm diff --git a/tools/perf/scripts/python/bin/netdev-times-report b/tools/perf/scripts/python/bin/netdev-times-report index c3d0a638123..4ad361b3124 100644 --- a/tools/perf/scripts/python/bin/netdev-times-report +++ b/tools/perf/scripts/python/bin/netdev-times-report @@ -2,4 +2,4 @@ # description: display a process of packet and processing time # args: [tx] [rx] [dev=] [debug] -perf trace -s ~/libexec/perf-core/scripts/python/netdev-times.py $@ +perf trace -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@ diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report index 61d05f72e44..df1791f07c2 100644 --- a/tools/perf/scripts/python/bin/sched-migration-report +++ b/tools/perf/scripts/python/bin/sched-migration-report @@ -1,3 +1,3 @@ #!/bin/bash # description: sched migration overview -perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/scripts/python/bin/sctop-report index b01c842ae7b..36b409c05e5 100644 --- a/tools/perf/scripts/python/bin/sctop-report +++ b/tools/perf/scripts/python/bin/sctop-report @@ -21,4 +21,4 @@ elif [ "$n_args" -gt 0 ] ; then interval=$1 shift fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/sctop.py $comm $interval +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report index 9e9d8ddd72c..4eb88c9fc83 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report @@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then shift fi fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $comm +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.py $comm diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report index dc076b61879..cb2f9c5cf17 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-report +++ b/tools/perf/scripts/python/bin/syscall-counts-report @@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then shift fi fi -perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts.py $comm +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm -- cgit v1.2.3-70-g09d2 From 6cc7361440e499abb3a30cdbcfedad03e43c92ae Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 Oct 2010 15:15:10 -0200 Subject: perf python scripting: Improve the failed-syscalls-by-pid script . Print message at script start telling how to get te summary . Print the syscall name using the audit-lib-python package, if installed . Print the errno string . Accept both pid (if numeric) or COMM name Now it looks like this: [root@emilia ~]# perf trace failed-syscalls-by-pid Press control+C to stop and show the summary ^C syscall errors: comm [pid] count ------------------------------ ---------- automount [1670] syscall: futex err = ETIMEDOUT 39 irqbalance [1462] syscall: openat err = ENOENT 4 perf [7888] syscall: lseek err = ESPIPE 1 syscall: open err = ENOENT 24 perf [7889] syscall: ioctl err = EINVAL 1 syscall: readlink err = EINVAL 2 syscall: open err = ENOENT 389 syscall: stat err = ENOENT 141 syscall: lseek err = ESPIPE 3 [root@emilia ~]# [root@emilia ~]# perf trace failed-syscalls-by-pid 1670 Press control+C to stop and show the summary ^C syscall errors: comm [pid] count ------------------------------ ---------- automount [1670] syscall: futex err = ETIMEDOUT 2 [root@emilia ~]# [root@emilia ~]# [root@emilia ~]# [root@emilia ~]# perf trace failed-syscalls-by-pid automount Press control+C to stop and show the summary ^C syscall errors for automount: comm [pid] count ------------------------------ ---------- automount [1669] syscall: futex err = ETIMEDOUT 1 automount [1670] syscall: futex err = ETIMEDOUT 5 [root@emilia ~]# Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- .../python/Perf-Trace-Util/lib/Perf/Trace/Util.py | 37 ++++++++++++++++++++++ .../perf/scripts/python/failed-syscalls-by-pid.py | 21 +++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 9689bc0acd9..9d15f484308 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -6,6 +6,8 @@ # Public License ("GPL") version 2 as published by the Free Software # Foundation. +import errno, os + NSECS_PER_SEC = 1000000000 def avg(total, n): @@ -26,3 +28,38 @@ def nsecs_str(nsecs): def clear_term(): print("\x1b[H\x1b[2J") + +audit_package_warned = False + +try: + import audit + machine_to_id = { + 'x86_64': audit.MACH_86_64, + 'alpha' : audit.MACH_ALPHA, + 'armeb' : audit.MACH_ARMEB, + 'ia64' : audit.MACH_IA64, + 'ppc' : audit.MACH_PPC, + 'ppc64' : audit.MACH_PPC64, + 's390' : audit.MACH_S390, + 's390x' : audit.MACH_S390X, + 'i386' : audit.MACH_X86, + 'i586' : audit.MACH_X86, + 'i686' : audit.MACH_X86, + } + machine_id = machine_to_id[os.uname()[4]] +except: + if not audit_package_warned: + audit_package_warned = True + print "Install the audit-libs-python package to get syscall names" + +def syscall_name(id): + try: + return audit.audit_syscall_to_name(id, machine_id) + except: + return str(id) + +def strerror(nr): + try: + return errno.errorcode[abs(nr)] + except: + return "Unknown %d errno" % nr diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py index 0ca02278fe6..acd7848717b 100644 --- a/tools/perf/scripts/python/failed-syscalls-by-pid.py +++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py @@ -13,21 +13,26 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \ from perf_trace_context import * from Core import * +from Util import * -usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; +usage = "perf trace -s syscall-counts-by-pid.py [comm|pid]\n"; for_comm = None +for_pid = None if len(sys.argv) > 2: sys.exit(usage) if len(sys.argv) > 1: - for_comm = sys.argv[1] + try: + for_pid = int(sys.argv[1]) + except: + for_comm = sys.argv[1] syscalls = autodict() def trace_begin(): - pass + print "Press control+C to stop and show the summary" def trace_end(): print_error_totals() @@ -35,9 +40,9 @@ def trace_end(): def raw_syscalls__sys_exit(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, id, ret): - if for_comm is not None: - if common_comm != for_comm: - return + if (for_comm and common_comm != for_comm) or \ + (for_pid and common_pid != for_pid ): + return if ret < 0: try: @@ -62,7 +67,7 @@ def print_error_totals(): print "\n%s [%d]\n" % (comm, pid), id_keys = syscalls[comm][pid].keys() for id in id_keys: - print " syscall: %-16d\n" % (id), + print " syscall: %-16s\n" % syscall_name(id), ret_keys = syscalls[comm][pid][id].keys() for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True): - print " err = %-20d %10d\n" % (ret, val), + print " err = %-20s %10d\n" % (strerror(ret), val), -- cgit v1.2.3-70-g09d2 From 6545aaa561b5678c497e94dea22cb2d1af1d6859 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 Oct 2010 17:11:25 -0200 Subject: perf python scripting: Improve the syscalls-counts script . Print message at script start telling how to get te summary . Print the syscall name Now it looks like this: [root@emilia ~]# perf trace syscall-counts Press control+C to stop and show the summary ^C syscall events: event count ---------------------------------------- ----------- read 102752 open 1293 close 878 write 319 stat 185 fstat 149 getdents 116 mmap 98 brk 80 rt_sigaction 66 munmap 42 mprotect 24 lseek 21 lstat 7 rt_sigprocmask 4 futex 3 statfs 3 ioctl 3 readlink 2 select 2 getegid 1 geteuid 1 getgid 1 getuid 1 getrlimit 1 fcntl 1 uname 1 [root@emilia ~]# Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/syscall-counts.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scripts/python/syscall-counts.py index f977e85ff04..ea183dc82d2 100644 --- a/tools/perf/scripts/python/syscall-counts.py +++ b/tools/perf/scripts/python/syscall-counts.py @@ -13,6 +13,7 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \ from perf_trace_context import * from Core import * +from Util import syscall_name usage = "perf trace -s syscall-counts.py [comm]\n"; @@ -27,7 +28,7 @@ if len(sys.argv) > 1: syscalls = autodict() def trace_begin(): - pass + print "Press control+C to stop and show the summary" def trace_end(): print_syscall_totals() @@ -55,4 +56,4 @@ def print_syscall_totals(): for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ reverse = True): - print "%-40d %10d\n" % (id, val), + print "%-40s %10d\n" % (syscall_name(id), val), -- cgit v1.2.3-70-g09d2 From 2e7d1e3fb8043380a2fc5d759eb357bf05acf935 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 Oct 2010 18:39:20 -0200 Subject: perf python scripting: print the syscall name on sctop [root@emilia tmp]# perf trace sctop 1 syscall events: event count ---------------------------------------- ---------- read 215400 futex 4029 write 376 brk 33 rt_sigprocmask 24 select 17 lseek 2 fsync 1 ^C[root@emilia tmp]# Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/sctop.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py index 6cafad40c29..547cbe99de6 100644 --- a/tools/perf/scripts/python/sctop.py +++ b/tools/perf/scripts/python/sctop.py @@ -8,10 +8,7 @@ # will be refreshed every [interval] seconds. The default interval is # 3 seconds. -import thread -import time -import os -import sys +import os, sys, thread, time sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') @@ -71,7 +68,7 @@ def print_syscall_totals(interval): for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ reverse = True): try: - print "%-40d %10d\n" % (id, val), + print "%-40s %10d\n" % (syscall_name(id), val), except TypeError: pass syscalls.clear() -- cgit v1.2.3-70-g09d2 From a64fa198ba1cd232871710c37476e006ed5516ed Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 Oct 2010 18:44:09 -0200 Subject: perf python scripting: Improve the syscalls-by-pid script . Print message at script start telling how to get te summary . Print the syscall names . Accept both pid (if numeric) or COMM name Now it looks like this: [root@emilia tmp]# perf trace syscall-counts-by-pid Press control+C to stop and show the summary ^C syscall events by comm/pid: comm [pid]/syscalls count ---------------------------------------- ---------- automount [1670] futex 2 sshd [2322] rt_sigprocmask 4 select 2 write 1 read 1 perf [15178] read 2506 open 794 close 769 write 240 getdents 112 lseek 16 stat 9 perf_counter_open 5 fcntl 5 mmap 5 statfs 2 perf [15179] read 56701 open 499 stat 176 fstat 149 close 109 mmap 98 brk 75 rt_sigaction 66 munmap 42 mprotect 24 lstat 7 lseek 5 getdents 4 ioctl 3 readlink 2 futex 1 statfs 1 getegid 1 geteuid 1 getgid 1 getuid 1 getrlimit 1 fcntl 1 uname 1 write 1 [root@emilia tmp]# fg -bash: fg: current: no such job [root@emilia tmp]# perf trace syscall-counts-by-pid 2322 Press control+C to stop and show the summary ^C syscall events by comm/pid: comm [pid]/syscalls count ---------------------------------------- ---------- sshd [2322] rt_sigprocmask 4 select 2 write 1 read 1 [root@emilia tmp]# perf trace syscall-counts-by-pid sshd Press control+C to stop and show the summary ^C syscall events for sshd: comm [pid]/syscalls count ---------------------------------------- ---------- sshd [2322] rt_sigprocmask 4 select 2 write 1 read 1 [root@emilia tmp]# Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/syscall-counts-by-pid.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py index af722d6a4b3..d1ee3ec10cf 100644 --- a/tools/perf/scripts/python/syscall-counts-by-pid.py +++ b/tools/perf/scripts/python/syscall-counts-by-pid.py @@ -5,29 +5,33 @@ # Displays system-wide system call totals, broken down by syscall. # If a [comm] arg is specified, only syscalls called by [comm] are displayed. -import os -import sys +import os, sys sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') from perf_trace_context import * from Core import * +from Util import syscall_name usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; for_comm = None +for_pid = None if len(sys.argv) > 2: sys.exit(usage) if len(sys.argv) > 1: - for_comm = sys.argv[1] + try: + for_pid = int(sys.argv[1]) + except: + for_comm = sys.argv[1] syscalls = autodict() def trace_begin(): - pass + print "Press control+C to stop and show the summary" def trace_end(): print_syscall_totals() @@ -35,9 +39,10 @@ def trace_end(): def raw_syscalls__sys_enter(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, id, args): - if for_comm is not None: - if common_comm != for_comm: - return + + if (for_comm and common_comm != for_comm) or \ + (for_pid and common_pid != for_pid ): + return try: syscalls[common_comm][common_pid][id] += 1 except TypeError: @@ -61,4 +66,4 @@ def print_syscall_totals(): id_keys = syscalls[comm][pid].keys() for id, val in sorted(syscalls[comm][pid].iteritems(), \ key = lambda(k, v): (v, k), reverse = True): - print " %-38d %10d\n" % (id, val), + print " %-38s %10d\n" % (syscall_name(id), val), -- cgit v1.2.3-70-g09d2 From 7f6c1bd50d73d12f8b4ea09edb4515997f6527f5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 25 Oct 2010 22:12:01 -0200 Subject: perf python scripting: Support fedora 11 (audit 1.7.17) Where we don't have the audit.MACH_ARMEB constant. Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 9d15f484308..99ff1b7a0d2 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -36,7 +36,6 @@ try: machine_to_id = { 'x86_64': audit.MACH_86_64, 'alpha' : audit.MACH_ALPHA, - 'armeb' : audit.MACH_ARMEB, 'ia64' : audit.MACH_IA64, 'ppc' : audit.MACH_PPC, 'ppc64' : audit.MACH_PPC64, @@ -46,6 +45,10 @@ try: 'i586' : audit.MACH_X86, 'i686' : audit.MACH_X86, } + try: + machine_to_id['armeb'] = audit.MACH_ARMEB + except: + pass machine_id = machine_to_id[os.uname()[4]] except: if not audit_package_warned: -- cgit v1.2.3-70-g09d2 From 22d0594b31046793dfb58b7ce866d7cb0a9862e5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 26 Oct 2010 15:21:15 -0200 Subject: perf python scripting: Fixup cut'n'paste error in sctop script Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/sctop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py index 547cbe99de6..7a6ec2c7d8a 100644 --- a/tools/perf/scripts/python/sctop.py +++ b/tools/perf/scripts/python/sctop.py @@ -17,7 +17,7 @@ from perf_trace_context import * from Core import * from Util import * -usage = "perf trace -s syscall-counts.py [comm] [interval]\n"; +usage = "perf trace -s sctop.py [comm] [interval]\n"; for_comm = None default_interval = 3 -- cgit v1.2.3-70-g09d2 From 00204c3396469f407bac56e1475ea16e4a279b13 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 26 Oct 2010 17:07:33 -0200 Subject: perf python scripting: Add futex-contention script The equivalent to this SystemTAP script: http://sourceware.org/systemtap/wiki/WSFutexContention [root@doppio ~]# perf trace futex-contention Press control+C to stop and show the summary ^Cnpviewer.bin[15242] lock 7f0a8be19104 contended 29 times, 72806 avg ns npviewer.bin[15242] lock 7f0a8be19130 contended 2 times, 1355 avg ns synergyc[17245] lock f127f4 contended 1 times, 1830569 avg ns firefox[15116] lock 7f2b7238af0c contended 168 times, 1230390 avg ns synergyc[17245] lock f2fc20 contended 1 times, 33149 avg ns npviewer.bin[15255] lock 7f0a8be19074 contended 155 times, 73047 avg ns npviewer.bin[15255] lock 7f0a8be190a0 contended 127 times, 7088 avg ns synergyc[17247] lock f12854 contended 1 times, 46741 avg ns synergyc[17245] lock f12610 contended 1 times, 7358 avg ns [root@doppio ~]# Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- .../python/Perf-Trace-Util/lib/Perf/Trace/Util.py | 18 ++++++++ .../scripts/python/bin/futex-contention-record | 2 + .../scripts/python/bin/futex-contention-report | 4 ++ tools/perf/scripts/python/futex-contention.py | 50 ++++++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 tools/perf/scripts/python/bin/futex-contention-record create mode 100644 tools/perf/scripts/python/bin/futex-contention-report create mode 100644 tools/perf/scripts/python/futex-contention.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 99ff1b7a0d2..13cc02b5893 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -8,6 +8,12 @@ import errno, os +FUTEX_WAIT = 0 +FUTEX_WAKE = 1 +FUTEX_PRIVATE_FLAG = 128 +FUTEX_CLOCK_REALTIME = 256 +FUTEX_CMD_MASK = ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) + NSECS_PER_SEC = 1000000000 def avg(total, n): @@ -26,6 +32,18 @@ def nsecs_str(nsecs): str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), return str +def add_stats(dict, key, value): + if not dict.has_key(key): + dict[key] = (value, value, value, 1) + else: + min, max, avg, count = dict[key] + if value < min: + min = value + if value > max: + max = value + avg = (avg + value) / 2 + dict[key] = (min, max, avg, count + 1) + def clear_term(): print("\x1b[H\x1b[2J") diff --git a/tools/perf/scripts/python/bin/futex-contention-record b/tools/perf/scripts/python/bin/futex-contention-record new file mode 100644 index 00000000000..5ecbb433caf --- /dev/null +++ b/tools/perf/scripts/python/bin/futex-contention-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -a -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@ diff --git a/tools/perf/scripts/python/bin/futex-contention-report b/tools/perf/scripts/python/bin/futex-contention-report new file mode 100644 index 00000000000..c8268138fb7 --- /dev/null +++ b/tools/perf/scripts/python/bin/futex-contention-report @@ -0,0 +1,4 @@ +#!/bin/bash +# description: futext contention measurement + +perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py diff --git a/tools/perf/scripts/python/futex-contention.py b/tools/perf/scripts/python/futex-contention.py new file mode 100644 index 00000000000..11e70a388d4 --- /dev/null +++ b/tools/perf/scripts/python/futex-contention.py @@ -0,0 +1,50 @@ +# futex contention +# (c) 2010, Arnaldo Carvalho de Melo +# Licensed under the terms of the GNU GPL License version 2 +# +# Translation of: +# +# http://sourceware.org/systemtap/wiki/WSFutexContention +# +# to perf python scripting. +# +# Measures futex contention + +import os, sys +sys.path.append(os.environ['PERF_EXEC_PATH'] + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') +from Util import * + +process_names = {} +thread_thislock = {} +thread_blocktime = {} + +lock_waits = {} # long-lived stats on (tid,lock) blockage elapsed time +process_names = {} # long-lived pid-to-execname mapping + +def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, + nr, uaddr, op, val, utime, uaddr2, val3): + cmd = op & FUTEX_CMD_MASK + if cmd != FUTEX_WAIT: + return # we don't care about originators of WAKE events + + process_names[tid] = comm + thread_thislock[tid] = uaddr + thread_blocktime[tid] = nsecs(s, ns) + +def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm, + nr, ret): + if thread_blocktime.has_key(tid): + elapsed = nsecs(s, ns) - thread_blocktime[tid] + add_stats(lock_waits, (tid, thread_thislock[tid]), elapsed) + del thread_blocktime[tid] + del thread_thislock[tid] + +def trace_begin(): + print "Press control+C to stop and show the summary" + +def trace_end(): + for (tid, lock) in lock_waits: + min, max, avg, count = lock_waits[tid, lock] + print "%s[%d] lock %x contended %d times, %d avg ns" % \ + (process_names[tid], tid, lock, count, avg) + -- cgit v1.2.3-70-g09d2 From b0b6d914e2b7e0a736635515e87be718050c17c8 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Wed, 10 Nov 2010 08:08:20 -0600 Subject: perf trace scripting: remove system-wide param from shell scripts Including -a unconditionally when recording doesn't allow for the option of running scripts without it. Future patches will add add it back if needed at run-time. Signed-off-by: Tom Zanussi Acked-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/perl/bin/failed-syscalls-record | 2 +- tools/perf/scripts/perl/bin/rw-by-file-record | 2 +- tools/perf/scripts/perl/bin/rw-by-pid-record | 2 +- tools/perf/scripts/perl/bin/rwtop-record | 2 +- tools/perf/scripts/perl/bin/wakeup-latency-record | 2 +- tools/perf/scripts/perl/bin/workqueue-stats-record | 2 +- tools/perf/scripts/python/bin/failed-syscalls-by-pid-record | 2 +- tools/perf/scripts/python/bin/futex-contention-record | 2 +- tools/perf/scripts/python/bin/netdev-times-record | 2 +- tools/perf/scripts/python/bin/sched-migration-record | 2 +- tools/perf/scripts/python/bin/sctop-record | 2 +- tools/perf/scripts/python/bin/syscall-counts-by-pid-record | 2 +- tools/perf/scripts/python/bin/syscall-counts-record | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record index eb5846bcb56..8104895a7b6 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-record +++ b/tools/perf/scripts/perl/bin/failed-syscalls-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_exit $@ +perf record -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record index 5bfaae5a6cb..33efc8673aa 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-record +++ b/tools/perf/scripts/perl/bin/rw-by-file-record @@ -1,3 +1,3 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ +perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record index 6e0b2f7755a..7cb9db23044 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-record +++ b/tools/perf/scripts/perl/bin/rw-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ +perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record index 6e0b2f7755a..7cb9db23044 100644 --- a/tools/perf/scripts/perl/bin/rwtop-record +++ b/tools/perf/scripts/perl/bin/rwtop-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ +perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record index 9f2acaaae9f..464251a1bd7 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-record +++ b/tools/perf/scripts/perl/bin/wakeup-latency-record @@ -1,5 +1,5 @@ #!/bin/bash -perf record -a -e sched:sched_switch -e sched:sched_wakeup $@ +perf record -e sched:sched_switch -e sched:sched_wakeup $@ diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record index 85301f2471f..8edda9078d5 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-record +++ b/tools/perf/scripts/perl/bin/workqueue-stats-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ +perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record index eb5846bcb56..8104895a7b6 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_exit $@ +perf record -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/python/bin/futex-contention-record b/tools/perf/scripts/python/bin/futex-contention-record index 5ecbb433caf..b1495c9a9b2 100644 --- a/tools/perf/scripts/python/bin/futex-contention-record +++ b/tools/perf/scripts/python/bin/futex-contention-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@ +perf record -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@ diff --git a/tools/perf/scripts/python/bin/netdev-times-record b/tools/perf/scripts/python/bin/netdev-times-record index d931a828126..558754b840a 100644 --- a/tools/perf/scripts/python/bin/netdev-times-record +++ b/tools/perf/scripts/python/bin/netdev-times-record @@ -1,5 +1,5 @@ #!/bin/bash -perf record -a -e net:net_dev_xmit -e net:net_dev_queue \ +perf record -e net:net_dev_xmit -e net:net_dev_queue \ -e net:netif_receive_skb -e net:netif_rx \ -e skb:consume_skb -e skb:kfree_skb \ -e skb:skb_copy_datagram_iovec -e napi:napi_poll \ diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/perf/scripts/python/bin/sched-migration-record index 17a3e9bd9e8..7493fddbe99 100644 --- a/tools/perf/scripts/python/bin/sched-migration-record +++ b/tools/perf/scripts/python/bin/sched-migration-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ +perf record -m 16384 -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/scripts/python/bin/sctop-record index 1fc5998b721..4efbfaa7f6a 100644 --- a/tools/perf/scripts/python/bin/sctop-record +++ b/tools/perf/scripts/python/bin/sctop-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ +perf record -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record index 1fc5998b721..4efbfaa7f6a 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ +perf record -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/perf/scripts/python/bin/syscall-counts-record index 1fc5998b721..4efbfaa7f6a 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-record +++ b/tools/perf/scripts/python/bin/syscall-counts-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -a -e raw_syscalls:sys_enter $@ +perf record -e raw_syscalls:sys_enter $@ -- cgit v1.2.3-70-g09d2 From 133dc4c39c57eeef2577ca5b4ed24765b7a78ce2 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 16 Nov 2010 18:45:39 +0100 Subject: perf: Rename 'perf trace' to 'perf script' Free the perf trace name space and rename the trace to 'script' which is a better match for the scripting engine. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- tools/perf/Documentation/perf-script-perl.txt | 217 ++++++ tools/perf/Documentation/perf-script-python.txt | 623 ++++++++++++++++ tools/perf/Documentation/perf-script.txt | 111 +++ tools/perf/Documentation/perf-trace-perl.txt | 217 ------ tools/perf/Documentation/perf-trace-python.txt | 623 ---------------- tools/perf/Documentation/perf-trace.txt | 111 --- tools/perf/Makefile | 2 +- tools/perf/builtin-lock.c | 6 +- tools/perf/builtin-sched.c | 6 +- tools/perf/builtin-script.c | 826 +++++++++++++++++++++ tools/perf/builtin-trace.c | 826 --------------------- tools/perf/builtin.h | 2 +- tools/perf/perf.c | 2 +- .../perf/scripts/python/Perf-Trace-Util/Context.c | 2 +- .../perf/util/scripting-engines/trace-event-perl.c | 6 +- .../util/scripting-engines/trace-event-python.c | 4 +- 16 files changed, 1792 insertions(+), 1792 deletions(-) create mode 100644 tools/perf/Documentation/perf-script-perl.txt create mode 100644 tools/perf/Documentation/perf-script-python.txt create mode 100644 tools/perf/Documentation/perf-script.txt delete mode 100644 tools/perf/Documentation/perf-trace-perl.txt delete mode 100644 tools/perf/Documentation/perf-trace-python.txt delete mode 100644 tools/perf/Documentation/perf-trace.txt create mode 100644 tools/perf/builtin-script.c delete mode 100644 tools/perf/builtin-trace.c (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt new file mode 100644 index 00000000000..5bb41e55a3a --- /dev/null +++ b/tools/perf/Documentation/perf-script-perl.txt @@ -0,0 +1,217 @@ +perf-script-perl(1) +================== + +NAME +---- +perf-script-perl - Process trace data with a Perl script + +SYNOPSIS +-------- +[verse] +'perf script' [-s [Perl]:script[.pl] ] + +DESCRIPTION +----------- + +This perf script option is used to process perf script data using perf's +built-in Perl interpreter. It reads and processes the input file and +displays the results of the trace analysis implemented in the given +Perl script, if any. + +STARTER SCRIPTS +--------------- + +You can avoid reading the rest of this document by running 'perf script +-g perl' in the same directory as an existing perf.data trace file. +That will generate a starter script containing a handler for each of +the event types in the trace file; it simply prints every available +field for each event in the trace file. + +You can also look at the existing scripts in +~/libexec/perf-core/scripts/perl for typical examples showing how to +do basic things like aggregate event data, print results, etc. Also, +the check-perf-script.pl script, while not interesting for its results, +attempts to exercise all of the main scripting features. + +EVENT HANDLERS +-------------- + +When perf script is invoked using a trace script, a user-defined +'handler function' is called for each event in the trace. If there's +no handler function defined for a given event type, the event is +ignored (or passed to a 'trace_handled' function, see below) and the +next event is processed. + +Most of the event's field values are passed as arguments to the +handler function; some of the less common ones aren't - those are +available as calls back into the perf executable (see below). + +As an example, the following perf record command can be used to record +all sched_wakeup events in the system: + + # perf record -a -e sched:sched_wakeup + +Traces meant to be processed using a script should be recorded with +the above option: -a to enable system-wide collection. + +The format file for the sched_wakep event defines the following fields +(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): + +---- + format: + field:unsigned short common_type; + field:unsigned char common_flags; + field:unsigned char common_preempt_count; + field:int common_pid; + field:int common_lock_depth; + + field:char comm[TASK_COMM_LEN]; + field:pid_t pid; + field:int prio; + field:int success; + field:int target_cpu; +---- + +The handler function for this event would be defined as: + +---- +sub sched::sched_wakeup +{ + my ($event_name, $context, $common_cpu, $common_secs, + $common_nsecs, $common_pid, $common_comm, + $comm, $pid, $prio, $success, $target_cpu) = @_; +} +---- + +The handler function takes the form subsystem::event_name. + +The $common_* arguments in the handler's argument list are the set of +arguments passed to all event handlers; some of the fields correspond +to the common_* fields in the format file, but some are synthesized, +and some of the common_* fields aren't common enough to to be passed +to every event as arguments but are available as library functions. + +Here's a brief description of each of the invariant event args: + + $event_name the name of the event as text + $context an opaque 'cookie' used in calls back into perf + $common_cpu the cpu the event occurred on + $common_secs the secs portion of the event timestamp + $common_nsecs the nsecs portion of the event timestamp + $common_pid the pid of the current task + $common_comm the name of the current process + +All of the remaining fields in the event's format file have +counterparts as handler function arguments of the same name, as can be +seen in the example above. + +The above provides the basics needed to directly access every field of +every event in a trace, which covers 90% of what you need to know to +write a useful trace script. The sections below cover the rest. + +SCRIPT LAYOUT +------------- + +Every perf script Perl script should start by setting up a Perl module +search path and 'use'ing a few support modules (see module +descriptions below): + +---- + use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib"; + use lib "./perf-script-Util/lib"; + use Perf::Trace::Core; + use Perf::Trace::Context; + use Perf::Trace::Util; +---- + +The rest of the script can contain handler functions and support +functions in any order. + +Aside from the event handler functions discussed above, every script +can implement a set of optional functions: + +*trace_begin*, if defined, is called before any event is processed and +gives scripts a chance to do setup tasks: + +---- + sub trace_begin + { + } +---- + +*trace_end*, if defined, is called after all events have been + processed and gives scripts a chance to do end-of-script tasks, such + as display results: + +---- +sub trace_end +{ +} +---- + +*trace_unhandled*, if defined, is called after for any event that + doesn't have a handler explicitly defined for it. The standard set + of common arguments are passed into it: + +---- +sub trace_unhandled +{ + my ($event_name, $context, $common_cpu, $common_secs, + $common_nsecs, $common_pid, $common_comm) = @_; +} +---- + +The remaining sections provide descriptions of each of the available +built-in perf script Perl modules and their associated functions. + +AVAILABLE MODULES AND FUNCTIONS +------------------------------- + +The following sections describe the functions and variables available +via the various Perf::Trace::* Perl modules. To use the functions and +variables from the given module, add the corresponding 'use +Perf::Trace::XXX' line to your perf script script. + +Perf::Trace::Core Module +~~~~~~~~~~~~~~~~~~~~~~~~ + +These functions provide some essential functions to user scripts. + +The *flag_str* and *symbol_str* functions provide human-readable +strings for flag and symbolic fields. These correspond to the strings +and values parsed from the 'print fmt' fields of the event format +files: + + flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name + symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name + +Perf::Trace::Context Module +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some of the 'common' fields in the event format file aren't all that +common, but need to be made accessible to user scripts nonetheless. + +Perf::Trace::Context defines a set of functions that can be used to +access this data in the context of the current event. Each of these +functions expects a $context variable, which is the same as the +$context variable passed into every event handler as the second +argument. + + common_pc($context) - returns common_preempt count for the current event + common_flags($context) - returns common_flags for the current event + common_lock_depth($context) - returns common_lock_depth for the current event + +Perf::Trace::Util Module +~~~~~~~~~~~~~~~~~~~~~~~~ + +Various utility functions for use with perf script: + + nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair + nsecs_secs($nsecs) - returns whole secs portion given nsecs + nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs + nsecs_str($nsecs) - returns printable string in the form secs.nsecs + avg($total, $n) - returns average given a sum and a total number of values + +SEE ALSO +-------- +linkperf:perf-script[1] diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt new file mode 100644 index 00000000000..36b38277422 --- /dev/null +++ b/tools/perf/Documentation/perf-script-python.txt @@ -0,0 +1,623 @@ +perf-script-python(1) +==================== + +NAME +---- +perf-script-python - Process trace data with a Python script + +SYNOPSIS +-------- +[verse] +'perf script' [-s [Python]:script[.py] ] + +DESCRIPTION +----------- + +This perf script option is used to process perf script data using perf's +built-in Python interpreter. It reads and processes the input file and +displays the results of the trace analysis implemented in the given +Python script, if any. + +A QUICK EXAMPLE +--------------- + +This section shows the process, start to finish, of creating a working +Python script that aggregates and extracts useful information from a +raw perf script stream. You can avoid reading the rest of this +document if an example is enough for you; the rest of the document +provides more details on each step and lists the library functions +available to script writers. + +This example actually details the steps that were used to create the +'syscall-counts' script you see when you list the available perf script +scripts via 'perf script -l'. As such, this script also shows how to +integrate your script into the list of general-purpose 'perf script' +scripts listed by that command. + +The syscall-counts script is a simple script, but demonstrates all the +basic ideas necessary to create a useful script. Here's an example +of its output (syscall names are not yet supported, they will appear +as numbers): + +---- +syscall events: + +event count +---------------------------------------- ----------- +sys_write 455067 +sys_getdents 4072 +sys_close 3037 +sys_swapoff 1769 +sys_read 923 +sys_sched_setparam 826 +sys_open 331 +sys_newfstat 326 +sys_mmap 217 +sys_munmap 216 +sys_futex 141 +sys_select 102 +sys_poll 84 +sys_setitimer 12 +sys_writev 8 +15 8 +sys_lseek 7 +sys_rt_sigprocmask 6 +sys_wait4 3 +sys_ioctl 3 +sys_set_robust_list 1 +sys_exit 1 +56 1 +sys_access 1 +---- + +Basically our task is to keep a per-syscall tally that gets updated +every time a system call occurs in the system. Our script will do +that, but first we need to record the data that will be processed by +that script. Theoretically, there are a couple of ways we could do +that: + +- we could enable every event under the tracing/events/syscalls + directory, but this is over 600 syscalls, well beyond the number + allowable by perf. These individual syscall events will however be + useful if we want to later use the guidance we get from the + general-purpose scripts to drill down and get more detail about + individual syscalls of interest. + +- we can enable the sys_enter and/or sys_exit syscalls found under + tracing/events/raw_syscalls. These are called for all syscalls; the + 'id' field can be used to distinguish between individual syscall + numbers. + +For this script, we only need to know that a syscall was entered; we +don't care how it exited, so we'll use 'perf record' to record only +the sys_enter events: + +---- +# perf record -a -e raw_syscalls:sys_enter + +^C[ perf record: Woken up 1 times to write data ] +[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ] +---- + +The options basically say to collect data for every syscall event +system-wide and multiplex the per-cpu output into a single stream. +That single stream will be recorded in a file in the current directory +called perf.data. + +Once we have a perf.data file containing our data, we can use the -g +'perf script' option to generate a Python script that will contain a +callback handler for each event type found in the perf.data trace +stream (for more details, see the STARTER SCRIPTS section). + +---- +# perf script -g python +generated Python script: perf-script.py + +The output file created also in the current directory is named +perf-script.py. Here's the file in its entirety: + +# perf script event handlers, generated by perf script -g python +# Licensed under the terms of the GNU GPL License version 2 + +# The common_* event handler fields are the most useful fields common to +# all events. They don't necessarily correspond to the 'common_*' fields +# in the format files. Those fields not available as handler params can +# be retrieved using Python functions of the form common_*(context). +# See the perf-script-python Documentation for the list of available functions. + +import os +import sys + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/perf-script-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * + +def trace_begin(): + print "in trace_begin" + +def trace_end(): + print "in trace_end" + +def raw_syscalls__sys_enter(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + id, args): + print_header(event_name, common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + + print "id=%d, args=%s\n" % \ + (id, args), + +def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, + common_pid, common_comm): + print_header(event_name, common_cpu, common_secs, common_nsecs, + common_pid, common_comm) + +def print_header(event_name, cpu, secs, nsecs, pid, comm): + print "%-20s %5u %05u.%09u %8u %-20s " % \ + (event_name, cpu, secs, nsecs, pid, comm), +---- + +At the top is a comment block followed by some import statements and a +path append which every perf script script should include. + +Following that are a couple generated functions, trace_begin() and +trace_end(), which are called at the beginning and the end of the +script respectively (for more details, see the SCRIPT_LAYOUT section +below). + +Following those are the 'event handler' functions generated one for +every event in the 'perf record' output. The handler functions take +the form subsystem__event_name, and contain named parameters, one for +each field in the event; in this case, there's only one event, +raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for +more info on event handlers). + +The final couple of functions are, like the begin and end functions, +generated for every script. The first, trace_unhandled(), is called +every time the script finds an event in the perf.data file that +doesn't correspond to any event handler in the script. This could +mean either that the record step recorded event types that it wasn't +really interested in, or the script was run against a trace file that +doesn't correspond to the script. + +The script generated by -g option simply prints a line for each +event found in the trace stream i.e. it basically just dumps the event +and its parameter values to stdout. The print_header() function is +simply a utility function used for that purpose. Let's rename the +script and run it to see the default output: + +---- +# mv perf-script.py syscall-counts.py +# perf script -s syscall-counts.py + +raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args= +raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args= +raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args= +raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args= +raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args= +raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args= +raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args= +raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args= +. +. +. +---- + +Of course, for this script, we're not interested in printing every +trace event, but rather aggregating it in a useful way. So we'll get +rid of everything to do with printing as well as the trace_begin() and +trace_unhandled() functions, which we won't be using. That leaves us +with this minimalistic skeleton: + +---- +import os +import sys + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/perf-script-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * + +def trace_end(): + print "in trace_end" + +def raw_syscalls__sys_enter(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + id, args): +---- + +In trace_end(), we'll simply print the results, but first we need to +generate some results to print. To do that we need to have our +sys_enter() handler do the necessary tallying until all events have +been counted. A hash table indexed by syscall id is a good way to +store that information; every time the sys_enter() handler is called, +we simply increment a count associated with that hash entry indexed by +that syscall id: + +---- + syscalls = autodict() + + try: + syscalls[id] += 1 + except TypeError: + syscalls[id] = 1 +---- + +The syscalls 'autodict' object is a special kind of Python dictionary +(implemented in Core.py) that implements Perl's 'autovivifying' hashes +in Python i.e. with autovivifying hashes, you can assign nested hash +values without having to go to the trouble of creating intermediate +levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create +the intermediate hash levels and finally assign the value 1 to the +hash entry for 'id' (because the value being assigned isn't a hash +object itself, the initial value is assigned in the TypeError +exception. Well, there may be a better way to do this in Python but +that's what works for now). + +Putting that code into the raw_syscalls__sys_enter() handler, we +effectively end up with a single-level dictionary keyed on syscall id +and having the counts we've tallied as values. + +The print_syscall_totals() function iterates over the entries in the +dictionary and displays a line for each entry containing the syscall +name (the dictonary keys contain the syscall ids, which are passed to +the Util function syscall_name(), which translates the raw syscall +numbers to the corresponding syscall name strings). The output is +displayed after all the events in the trace have been processed, by +calling the print_syscall_totals() function from the trace_end() +handler called at the end of script processing. + +The final script producing the output shown above is shown in its +entirety below (syscall_name() helper is not yet available, you can +only deal with id's for now): + +---- +import os +import sys + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/perf-script-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * +from Util import * + +syscalls = autodict() + +def trace_end(): + print_syscall_totals() + +def raw_syscalls__sys_enter(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + id, args): + try: + syscalls[id] += 1 + except TypeError: + syscalls[id] = 1 + +def print_syscall_totals(): + if for_comm is not None: + print "\nsyscall events for %s:\n\n" % (for_comm), + else: + print "\nsyscall events:\n\n", + + print "%-40s %10s\n" % ("event", "count"), + print "%-40s %10s\n" % ("----------------------------------------", \ + "-----------"), + + for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ + reverse = True): + print "%-40s %10d\n" % (syscall_name(id), val), +---- + +The script can be run just as before: + + # perf script -s syscall-counts.py + +So those are the essential steps in writing and running a script. The +process can be generalized to any tracepoint or set of tracepoints +you're interested in - basically find the tracepoint(s) you're +interested in by looking at the list of available events shown by +'perf list' and/or look in /sys/kernel/debug/tracing events for +detailed event and field info, record the corresponding trace data +using 'perf record', passing it the list of interesting events, +generate a skeleton script using 'perf script -g python' and modify the +code to aggregate and display it for your particular needs. + +After you've done that you may end up with a general-purpose script +that you want to keep around and have available for future use. By +writing a couple of very simple shell scripts and putting them in the +right place, you can have your script listed alongside the other +scripts listed by the 'perf script -l' command e.g.: + +---- +root@tropicana:~# perf script -l +List of available trace scripts: + workqueue-stats workqueue stats (ins/exe/create/destroy) + wakeup-latency system-wide min/max/avg wakeup latency + rw-by-file r/w activity for a program, by file + rw-by-pid system-wide r/w activity +---- + +A nice side effect of doing this is that you also then capture the +probably lengthy 'perf record' command needed to record the events for +the script. + +To have the script appear as a 'built-in' script, you write two simple +scripts, one for recording and one for 'reporting'. + +The 'record' script is a shell script with the same base name as your +script, but with -record appended. The shell script should be put +into the perf/scripts/python/bin directory in the kernel source tree. +In that script, you write the 'perf record' command-line needed for +your script: + +---- +# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record + +#!/bin/bash +perf record -a -e raw_syscalls:sys_enter +---- + +The 'report' script is also a shell script with the same base name as +your script, but with -report appended. It should also be located in +the perf/scripts/python/bin directory. In that script, you write the +'perf script -s' command-line needed for running your script: + +---- +# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report + +#!/bin/bash +# description: system-wide syscall counts +perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py +---- + +Note that the location of the Python script given in the shell script +is in the libexec/perf-core/scripts/python directory - this is where +the script will be copied by 'make install' when you install perf. +For the installation to install your script there, your script needs +to be located in the perf/scripts/python directory in the kernel +source tree: + +---- +# ls -al kernel-source/tools/perf/scripts/python + +root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python +total 32 +drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 . +drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 .. +drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin +-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py +drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util +-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py +---- + +Once you've done that (don't forget to do a new 'make install', +otherwise your script won't show up at run-time), 'perf script -l' +should show a new entry for your script: + +---- +root@tropicana:~# perf script -l +List of available trace scripts: + workqueue-stats workqueue stats (ins/exe/create/destroy) + wakeup-latency system-wide min/max/avg wakeup latency + rw-by-file r/w activity for a program, by file + rw-by-pid system-wide r/w activity + syscall-counts system-wide syscall counts +---- + +You can now perform the record step via 'perf script record': + + # perf script record syscall-counts + +and display the output using 'perf script report': + + # perf script report syscall-counts + +STARTER SCRIPTS +--------------- + +You can quickly get started writing a script for a particular set of +trace data by generating a skeleton script using 'perf script -g +python' in the same directory as an existing perf.data trace file. +That will generate a starter script containing a handler for each of +the event types in the trace file; it simply prints every available +field for each event in the trace file. + +You can also look at the existing scripts in +~/libexec/perf-core/scripts/python for typical examples showing how to +do basic things like aggregate event data, print results, etc. Also, +the check-perf-script.py script, while not interesting for its results, +attempts to exercise all of the main scripting features. + +EVENT HANDLERS +-------------- + +When perf script is invoked using a trace script, a user-defined +'handler function' is called for each event in the trace. If there's +no handler function defined for a given event type, the event is +ignored (or passed to a 'trace_handled' function, see below) and the +next event is processed. + +Most of the event's field values are passed as arguments to the +handler function; some of the less common ones aren't - those are +available as calls back into the perf executable (see below). + +As an example, the following perf record command can be used to record +all sched_wakeup events in the system: + + # perf record -a -e sched:sched_wakeup + +Traces meant to be processed using a script should be recorded with +the above option: -a to enable system-wide collection. + +The format file for the sched_wakep event defines the following fields +(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): + +---- + format: + field:unsigned short common_type; + field:unsigned char common_flags; + field:unsigned char common_preempt_count; + field:int common_pid; + field:int common_lock_depth; + + field:char comm[TASK_COMM_LEN]; + field:pid_t pid; + field:int prio; + field:int success; + field:int target_cpu; +---- + +The handler function for this event would be defined as: + +---- +def sched__sched_wakeup(event_name, context, common_cpu, common_secs, + common_nsecs, common_pid, common_comm, + comm, pid, prio, success, target_cpu): + pass +---- + +The handler function takes the form subsystem__event_name. + +The common_* arguments in the handler's argument list are the set of +arguments passed to all event handlers; some of the fields correspond +to the common_* fields in the format file, but some are synthesized, +and some of the common_* fields aren't common enough to to be passed +to every event as arguments but are available as library functions. + +Here's a brief description of each of the invariant event args: + + event_name the name of the event as text + context an opaque 'cookie' used in calls back into perf + common_cpu the cpu the event occurred on + common_secs the secs portion of the event timestamp + common_nsecs the nsecs portion of the event timestamp + common_pid the pid of the current task + common_comm the name of the current process + +All of the remaining fields in the event's format file have +counterparts as handler function arguments of the same name, as can be +seen in the example above. + +The above provides the basics needed to directly access every field of +every event in a trace, which covers 90% of what you need to know to +write a useful trace script. The sections below cover the rest. + +SCRIPT LAYOUT +------------- + +Every perf script Python script should start by setting up a Python +module search path and 'import'ing a few support modules (see module +descriptions below): + +---- + import os + import sys + + sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/perf-script-Util/lib/Perf/Trace') + + from perf_trace_context import * + from Core import * +---- + +The rest of the script can contain handler functions and support +functions in any order. + +Aside from the event handler functions discussed above, every script +can implement a set of optional functions: + +*trace_begin*, if defined, is called before any event is processed and +gives scripts a chance to do setup tasks: + +---- +def trace_begin: + pass +---- + +*trace_end*, if defined, is called after all events have been + processed and gives scripts a chance to do end-of-script tasks, such + as display results: + +---- +def trace_end: + pass +---- + +*trace_unhandled*, if defined, is called after for any event that + doesn't have a handler explicitly defined for it. The standard set + of common arguments are passed into it: + +---- +def trace_unhandled(event_name, context, common_cpu, common_secs, + common_nsecs, common_pid, common_comm): + pass +---- + +The remaining sections provide descriptions of each of the available +built-in perf script Python modules and their associated functions. + +AVAILABLE MODULES AND FUNCTIONS +------------------------------- + +The following sections describe the functions and variables available +via the various perf script Python modules. To use the functions and +variables from the given module, add the corresponding 'from XXXX +import' line to your perf script script. + +Core.py Module +~~~~~~~~~~~~~~ + +These functions provide some essential functions to user scripts. + +The *flag_str* and *symbol_str* functions provide human-readable +strings for flag and symbolic fields. These correspond to the strings +and values parsed from the 'print fmt' fields of the event format +files: + + flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name + symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name + +The *autodict* function returns a special kind of Python +dictionary that implements Perl's 'autovivifying' hashes in Python +i.e. with autovivifying hashes, you can assign nested hash values +without having to go to the trouble of creating intermediate levels if +they don't exist. + + autodict() - returns an autovivifying dictionary instance + + +perf_trace_context Module +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some of the 'common' fields in the event format file aren't all that +common, but need to be made accessible to user scripts nonetheless. + +perf_trace_context defines a set of functions that can be used to +access this data in the context of the current event. Each of these +functions expects a context variable, which is the same as the +context variable passed into every event handler as the second +argument. + + common_pc(context) - returns common_preempt count for the current event + common_flags(context) - returns common_flags for the current event + common_lock_depth(context) - returns common_lock_depth for the current event + +Util.py Module +~~~~~~~~~~~~~~ + +Various utility functions for use with perf script: + + nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair + nsecs_secs(nsecs) - returns whole secs portion given nsecs + nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs + nsecs_str(nsecs) - returns printable string in the form secs.nsecs + avg(total, n) - returns average given a sum and a total number of values + +SEE ALSO +-------- +linkperf:perf-script[1] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt new file mode 100644 index 00000000000..f442acc105b --- /dev/null +++ b/tools/perf/Documentation/perf-script.txt @@ -0,0 +1,111 @@ +perf-script(1) +============= + +NAME +---- +perf-script - Read perf.data (created by perf record) and display trace output + +SYNOPSIS +-------- +[verse] +'perf script' [] +'perf script' [] record