From f1cd52d8912f124cc513a00143fe3baeb44772d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 11 Jan 2018 11:27:11 +0300 Subject: scripts/qemu-gdb: add simple tcg lock status helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a simple helper to dump lock state. Signed-off-by: Alex Bennée Signed-off-by: Paolo Bonzini --- scripts/qemu-gdb.py | 3 ++- scripts/qemugdb/tcg.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 scripts/qemugdb/tcg.py (limited to 'scripts') diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index b3f8e04f77..d58213e549 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -26,7 +26,7 @@ import os, sys sys.path.append(os.path.dirname(__file__)) -from qemugdb import aio, mtree, coroutine +from qemugdb import aio, mtree, coroutine, tcg class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' @@ -38,6 +38,7 @@ QemuCommand() coroutine.CoroutineCommand() mtree.MtreeCommand() aio.HandlersCommand() +tcg.TCGLockStatusCommand() coroutine.CoroutineSPFunction() coroutine.CoroutinePCFunction() diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py new file mode 100644 index 0000000000..8c7f1d7454 --- /dev/null +++ b/scripts/qemugdb/tcg.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# GDB debugging support, TCG status +# +# Copyright 2016 Linaro Ltd +# +# Authors: +# Alex Bennée +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +# 'qemu tcg-lock-status' -- display the TCG lock status across threads + +import gdb + +class TCGLockStatusCommand(gdb.Command): + '''Display TCG Execution Status''' + def __init__(self): + gdb.Command.__init__(self, 'qemu tcg-lock-status', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + gdb.write("Thread, BQL (iothread_mutex), Replay, Blocked?\n") + for thread in gdb.inferiors()[0].threads(): + thread.switch() + + iothread = gdb.parse_and_eval("iothread_locked") + replay = gdb.parse_and_eval("replay_locked") + + frame = gdb.selected_frame() + if frame.name() == "__lll_lock_wait": + frame.older().select() + mutex = gdb.parse_and_eval("mutex") + owner = gdb.parse_and_eval("mutex->__data.__owner") + blocked = ("__lll_lock_wait waiting on %s from %d" % + (mutex, owner)) + else: + blocked = "not blocked" + + gdb.write("%d/%d, %s, %s, %s\n" % (thread.num, thread.ptid[1], + iothread, replay, blocked)) -- cgit v1.2.3 From c24999fa53e581335413c33f68748c1fc7b3d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 11 Jan 2018 11:27:33 +0300 Subject: scripts/qemu-gdb/timers.py: new helper to dump timer state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces the qemu-gdb command "qemu timers" which will dump the state of the main timers in the system. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- scripts/qemu-gdb.py | 3 ++- scripts/qemugdb/timers.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 scripts/qemugdb/timers.py (limited to 'scripts') diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index d58213e549..690827e6fc 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -26,7 +26,7 @@ import os, sys sys.path.append(os.path.dirname(__file__)) -from qemugdb import aio, mtree, coroutine, tcg +from qemugdb import aio, mtree, coroutine, tcg, timers class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' @@ -39,6 +39,7 @@ coroutine.CoroutineCommand() mtree.MtreeCommand() aio.HandlersCommand() tcg.TCGLockStatusCommand() +timers.TimersCommand() coroutine.CoroutineSPFunction() coroutine.CoroutinePCFunction() diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py new file mode 100644 index 0000000000..be71a001e3 --- /dev/null +++ b/scripts/qemugdb/timers.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# GDB debugging support +# +# Copyright 2017 Linaro Ltd +# +# Author: Alex Bennée +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. + +# 'qemu timers' -- display the current timerlists + +import gdb + +class TimersCommand(gdb.Command): + '''Display the current QEMU timers''' + + def __init__(self): + 'Register the class as a gdb command' + gdb.Command.__init__(self, 'qemu timers', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def dump_timers(self, timer): + "Follow a timer and recursively dump each one in the list." + # timer should be of type QemuTimer + gdb.write(" timer %s/%s (cb:%s,opq:%s)\n" % ( + timer['expire_time'], + timer['scale'], + timer['cb'], + timer['opaque'])) + + if int(timer['next']) > 0: + self.dump_timers(timer['next']) + + + def process_timerlist(self, tlist, ttype): + gdb.write("Processing %s timers\n" % (ttype)) + gdb.write(" clock %s is enabled:%s, last:%s\n" % ( + tlist['clock']['type'], + tlist['clock']['enabled'], + tlist['clock']['last'])) + if int(tlist['active_timers']) > 0: + self.dump_timers(tlist['active_timers']) + + + def invoke(self, arg, from_tty): + 'Run the command' + main_timers = gdb.parse_and_eval("main_loop_tlg") + + # This will break if QEMUClockType in timer.h is redfined + self.process_timerlist(main_timers['tl'][0], "Realtime") + self.process_timerlist(main_timers['tl'][1], "Virtual") + self.process_timerlist(main_timers['tl'][2], "Host") + self.process_timerlist(main_timers['tl'][3], "Virtual RT") -- cgit v1.2.3 From f4bdc13e492208f4f9cad0ff1c14247dea1cd197 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 1 Dec 2017 17:24:33 -0600 Subject: checkpatch: Enforce proper do/while (0) style Use of a loop construct for code that is not intended to repeat does not make much idiomatic sense, except in one place: it is a common usage in macros in order to wrap arbitrary code with single-statement semantics. But when used in a macro, it is more typical for the caller to supply the trailing ';' when calling the macro. Although qemu coding style frowns on bare: if (cond) statement1; else statement2; where extra semicolons actually cause syntax errors, we still want our macro styles to be easily copied to other projects. Thus, declare it an error if we encounter any form of 'while (0)' with a semicolon in the same line. Signed-off-by: Eric Blake Message-Id: <20171201232433.25193-8-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- scripts/checkpatch.pl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3dc27d9656..accba24a31 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1622,6 +1622,11 @@ sub process { } } +# 'do ... while (0/false)' only makes sense in macros, without trailing ';' + if ($line =~ /while\s*\((0|false)\);/) { + ERROR("suspicious ; after while (0)\n" . $herecurr); + } + # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { my ($s, $c) = ($stat, $cond); -- cgit v1.2.3 From b5976c2e46e86b36b01d8ac380a182e22209a7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 11 Jan 2018 11:27:22 +0300 Subject: scripts/analyse-locks-simpletrace.py: script to analyse lock times MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This script allows analysis of mutex acquisition and hold times based on a trace file. Given a trace control file of: qemu_mutex_lock qemu_mutex_locked qemu_mutex_unlock And running with: $QEMU $QEMU_ARGS -trace events=./lock-trace You can analyse the results with: ./scripts/analyse-locks-simpletrace.py trace-events-all ./trace-21812 Signed-off-by: Alex Bennée Signed-off-by: Paolo Bonzini --- scripts/analyse-locks-simpletrace.py | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100755 scripts/analyse-locks-simpletrace.py (limited to 'scripts') diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py new file mode 100755 index 0000000000..101e84dea5 --- /dev/null +++ b/scripts/analyse-locks-simpletrace.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Analyse lock events and compute statistics +# +# Author: Alex Bennée +# + +import os +import simpletrace +import argparse +import numpy as np + +class MutexAnalyser(simpletrace.Analyzer): + "A simpletrace Analyser for checking locks." + + def __init__(self): + self.locks = 0 + self.locked = 0 + self.unlocks = 0 + self.mutex_records = {} + + def _get_mutex(self, mutex): + if not mutex in self.mutex_records: + self.mutex_records[mutex] = {"locks": 0, + "lock_time": 0, + "acquire_times": [], + "locked": 0, + "locked_time": 0, + "held_times": [], + "unlocked": 0} + + return self.mutex_records[mutex] + + def qemu_mutex_lock(self, timestamp, mutex, filename, line): + self.locks += 1 + rec = self._get_mutex(mutex) + rec["locks"] += 1 + rec["lock_time"] = timestamp[0] + rec["lock_loc"] = (filename, line) + + def qemu_mutex_locked(self, timestamp, mutex, filename, line): + self.locked += 1 + rec = self._get_mutex(mutex) + rec["locked"] += 1 + rec["locked_time"] = timestamp[0] + acquire_time = rec["locked_time"] - rec["lock_time"] + rec["locked_loc"] = (filename, line) + rec["acquire_times"].append(acquire_time) + + def qemu_mutex_unlock(self, timestamp, mutex, filename, line): + self.unlocks += 1 + rec = self._get_mutex(mutex) + rec["unlocked"] += 1 + held_time = timestamp[0] - rec["locked_time"] + rec["held_times"].append(held_time) + rec["unlock_loc"] = (filename, line) + + +def get_args(): + "Grab options" + parser = argparse.ArgumentParser() + parser.add_argument("--output", "-o", type=str, help="Render plot to file") + parser.add_argument("events", type=str, help='trace file read from') + parser.add_argument("tracefile", type=str, help='trace file read from') + return parser.parse_args() + +if __name__ == '__main__': + args = get_args() + + # Gather data from the trace + analyser = MutexAnalyser() + simpletrace.process(args.events, args.tracefile, analyser) + + print ("Total locks: %d, locked: %d, unlocked: %d" % + (analyser.locks, analyser.locked, analyser.unlocks)) + + # Now dump the individual lock stats + for key, val in sorted(analyser.mutex_records.iteritems(), + key=lambda (k,v): v["locks"]): + print ("Lock: %#x locks: %d, locked: %d, unlocked: %d" % + (key, val["locks"], val["locked"], val["unlocked"])) + + acquire_times = np.array(val["acquire_times"]) + if len(acquire_times) > 0: + print (" Acquire Time: min:%d median:%d avg:%.2f max:%d" % + (acquire_times.min(), np.median(acquire_times), + acquire_times.mean(), acquire_times.max())) + + held_times = np.array(val["held_times"]) + if len(held_times) > 0: + print (" Held Time: min:%d median:%d avg:%.2f max:%d" % + (held_times.min(), np.median(held_times), + held_times.mean(), held_times.max())) + + # Check if any locks still held + if val["locks"] > val["locked"]: + print (" LOCK HELD (%s:%s)" % (val["locked_loc"])) + print (" BLOCKED (%s:%s)" % (val["lock_loc"])) -- cgit v1.2.3