From ec1e6e6a681815e4d02a54eed92edec6fa4128ee Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Tue, 5 Jun 2018 18:18:01 +0530 Subject: perf script powerpc: Python script for hypervisor call statistics Add python script to show hypervisor call statistics. Ex, # perf record -a -e "{powerpc:hcall_entry,powerpc:hcall_exit}" # perf script -s scripts/python/powerpc-hcalls.py hcall count min(ns) max(ns) avg(ns) -------------------------------------------------------------------- H_RANDOM 82 838 1164 904 H_PUT_TCE 47 1078 5928 2003 H_EOI 266 1336 3546 1654 H_ENTER 28 1646 4038 1952 H_PUT_TCE_INDIRECT 230 2166 18168 6109 H_IPI 238 1072 3232 1688 H_SEND_LOGICAL_LAN 42 5488 21366 7694 H_STUFF_TCE 294 986 6210 3591 H_XIRR 266 2286 6990 3783 H_PROTECT 10 2196 3556 2555 H_VIO_SIGNAL 294 1028 2784 1311 H_ADD_LOGICAL_LAN_BUFFER 53 1978 3450 2600 H_SEND_CRQ 77 1762 7240 2447 Signed-off-by: Ravi Bangoria Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Michael Ellerman Cc: Namhyung Kim Cc: Naveen N. Rao Link: http://lkml.kernel.org/r/20180605124801.17210-1-ravi.bangoria@linux.ibm.com [ Fixup typo: table_loockup -> table_lookup ] Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/scripts/python/bin/powerpc-hcalls-record | 2 + .../perf/scripts/python/bin/powerpc-hcalls-report | 2 + tools/perf/scripts/python/powerpc-hcalls.py | 200 +++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 tools/perf/scripts/python/bin/powerpc-hcalls-record create mode 100644 tools/perf/scripts/python/bin/powerpc-hcalls-report create mode 100644 tools/perf/scripts/python/powerpc-hcalls.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/bin/powerpc-hcalls-record b/tools/perf/scripts/python/bin/powerpc-hcalls-record new file mode 100644 index 000000000000..b7402aa9147d --- /dev/null +++ b/tools/perf/scripts/python/bin/powerpc-hcalls-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -e "{powerpc:hcall_entry,powerpc:hcall_exit}" $@ diff --git a/tools/perf/scripts/python/bin/powerpc-hcalls-report b/tools/perf/scripts/python/bin/powerpc-hcalls-report new file mode 100644 index 000000000000..dd32ad7465f6 --- /dev/null +++ b/tools/perf/scripts/python/bin/powerpc-hcalls-report @@ -0,0 +1,2 @@ +#!/bin/bash +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/powerpc-hcalls.py diff --git a/tools/perf/scripts/python/powerpc-hcalls.py b/tools/perf/scripts/python/powerpc-hcalls.py new file mode 100644 index 000000000000..00e0e7476e55 --- /dev/null +++ b/tools/perf/scripts/python/powerpc-hcalls.py @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2018 Ravi Bangoria, IBM Corporation +# +# Hypervisor call statisics + +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 * + +# output: { +# opcode: { +# 'min': minimum time nsec +# 'max': maximum time nsec +# 'time': average time nsec +# 'cnt': counter +# } ... +# } +output = {} + +# d_enter: { +# cpu: { +# opcode: nsec +# } ... +# } +d_enter = {} + +hcall_table = { + 4: 'H_REMOVE', + 8: 'H_ENTER', + 12: 'H_READ', + 16: 'H_CLEAR_MOD', + 20: 'H_CLEAR_REF', + 24: 'H_PROTECT', + 28: 'H_GET_TCE', + 32: 'H_PUT_TCE', + 36: 'H_SET_SPRG0', + 40: 'H_SET_DABR', + 44: 'H_PAGE_INIT', + 48: 'H_SET_ASR', + 52: 'H_ASR_ON', + 56: 'H_ASR_OFF', + 60: 'H_LOGICAL_CI_LOAD', + 64: 'H_LOGICAL_CI_STORE', + 68: 'H_LOGICAL_CACHE_LOAD', + 72: 'H_LOGICAL_CACHE_STORE', + 76: 'H_LOGICAL_ICBI', + 80: 'H_LOGICAL_DCBF', + 84: 'H_GET_TERM_CHAR', + 88: 'H_PUT_TERM_CHAR', + 92: 'H_REAL_TO_LOGICAL', + 96: 'H_HYPERVISOR_DATA', + 100: 'H_EOI', + 104: 'H_CPPR', + 108: 'H_IPI', + 112: 'H_IPOLL', + 116: 'H_XIRR', + 120: 'H_MIGRATE_DMA', + 124: 'H_PERFMON', + 220: 'H_REGISTER_VPA', + 224: 'H_CEDE', + 228: 'H_CONFER', + 232: 'H_PROD', + 236: 'H_GET_PPP', + 240: 'H_SET_PPP', + 244: 'H_PURR', + 248: 'H_PIC', + 252: 'H_REG_CRQ', + 256: 'H_FREE_CRQ', + 260: 'H_VIO_SIGNAL', + 264: 'H_SEND_CRQ', + 272: 'H_COPY_RDMA', + 276: 'H_REGISTER_LOGICAL_LAN', + 280: 'H_FREE_LOGICAL_LAN', + 284: 'H_ADD_LOGICAL_LAN_BUFFER', + 288: 'H_SEND_LOGICAL_LAN', + 292: 'H_BULK_REMOVE', + 304: 'H_MULTICAST_CTRL', + 308: 'H_SET_XDABR', + 312: 'H_STUFF_TCE', + 316: 'H_PUT_TCE_INDIRECT', + 332: 'H_CHANGE_LOGICAL_LAN_MAC', + 336: 'H_VTERM_PARTNER_INFO', + 340: 'H_REGISTER_VTERM', + 344: 'H_FREE_VTERM', + 348: 'H_RESET_EVENTS', + 352: 'H_ALLOC_RESOURCE', + 356: 'H_FREE_RESOURCE', + 360: 'H_MODIFY_QP', + 364: 'H_QUERY_QP', + 368: 'H_REREGISTER_PMR', + 372: 'H_REGISTER_SMR', + 376: 'H_QUERY_MR', + 380: 'H_QUERY_MW', + 384: 'H_QUERY_HCA', + 388: 'H_QUERY_PORT', + 392: 'H_MODIFY_PORT', + 396: 'H_DEFINE_AQP1', + 400: 'H_GET_TRACE_BUFFER', + 404: 'H_DEFINE_AQP0', + 408: 'H_RESIZE_MR', + 412: 'H_ATTACH_MCQP', + 416: 'H_DETACH_MCQP', + 420: 'H_CREATE_RPT', + 424: 'H_REMOVE_RPT', + 428: 'H_REGISTER_RPAGES', + 432: 'H_DISABLE_AND_GETC', + 436: 'H_ERROR_DATA', + 440: 'H_GET_HCA_INFO', + 444: 'H_GET_PERF_COUNT', + 448: 'H_MANAGE_TRACE', + 468: 'H_FREE_LOGICAL_LAN_BUFFER', + 472: 'H_POLL_PENDING', + 484: 'H_QUERY_INT_STATE', + 580: 'H_ILLAN_ATTRIBUTES', + 592: 'H_MODIFY_HEA_QP', + 596: 'H_QUERY_HEA_QP', + 600: 'H_QUERY_HEA', + 604: 'H_QUERY_HEA_PORT', + 608: 'H_MODIFY_HEA_PORT', + 612: 'H_REG_BCMC', + 616: 'H_DEREG_BCMC', + 620: 'H_REGISTER_HEA_RPAGES', + 624: 'H_DISABLE_AND_GET_HEA', + 628: 'H_GET_HEA_INFO', + 632: 'H_ALLOC_HEA_RESOURCE', + 644: 'H_ADD_CONN', + 648: 'H_DEL_CONN', + 664: 'H_JOIN', + 676: 'H_VASI_STATE', + 688: 'H_ENABLE_CRQ', + 696: 'H_GET_EM_PARMS', + 720: 'H_SET_MPP', + 724: 'H_GET_MPP', + 748: 'H_HOME_NODE_ASSOCIATIVITY', + 756: 'H_BEST_ENERGY', + 764: 'H_XIRR_X', + 768: 'H_RANDOM', + 772: 'H_COP', + 788: 'H_GET_MPP_X', + 796: 'H_SET_MODE', + 61440: 'H_RTAS', +} + +def hcall_table_lookup(opcode): + if (hcall_table.has_key(opcode)): + return hcall_table[opcode] + else: + return opcode + +print_ptrn = '%-28s%10s%10s%10s%10s' + +def trace_end(): + print print_ptrn % ('hcall', 'count', 'min(ns)', 'max(ns)', 'avg(ns)') + print '-' * 68 + for opcode in output: + h_name = hcall_table_lookup(opcode) + time = output[opcode]['time'] + cnt = output[opcode]['cnt'] + min_t = output[opcode]['min'] + max_t = output[opcode]['max'] + + print print_ptrn % (h_name, cnt, min_t, max_t, time/cnt) + +def powerpc__hcall_exit(name, context, cpu, sec, nsec, pid, comm, callchain, + opcode, retval): + if (d_enter.has_key(cpu) and d_enter[cpu].has_key(opcode)): + diff = nsecs(sec, nsec) - d_enter[cpu][opcode] + + if (output.has_key(opcode)): + output[opcode]['time'] += diff + output[opcode]['cnt'] += 1 + if (output[opcode]['min'] > diff): + output[opcode]['min'] = diff + if (output[opcode]['max'] < diff): + output[opcode]['max'] = diff + else: + output[opcode] = { + 'time': diff, + 'cnt': 1, + 'min': diff, + 'max': diff, + } + + del d_enter[cpu][opcode] +# else: +# print "Can't find matching hcall_enter event. Ignoring sample" + +def powerpc__hcall_entry(event_name, context, cpu, sec, nsec, pid, comm, + callchain, opcode): + if (d_enter.has_key(cpu)): + d_enter[cpu][opcode] = nsecs(sec, nsec) + else: + d_enter[cpu] = {opcode: nsecs(sec, nsec)} -- cgit v1.2.3 From 770d2f86c0051d4f2c0ab9d74d68434cb383241d Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Tue, 8 May 2018 21:27:45 +0000 Subject: perf scripts python: Add Python 3 support to Core.py Support both Python 2 and Python 3 in Core.py. This should have no functional change. Signed-off-by: Jeremy Cline Cc: Alexander Shishkin Cc: Herton Krzesinski Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/0100016341a72ebe-e572899e-f445-4765-98f0-c314935727f9-000000@email.amazonses.com Signed-off-by: Arnaldo Carvalho de Melo --- .../python/Perf-Trace-Util/lib/Perf/Trace/Core.py | 40 +++++++++------------- 1 file changed, 17 insertions(+), 23 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 38dfb720fb6f..54ace2f6bc36 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 @@ -31,10 +31,8 @@ def flag_str(event_name, field_name, value): string = "" if flag_fields[event_name][field_name]: - print_delim = 0 - keys = flag_fields[event_name][field_name]['values'].keys() - keys.sort() - for idx in keys: + print_delim = 0 + for idx in sorted(flag_fields[event_name][field_name]['values']): if not value and not idx: string += flag_fields[event_name][field_name]['values'][idx] break @@ -51,14 +49,12 @@ def symbol_str(event_name, field_name, value): string = "" if symbolic_fields[event_name][field_name]: - keys = symbolic_fields[event_name][field_name]['values'].keys() - keys.sort() - for idx in keys: + for idx in sorted(symbolic_fields[event_name][field_name]['values']): if not value and not idx: - string = symbolic_fields[event_name][field_name]['values'][idx] + string = symbolic_fields[event_name][field_name]['values'][idx] break - if (value == idx): - string = symbolic_fields[event_name][field_name]['values'][idx] + if (value == idx): + string = symbolic_fields[event_name][field_name]['values'][idx] break return string @@ -74,19 +70,17 @@ def trace_flag_str(value): string = "" print_delim = 0 - keys = trace_flags.keys() - - for idx in keys: - if not value and not idx: - string += "NONE" - break - - if idx and (value & idx) == idx: - if print_delim: - string += " | "; - string += trace_flags[idx] - print_delim = 1 - value &= ~idx + for idx in trace_flags: + if not value and not idx: + string += "NONE" + break + + if idx and (value & idx) == idx: + if print_delim: + string += " | "; + string += trace_flags[idx] + print_delim = 1 + value &= ~idx return string -- cgit v1.2.3 From 2ab89262ff8895b8476b97345507c676fe3081fa Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Tue, 8 May 2018 21:27:45 +0000 Subject: perf scripts python: Add Python 3 support to SchedGui.py Fix a single syntax error in SchedGui.py to support both Python 2 and Python 3. This should have no functional change. Signed-off-by: Jeremy Cline Cc: Alexander Shishkin Cc: Herton Krzesinski Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/0100016341a72d26-75729663-fe55-4309-8c9b-302e065ed2f1-000000@email.amazonses.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (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 index fdd92f699055..cac7b2542ee8 100644 --- 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 @@ -11,7 +11,7 @@ try: import wx except ImportError: - raise ImportError, "You need to install the wxpython lib for this script" + raise ImportError("You need to install the wxpython lib for this script") class RootFrame(wx.Frame): -- cgit v1.2.3 From c45b168effb12719f6dfc8d2c20ba8c057e8c16b Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Tue, 8 May 2018 21:27:46 +0000 Subject: perf scripts python: Add Python 3 support to Util.py Support both Python 2 and Python 3 in Util.py. The dict class no longer has a ``has_key`` method and print is now a function rather than a statement. This should have no functional change. Signed-off-by: Jeremy Cline Cc: Alexander Shishkin Cc: Herton Krzesinski Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/0100016341a730c6-8db8b9b1-da2d-4ee3-96bf-47e0ae9796bd-000000@email.amazonses.com Signed-off-by: Arnaldo Carvalho de Melo --- .../scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 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 f6c84966e4f8..7384dcb628c4 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 @@ -5,6 +5,7 @@ # This software may be distributed under the terms of the GNU General # Public License ("GPL") version 2 as published by the Free Software # Foundation. +from __future__ import print_function import errno, os @@ -33,7 +34,7 @@ def nsecs_str(nsecs): return str def add_stats(dict, key, value): - if not dict.has_key(key): + if key not in dict: dict[key] = (value, value, value, 1) else: min, max, avg, count = dict[key] @@ -72,10 +73,10 @@ try: except: if not audit_package_warned: audit_package_warned = True - print "Install the audit-libs-python package to get syscall names.\n" \ - "For example:\n # apt-get install python-audit (Ubuntu)" \ - "\n # yum install audit-libs-python (Fedora)" \ - "\n etc.\n" + print("Install the audit-libs-python package to get syscall names.\n" + "For example:\n # apt-get install python-audit (Ubuntu)" + "\n # yum install audit-libs-python (Fedora)" + "\n etc.\n") def syscall_name(id): try: -- cgit v1.2.3 From 8c1c1ab2d2a77cb50841822168e56d11c4ebfd6e Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Tue, 8 May 2018 21:27:47 +0000 Subject: perf scripts python: Add Python 3 support to sched-migration.py Support both Python 2 and Python 3 in the sched-migration.py script. This should have no functional change. Signed-off-by: Jeremy Cline Cc: Alexander Shishkin Cc: Herton Krzesinski Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/0100016341a737a5-44ec436f-3440-4cac-a03f-ddfa589bf308-000000@email.amazonses.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/sched-migration.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 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 de66cb3b72c9..3473e7f66081 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -9,13 +9,17 @@ # This software is distributed under the terms of the GNU General # Public License ("GPL") version 2 as published by the Free Software # Foundation. - +from __future__ import print_function import os import sys from collections import defaultdict -from UserList import UserList +try: + from UserList import UserList +except ImportError: + # Python 3: UserList moved to the collections package + from collections import UserList sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') @@ -300,7 +304,7 @@ class TimeSliceList(UserList): if i == -1: return - for i in xrange(i, len(self.data)): + for i in range(i, len(self.data)): timeslice = self.data[i] if timeslice.start > end: return @@ -336,8 +340,8 @@ class SchedEventProxy: 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) + 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 -- cgit v1.2.3 From 12aa6c7389a321b4b5d921f89c3f83b9750598f7 Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Tue, 8 May 2018 21:27:48 +0000 Subject: perf scripts python: Add Python 3 support to EventClass.py Support both Python 2 and Python 3 in EventClass.py. ``print`` is now a function rather than a statement. This should have no functional change. Signed-off-by: Jeremy Cline Cc: Alexander Shishkin Cc: Herton Krzesinski Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/0100016341a73aac-e0734bdc-dcab-4c61-8333-d8be97524aa0-000000@email.amazonses.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py index 81a56cd2b3c1..21a7a1298094 100755 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py @@ -8,6 +8,7 @@ # PerfEvent is the base class for all perf event sample, PebsEvent # is a HW base Intel x86 PEBS event, and user could add more SW/HW # event classes based on requirements. +from __future__ import print_function import struct @@ -44,7 +45,8 @@ class PerfEvent(object): PerfEvent.event_num += 1 def show(self): - print "PMU event: name=%12s, symbol=%24s, comm=%8s, dso=%12s" % (self.name, self.symbol, self.comm, self.dso) + print("PMU event: name=%12s, symbol=%24s, comm=%8s, dso=%12s" % + (self.name, self.symbol, self.comm, self.dso)) # # Basic Intel PEBS (Precise Event-based Sampling) event, whose raw buffer -- cgit v1.2.3 From 25e11700b54c7b6b5ebfc4361981dae12299557b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 11 Sep 2018 14:45:03 +0300 Subject: perf script python: Fix export-to-postgresql.py occasional failure Occasional export failures were found to be caused by truncating 64-bit pointers to 32-bits. Fix by explicitly setting types for all ctype arguments and results. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20180911114504.28516-2-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-postgresql.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index efcaf6cac2eb..e46f51b17513 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py @@ -204,14 +204,23 @@ from ctypes import * libpq = CDLL("libpq.so.5") PQconnectdb = libpq.PQconnectdb PQconnectdb.restype = c_void_p +PQconnectdb.argtypes = [ c_char_p ] PQfinish = libpq.PQfinish +PQfinish.argtypes = [ c_void_p ] PQstatus = libpq.PQstatus +PQstatus.restype = c_int +PQstatus.argtypes = [ c_void_p ] PQexec = libpq.PQexec PQexec.restype = c_void_p +PQexec.argtypes = [ c_void_p, c_char_p ] PQresultStatus = libpq.PQresultStatus +PQresultStatus.restype = c_int +PQresultStatus.argtypes = [ c_void_p ] PQputCopyData = libpq.PQputCopyData +PQputCopyData.restype = c_int PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ] PQputCopyEnd = libpq.PQputCopyEnd +PQputCopyEnd.restype = c_int PQputCopyEnd.argtypes = [ c_void_p, c_void_p ] sys.path.append(os.environ['PERF_EXEC_PATH'] + \ -- cgit v1.2.3 From d005efe18db0b4a123dd92ea8e77e27aee8f99fd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 11 Sep 2018 14:45:04 +0300 Subject: perf script python: Fix export-to-sqlite.py sample columns With the "branches" export option, not all sample columns are exported. However the unwanted columns are not at the end of the tuple, as assumed by the code. Fix by taking the first 15 and last 3 values, instead of the first 18. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20180911114504.28516-3-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-sqlite.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py index f827bf77e9d2..e4bb82c8aba9 100644 --- a/tools/perf/scripts/python/export-to-sqlite.py +++ b/tools/perf/scripts/python/export-to-sqlite.py @@ -440,7 +440,11 @@ def branch_type_table(*x): def sample_table(*x): if branches: - bind_exec(sample_query, 18, x) + for xx in x[0:15]: + sample_query.addBindValue(str(xx)) + for xx in x[19:22]: + sample_query.addBindValue(str(xx)) + do_query_(sample_query) else: bind_exec(sample_query, 22, x) -- cgit v1.2.3 From 3e71c70c946b5d5e7b21397c621b14951e5c0fcf Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:35 +0300 Subject: perf scripts python: call-graph-from-sql.py: Use SPDX license identifier Use SPDX license identifier in call-graph-from-sql.py. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-2-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index b494a67a1c67..ce1b91fcd6b8 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -1,15 +1,7 @@ #!/usr/bin/python2 -# call-graph-from-sql.py: create call-graph from sql database -# Copyright (c) 2014-2017, Intel Corporation. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. -# -# This program is distributed in the hope it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. +# SPDX-License-Identifier: GPL-2.0 +# exported-sql-viewer.py: view data from sql database +# Copyright (c) 2014-2018, Intel Corporation. # To use this script you will need to have exported data using either the # export-to-sqlite.py or the export-to-postgresql.py script. Refer to those -- cgit v1.2.3 From 1d865c06f5715df94528f76d6bb7f6f98975e04e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:36 +0300 Subject: perf scripts python: call-graph-from-sql.py: Provide better default column sizes Set initial column sizes to improve initial display. Committer testing: Extended instructions on testing this, using the sqlite variant: Make sure you have the SQLite glue for python+Qt installed, on fedora 27 I used: # dnf install python-pyside Collect some PT samples, say 5-secs worth, system wide: # perf record -r 10 -e intel_pt//u -a sleep 5 [ perf record: Woken up 49 times to write data ] [ perf record: Captured and wrote 96.131 MB perf.data ] This results in this perf.data file: # ls -larth perf.data -rw-------. 1 root root 97M Oct 23 10:11 perf.data With the following attributes: # perf evlist -v intel_pt//u: type: 8, size: 112, config: 0x300e601, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|IDENTIFIER, read_format: ID, disabled: 1, inherit: 1, exclude_kernel: 1, exclude_hv: 1, sample_id_all: 1 dummy:u: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|IDENTIFIER, read_format: ID, inherit: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, context_switch: 1 # Then generate the "pt_example" tables using: # perf script -s ~/libexec/perf-core/scripts/python/export-to-sqlite.py pt_example branches calls 2018-10-23 10:56:59.177711 Creating database... 2018-10-23 10:56:59.195842 Writing records... instruction trace error type 1 cpu 2 pid 1644 tid 1644 ip 0x263984516750 code 5: Failed to get instruction instruction trace error type 1 cpu 2 pid 1644 tid 1644 ip 0x7f26e116fd20 code 6: Trace doesn't match instruction instruction trace error type 1 cpu 2 pid 1644 tid 1644 ip 0x7f26e162c9ee code 6: Trace doesn't match instruction instruction trace error type 1 cpu 2 pid 1644 tid 1644 ip 0x7f26e9ce831a code 6: Trace doesn't match instruction instruction trace error type 1 cpu 0 pid 1644 tid 1644 ip 0x7f26e13d07b4 code 6: Trace doesn't match instruction Warning: 132 instruction trace errors 2018-10-23 11:25:25.015717 Adding indexes 2018-10-23 11:25:28.788061 Done # In my example, that perf.data file generated this db: # file pt_example pt_example: SQLite 3.x database, last written using SQLite version 3020001 [root@seventh perf]# ls -lah pt_example -rw-r--r--. 1 root root 6.6G Oct 23 11:25 pt_example # Then use this python script to use that db and provide a GUI: $ python tools/perf/scripts/python/call-graph-from-sql.py pt_example branches calls I compared the column widths before this patch and after applying it, the visual results match the patch intent. The following patches will refer to this set of instructions in the "Committer Testing" section. Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-3-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index ce1b91fcd6b8..e1014f2628a7 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -280,6 +280,9 @@ class MainWindow(QMainWindow): self.view = QTreeView() self.view.setModel(self.model) + for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): + self.view.setColumnWidth(c, w) + self.setCentralWidget(self.view) if __name__ == '__main__': -- cgit v1.2.3 From 3c4ef451506897a15aff76ff141c995c6cd32f4d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:37 +0300 Subject: perf scripts python: call-graph-from-sql.py: Set a minimum window size Prevent weirdly small window size. Committer testing: Seems to work, but even before this patch, on my system, it always started with: xwininfo: Window id: 0x1e00002 "Call Graph: pt_example" Width: 800 Height: 600 Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-4-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index e1014f2628a7..68153fa1b4d1 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -274,6 +274,7 @@ class MainWindow(QMainWindow): style = self.style() icon = style.standardIcon(QStyle.SP_MessageBoxInformation) self.setWindowIcon(icon); + self.setMinimumSize(200, 100) self.model = TreeModel(db) -- cgit v1.2.3 From 99a097c987c26c8c82293fcb92908d07009c925e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:38 +0300 Subject: perf scripts python: call-graph-from-sql.py: Change icon There are not many standard icons, but the computer icon looks slightly better than the information icon. Committer testing: Noticed the change on the icon on the gnome menu right next to the "Activities" menu, looks nicer indeed. Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-5-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 68153fa1b4d1..2e33540f3de0 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -271,9 +271,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("Call Graph: " + dbname) self.move(100, 100) self.resize(800, 600) - style = self.style() - icon = style.standardIcon(QStyle.SP_MessageBoxInformation) - self.setWindowIcon(icon); + self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.setMinimumSize(200, 100) self.model = TreeModel(db) -- cgit v1.2.3 From 7e4fc93e2ade2b0c453a97e307203ffe3f930c98 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:39 +0300 Subject: perf scripts python: call-graph-from-sql.py: Make a "Main" function Make a "Main" function so that the variables used do not pollute the global namespace. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 2e33540f3de0..2b74b94eeccc 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -284,7 +284,9 @@ class MainWindow(QMainWindow): self.setCentralWidget(self.view) -if __name__ == '__main__': +# Main + +def Main(): if (len(sys.argv) < 2): print >> sys.stderr, "Usage is: call-graph-from-sql.py " raise Exception("Too few arguments") @@ -331,3 +333,6 @@ if __name__ == '__main__': err = app.exec_() db.close() sys.exit(err) + +if __name__ == "__main__": + Main() -- cgit v1.2.3 From b2556c46a69b4c0e6bbf690ac4ca2913cbe90e1e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:40 +0300 Subject: perf scripts python: call-graph-from-sql.py: Separate the database details into a class Separate the database details into a class that can provide different connections using the same connection information. That paves the way for sub-processes that require their own connection. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-7-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 63 ++++++++++++++---------- 1 file changed, 38 insertions(+), 25 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 2b74b94eeccc..9d056deab2b1 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -284,6 +284,42 @@ class MainWindow(QMainWindow): self.setCentralWidget(self.view) +# Database reference + +class DBRef(): + + def __init__(self, is_sqlite3, dbname): + self.is_sqlite3 = is_sqlite3 + self.dbname = dbname + + def Open(self, connection_name): + dbname = self.dbname + if self.is_sqlite3: + db = QSqlDatabase.addDatabase("QSQLITE", connection_name) + else: + db = QSqlDatabase.addDatabase("QPSQL", connection_name) + opts = dbname.split() + for opt in opts: + if "=" in opt: + opt = opt.split("=") + if opt[0] == "hostname": + db.setHostName(opt[1]) + elif opt[0] == "port": + db.setPort(int(opt[1])) + elif opt[0] == "username": + db.setUserName(opt[1]) + elif opt[0] == "password": + db.setPassword(opt[1]) + elif opt[0] == "dbname": + dbname = opt[1] + else: + dbname = opt + + db.setDatabaseName(dbname) + if not db.open(): + raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) + return db, dbname + # Main def Main(): @@ -302,31 +338,8 @@ def Main(): except: pass - if is_sqlite3: - db = QSqlDatabase.addDatabase('QSQLITE') - else: - db = QSqlDatabase.addDatabase('QPSQL') - opts = dbname.split() - for opt in opts: - if '=' in opt: - opt = opt.split('=') - if opt[0] == 'hostname': - db.setHostName(opt[1]) - elif opt[0] == 'port': - db.setPort(int(opt[1])) - elif opt[0] == 'username': - db.setUserName(opt[1]) - elif opt[0] == 'password': - db.setPassword(opt[1]) - elif opt[0] == 'dbname': - dbname = opt[1] - else: - dbname = opt - - db.setDatabaseName(dbname) - if not db.open(): - raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) - + dbref = DBRef(is_sqlite3, dbname) + db, dbname = dbref.Open("main") app = QApplication(sys.argv) window = MainWindow(db, dbname) window.show() -- cgit v1.2.3 From 5f9dfef1bb7fadfb2d001244ef23359982fedd06 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:41 +0300 Subject: perf scripts python: call-graph-from-sql.py: Add a class for global data Keep global data in a single object that is easy to pass around as needed, without polluting the global namespace. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-8-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 9d056deab2b1..0a4dc13d4818 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -264,17 +264,19 @@ class TreeModel(QAbstractItemModel): class MainWindow(QMainWindow): - def __init__(self, db, dbname, parent=None): + def __init__(self, glb, parent=None): super(MainWindow, self).__init__(parent) + self.glb = glb + self.setObjectName("MainWindow") - self.setWindowTitle("Call Graph: " + dbname) + self.setWindowTitle("Call Graph: " + glb.dbname) self.move(100, 100) self.resize(800, 600) self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.setMinimumSize(200, 100) - self.model = TreeModel(db) + self.model = TreeModel(glb.db) self.view = QTreeView() self.view.setModel(self.model) @@ -284,6 +286,17 @@ class MainWindow(QMainWindow): self.setCentralWidget(self.view) +# Global data + +class Glb(): + + def __init__(self, dbref, db, dbname): + self.dbref = dbref + self.db = db + self.dbname = dbname + self.app = None + self.mainwindow = None + # Database reference class DBRef(): @@ -340,9 +353,12 @@ def Main(): dbref = DBRef(is_sqlite3, dbname) db, dbname = dbref.Open("main") + glb = Glb(dbref, db, dbname) app = QApplication(sys.argv) - window = MainWindow(db, dbname) - window.show() + glb.app = app + mainwindow = MainWindow(glb) + glb.mainwindow = mainwindow + mainwindow.show() err = app.exec_() db.close() sys.exit(err) -- cgit v1.2.3 From e99ef8141a6d97abaf47647cfd0034769144d080 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:42 +0300 Subject: perf scripts python: call-graph-from-sql.py: Remove use of setObjectName() The object name is never used, so don't bother setting it. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-9-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 1 - 1 file changed, 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 0a4dc13d4818..65c18e351bc4 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -269,7 +269,6 @@ class MainWindow(QMainWindow): self.glb = glb - self.setObjectName("MainWindow") self.setWindowTitle("Call Graph: " + glb.dbname) self.move(100, 100) self.resize(800, 600) -- cgit v1.2.3 From 70d831e85c1bdd87d193e85666bf3aa39aab7f21 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:43 +0300 Subject: perf scripts python: call-graph-from-sql.py: Factor out CallGraphModel from TreeModel Factor out CallGraphModel from TreeModel, which paves the way to reuse TreeModel in future reports. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-10-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 90 ++++++++++++++++-------- 1 file changed, 61 insertions(+), 29 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 65c18e351bc4..ada486048ad8 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -201,42 +201,47 @@ class TreeItem(): self.selectCalls() return self.child_count - def columnCount(self): - return 7 - - def columnHeader(self, column): - headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] - return headers[column] + def hasChildren(self): + if not self.query_done: + return True + return self.child_count > 0 def getData(self, column): return self.data[column] +# Tree data model + class TreeModel(QAbstractItemModel): - def __init__(self, db, parent=None): + def __init__(self, root, parent=None): super(TreeModel, self).__init__(parent) - self.db = db - self.root = TreeItem(db, 0, None) + self.root = root + self.last_row_read = 0 - def columnCount(self, parent): - return self.root.columnCount() - - def rowCount(self, parent): + def Item(self, parent): if parent.isValid(): - parent_item = parent.internalPointer() + return parent.internalPointer() else: - parent_item = self.root - return parent_item.childCount() + return self.root + + def rowCount(self, parent): + result = self.Item(parent).childCount() + if result < 0: + result = 0 + self.dataChanged.emit(parent, parent) + return result + + def hasChildren(self, parent): + return self.Item(parent).hasChildren() def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: - if section > 1: - return Qt.AlignRight + return self.columnAlignment(section) if role != Qt.DisplayRole: return None if orientation != Qt.Horizontal: return None - return self.root.columnHeader(section) + return self.columnHeader(section) def parent(self, child): child_item = child.internalPointer() @@ -246,21 +251,48 @@ class TreeModel(QAbstractItemModel): return self.createIndex(parent_item.getRow(), 0, parent_item) def index(self, row, column, parent): - if parent.isValid(): - parent_item = parent.internalPointer() - else: - parent_item = self.root - child_item = parent_item.getChildItem(row) + child_item = self.Item(parent).getChildItem(row) return self.createIndex(row, column, child_item) + def DisplayData(self, item, index): + return item.getData(index.column()) + + def columnAlignment(self, column): + return Qt.AlignLeft + + def columnFont(self, column): + return None + def data(self, index, role): if role == Qt.TextAlignmentRole: - if index.column() > 1: - return Qt.AlignRight + return self.columnAlignment(index.column()) + if role == Qt.FontRole: + return self.columnFont(index.column()) if role != Qt.DisplayRole: return None - index_item = index.internalPointer() - return index_item.getData(index.column()) + item = index.internalPointer() + return self.DisplayData(item, index) + +# Context-sensitive call graph data model + +class CallGraphModel(TreeModel): + + def __init__(self, glb, parent=None): + super(CallGraphModel, self).__init__(TreeItem(glb.db, 0, None), parent) + self.glb = glb + + def columnCount(self, parent=None): + return 7 + + def columnHeader(self, column): + headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] + return headers[column] + + def columnAlignment(self, column): + alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] + return alignment[column] + +# Main window class MainWindow(QMainWindow): @@ -275,7 +307,7 @@ class MainWindow(QMainWindow): self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.setMinimumSize(200, 100) - self.model = TreeModel(glb.db) + self.model = CallGraphModel(glb) self.view = QTreeView() self.view.setModel(self.model) -- cgit v1.2.3 From 4be9ace7e1cdcb44c1fba1fb41ec2b92dda06732 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:44 +0300 Subject: perf scripts python: call-graph-from-sql.py: Add data helper functions Add helper functions for a few common cases. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-11-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 54 +++++++++++++----------- 1 file changed, 29 insertions(+), 25 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index ada486048ad8..7f2eabe7dacd 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -52,6 +52,28 @@ from PySide.QtGui import * from PySide.QtSql import * from decimal import * +# Data formatting helpers + +def dsoname(name): + if name == "[kernel.kallsyms]": + return "[kernel]" + return name + +# Percent to one decimal place + +def PercentToOneDP(n, d): + if not d: + return "0.0" + x = (n * Decimal(100)) / d + return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) + +# Helper for queries that must not fail + +def QueryExec(query, stmt): + ret = query.exec_(stmt) + if not ret: + raise Exception("Query failed: " + query.lastError().text()) + class TreeItem(): def __init__(self, db, row, parent_item): @@ -73,9 +95,7 @@ class TreeItem(): def setUpRoot(self): self.query_done = True query = QSqlQuery(self.db) - ret = query.exec_('SELECT id, comm FROM comms') - if not ret: - raise Exception("Query failed: " + query.lastError().text()) + QueryExec(query, 'SELECT id, comm FROM comms') while query.next(): if not query.value(0): continue @@ -91,9 +111,7 @@ class TreeItem(): self.child_items = [] self.child_count = 0 query = QSqlQuery(self.db) - ret = query.exec_('SELECT thread_id, ( SELECT pid FROM threads WHERE id = thread_id ), ( SELECT tid FROM threads WHERE id = thread_id ) FROM comm_threads WHERE comm_id = ' + str(comm_id)) - if not ret: - raise Exception("Query failed: " + query.lastError().text()) + QueryExec(query, 'SELECT thread_id, ( SELECT pid FROM threads WHERE id = thread_id ), ( SELECT tid FROM threads WHERE id = thread_id ) FROM comm_threads WHERE comm_id = ' + str(comm_id)) while query.next(): child_item = TreeItem(self.db, self.child_count, self) self.child_items.append(child_item) @@ -114,18 +132,6 @@ class TreeItem(): def getRow(self): return self.row - def timePercent(self, b): - if not self.time: - return "0.0" - x = (b * Decimal(100)) / self.time - return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP)) - - def branchPercent(self, b): - if not self.branch_count: - return "0.0" - x = (b * Decimal(100)) / self.branch_count - return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP)) - def addChild(self, call_path_id, name, dso, count, time, branch_count): child_item = TreeItem(self.db, self.child_count, self) child_item.comm_id = self.comm_id @@ -134,14 +140,12 @@ class TreeItem(): child_item.branch_count = branch_count child_item.time = time child_item.data[0] = name - if dso == "[kernel.kallsyms]": - dso = "[kernel]" - child_item.data[1] = dso + child_item.data[1] = dsoname(dso) child_item.data[2] = str(count) child_item.data[3] = str(time) - child_item.data[4] = self.timePercent(time) + child_item.data[4] = PercentToOneDP(time, self.time) child_item.data[5] = str(branch_count) - child_item.data[6] = self.branchPercent(branch_count) + child_item.data[6] = PercentToOneDP(branch_count, self.branch_count) self.child_items.append(child_item) self.child_count += 1 @@ -189,12 +193,12 @@ class TreeItem(): self.branch_count = total_branch_count if self.branch_count: for child_item in self.child_items: - child_item.data[6] = self.branchPercent(child_item.branch_count) + child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) if total_time > self.time: self.time = total_time if self.time: for child_item in self.child_items: - child_item.data[4] = self.timePercent(child_item.time) + child_item.data[4] = PercentToOneDP(child_item.time, self.time) def childCount(self): if not self.query_done: -- cgit v1.2.3 From 341e73cbd3019d350d1271803b45d84af88f2408 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:45 +0300 Subject: perf scripts python: call-graph-from-sql.py: Refactor TreeItem class class TreeItem represents items at all levels of the call-graph tree. However, not all the levels represent the same data i.e. the top-level is comms, the next level is threads, and subsequent levels are functions. Consequently it is simpler to have separate classes for different levels with commonality in a base class. Refactor TreeItem class accordingly. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-12-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 273 +++++++++++------------ 1 file changed, 133 insertions(+), 140 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py index 7f2eabe7dacd..ee1085169a3e 100644 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ b/tools/perf/scripts/python/call-graph-from-sql.py @@ -74,145 +74,6 @@ def QueryExec(query, stmt): if not ret: raise Exception("Query failed: " + query.lastError().text()) -class TreeItem(): - - def __init__(self, db, row, parent_item): - self.db = db - self.row = row - self.parent_item = parent_item - self.query_done = False; - self.child_count = 0 - self.child_items = [] - self.data = ["", "", "", "", "", "", ""] - self.comm_id = 0 - self.thread_id = 0 - self.call_path_id = 1 - self.branch_count = 0 - self.time = 0 - if not parent_item: - self.setUpRoot() - - def setUpRoot(self): - self.query_done = True - query = QSqlQuery(self.db) - QueryExec(query, 'SELECT id, comm FROM comms') - while query.next(): - if not query.value(0): - continue - child_item = TreeItem(self.db, self.child_count, self) - self.child_items.append(child_item) - self.child_count += 1 - child_item.setUpLevel1(query.value(0), query.value(1)) - - def setUpLevel1(self, comm_id, comm): - self.query_done = True; - self.comm_id = comm_id - self.data[0] = comm - self.child_items = [] - self.child_count = 0 - query = QSqlQuery(self.db) - QueryExec(query, 'SELECT thread_id, ( SELECT pid FROM threads WHERE id = thread_id ), ( SELECT tid FROM threads WHERE id = thread_id ) FROM comm_threads WHERE comm_id = ' + str(comm_id)) - while query.next(): - child_item = TreeItem(self.db, self.child_count, self) - self.child_items.append(child_item) - self.child_count += 1 - child_item.setUpLevel2(comm_id, query.value(0), query.value(1), query.value(2)) - - def setUpLevel2(self, comm_id, thread_id, pid, tid): - self.comm_id = comm_id - self.thread_id = thread_id - self.data[0] = str(pid) + ":" + str(tid) - - def getChildItem(self, row): - return self.child_items[row] - - def getParentItem(self): - return self.parent_item - - def getRow(self): - return self.row - - def addChild(self, call_path_id, name, dso, count, time, branch_count): - child_item = TreeItem(self.db, self.child_count, self) - child_item.comm_id = self.comm_id - child_item.thread_id = self.thread_id - child_item.call_path_id = call_path_id - child_item.branch_count = branch_count - child_item.time = time - child_item.data[0] = name - child_item.data[1] = dsoname(dso) - child_item.data[2] = str(count) - child_item.data[3] = str(time) - child_item.data[4] = PercentToOneDP(time, self.time) - child_item.data[5] = str(branch_count) - child_item.data[6] = PercentToOneDP(branch_count, self.branch_count) - self.child_items.append(child_item) - self.child_count += 1 - - def selectCalls(self): - self.query_done = True; - query = QSqlQuery(self.db) - ret = query.exec_('SELECT id, call_path_id, branch_count, call_time, return_time, ' - '( SELECT name FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ), ' - '( SELECT short_name FROM dsos WHERE id = ( SELECT dso_id FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ) ), ' - '( SELECT ip FROM call_paths where id = call_path_id ) ' - 'FROM calls WHERE parent_call_path_id = ' + str(self.call_path_id) + ' AND comm_id = ' + str(self.comm_id) + ' AND thread_id = ' + str(self.thread_id) + - ' ORDER BY call_path_id') - if not ret: - raise Exception("Query failed: " + query.lastError().text()) - last_call_path_id = 0 - name = "" - dso = "" - count = 0 - branch_count = 0 - total_branch_count = 0 - time = 0 - total_time = 0 - while query.next(): - if query.value(1) == last_call_path_id: - count += 1 - branch_count += query.value(2) - time += query.value(4) - query.value(3) - else: - if count: - self.addChild(last_call_path_id, name, dso, count, time, branch_count) - last_call_path_id = query.value(1) - name = query.value(5) - dso = query.value(6) - count = 1 - total_branch_count += branch_count - total_time += time - branch_count = query.value(2) - time = query.value(4) - query.value(3) - if count: - self.addChild(last_call_path_id, name, dso, count, time, branch_count) - total_branch_count += branch_count - total_time += time - # Top level does not have time or branch count, so fix that here - if total_branch_count > self.branch_count: - self.branch_count = total_branch_count - if self.branch_count: - for child_item in self.child_items: - child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) - if total_time > self.time: - self.time = total_time - if self.time: - for child_item in self.child_items: - child_item.data[4] = PercentToOneDP(child_item.time, self.time) - - def childCount(self): - if not self.query_done: - self.selectCalls() - return self.child_count - - def hasChildren(self): - if not self.query_done: - return True - return self.child_count > 0 - - def getData(self, column): - return self.data[column] - # Tree data model class TreeModel(QAbstractItemModel): @@ -277,12 +138,144 @@ class TreeModel(QAbstractItemModel): item = index.internalPointer() return self.DisplayData(item, index) +# Context-sensitive call graph data model item base + +class CallGraphLevelItemBase(object): + + def __init__(self, glb, row, parent_item): + self.glb = glb + self.row = row + self.parent_item = parent_item + self.query_done = False; + self.child_count = 0 + self.child_items = [] + + def getChildItem(self, row): + return self.child_items[row] + + def getParentItem(self): + return self.parent_item + + def getRow(self): + return self.row + + def childCount(self): + if not self.query_done: + self.Select() + if not self.child_count: + return -1 + return self.child_count + + def hasChildren(self): + if not self.query_done: + return True + return self.child_count > 0 + + def getData(self, column): + return self.data[column] + +# Context-sensitive call graph data model level 2+ item base + +class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): + + def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): + super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) + self.comm_id = comm_id + self.thread_id = thread_id + self.call_path_id = call_path_id + self.branch_count = branch_count + self.time = time + + def Select(self): + self.query_done = True; + query = QSqlQuery(self.glb.db) + QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" + " FROM calls" + " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" + " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" + " INNER JOIN dsos ON symbols.dso_id = dsos.id" + " WHERE parent_call_path_id = " + str(self.call_path_id) + + " AND comm_id = " + str(self.comm_id) + + " AND thread_id = " + str(self.thread_id) + + " GROUP BY call_path_id, name, short_name" + " ORDER BY call_path_id") + while query.next(): + child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) + self.child_items.append(child_item) + self.child_count += 1 + +# Context-sensitive call graph data model level three item + +class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): + + def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): + super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) + dso = dsoname(dso) + self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] + self.dbid = call_path_id + +# Context-sensitive call graph data model level two item + +class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): + + def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): + super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) + self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] + self.dbid = thread_id + + def Select(self): + super(CallGraphLevelTwoItem, self).Select() + for child_item in self.child_items: + self.time += child_item.time + self.branch_count += child_item.branch_count + for child_item in self.child_items: + child_item.data[4] = PercentToOneDP(child_item.time, self.time) + child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) + +# Context-sensitive call graph data model level one item + +class CallGraphLevelOneItem(CallGraphLevelItemBase): + + def __init__(self, glb, row, comm_id, comm, parent_item): + super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) + self.data = [comm, "", "", "", "", "", ""] + self.dbid = comm_id + + def Select(self): + self.query_done = True; + query = QSqlQuery(self.glb.db) + QueryExec(query, "SELECT thread_id, pid, tid" + " FROM comm_threads" + " INNER JOIN threads ON thread_id = threads.id" + " WHERE comm_id = " + str(self.dbid)) + while query.next(): + child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) + self.child_items.append(child_item) + self.child_count += 1 + +# Context-sensitive call graph data model root item + +class CallGraphRootItem(CallGraphLevelItemBase): + + def __init__(self, glb): + super(CallGraphRootItem, self).__init__(glb, 0, None) + self.dbid = 0 + self.query_done = True; + query = QSqlQuery(glb.db) + QueryExec(query, "SELECT id, comm FROM comms") + while query.next(): + if not query.value(0): + continue + child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) + self.child_items.append(child_item) + self.child_count += 1 + # Context-sensitive call graph data model class CallGraphModel(TreeModel): def __init__(self, glb, parent=None): - super(CallGraphModel, self).__init__(TreeItem(glb.db, 0, None), parent) + super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) self.glb = glb def columnCount(self, parent=None): -- cgit v1.2.3 From 031c2a004ba75a4f8f2a6d0a7ca6f2fe5912de22 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 1 Oct 2018 09:28:46 +0300 Subject: perf scripts python: call-graph-from-sql.py: Rename to exported-sql-viewer.py Additional reports will be added to the script so rename to reflect the more general purpose. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20181001062853.28285-13-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/call-graph-from-sql.py | 395 ---------------------- tools/perf/scripts/python/export-to-postgresql.py | 2 +- tools/perf/scripts/python/export-to-sqlite.py | 2 +- tools/perf/scripts/python/exported-sql-viewer.py | 395 ++++++++++++++++++++++ 4 files changed, 397 insertions(+), 397 deletions(-) delete mode 100644 tools/perf/scripts/python/call-graph-from-sql.py create mode 100755 tools/perf/scripts/python/exported-sql-viewer.py (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py deleted file mode 100644 index ee1085169a3e..000000000000 --- a/tools/perf/scripts/python/call-graph-from-sql.py +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/python2 -# SPDX-License-Identifier: GPL-2.0 -# exported-sql-viewer.py: view data from sql database -# Copyright (c) 2014-2018, Intel Corporation. - -# To use this script you will need to have exported data using either the -# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those -# scripts for details. -# -# Following on from the example in the export scripts, a -# call-graph can be displayed for the pt_example database like this: -# -# python tools/perf/scripts/python/call-graph-from-sql.py pt_example -# -# Note that for PostgreSQL, this script supports connecting to remote databases -# by setting hostname, port, username, password, and dbname e.g. -# -# python tools/perf/scripts/python/call-graph-from-sql.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" -# -# The result is a GUI window with a tree representing a context-sensitive -# call-graph. Expanding a couple of levels of the tree and adjusting column -# widths to suit will display something like: -# -# Call Graph: pt_example -# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) -# v- ls -# v- 2638:2638 -# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 -# |- unknown unknown 1 13198 0.1 1 0.0 -# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 -# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 -# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 -# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 -# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 -# >- __libc_csu_init ls 1 10354 0.1 10 0.0 -# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 -# v- main ls 1 8182043 99.6 180254 99.9 -# -# Points to note: -# The top level is a command name (comm) -# The next level is a thread (pid:tid) -# Subsequent levels are functions -# 'Count' is the number of calls -# 'Time' is t