diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/create_config | 7 | ||||
-rw-r--r-- | scripts/dump-guest-memory.py | 339 | ||||
-rwxr-xr-x | scripts/qmp/qmp-shell | 3 | ||||
-rwxr-xr-x | scripts/switch-timer-api | 2 | ||||
-rw-r--r-- | scripts/tracetool/backend/simple.py | 6 | ||||
-rw-r--r-- | scripts/tracetool/backend/ust.py | 101 | ||||
-rw-r--r-- | scripts/tracetool/format/ust_events_c.py | 30 | ||||
-rw-r--r-- | scripts/tracetool/format/ust_events_h.py | 57 |
8 files changed, 484 insertions, 61 deletions
diff --git a/scripts/create_config b/scripts/create_config index b1adbf5897..546f889144 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -26,6 +26,10 @@ case $line in # save for the next definitions prefix=${line#*=} ;; + IASL=*) # iasl executable + value=${line#*=} + echo "#define CONFIG_IASL $value" + ;; CONFIG_AUDIO_DRIVERS=*) drivers=${line#*=} echo "#define CONFIG_AUDIO_DRIVERS \\" @@ -104,6 +108,9 @@ case $line in value=${line#*=} echo "#define $name $value" ;; + DSOSUF=*) + echo "#define HOST_DSOSUF \"${line#*=}\"" + ;; esac done # read diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py new file mode 100644 index 0000000000..1ed8b67883 --- /dev/null +++ b/scripts/dump-guest-memory.py @@ -0,0 +1,339 @@ +# This python script adds a new gdb command, "dump-guest-memory". It +# should be loaded with "source dump-guest-memory.py" at the (gdb) +# prompt. +# +# Copyright (C) 2013, Red Hat, Inc. +# +# Authors: +# Laszlo Ersek <lersek@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. See +# the COPYING file in the top-level directory. +# +# The leading docstring doesn't have idiomatic Python formatting. It is +# printed by gdb's "help" command (the first line is printed in the +# "help data" summary), and it should match how other help texts look in +# gdb. + +import struct + +class DumpGuestMemory(gdb.Command): + """Extract guest vmcore from qemu process coredump. + +The sole argument is FILE, identifying the target file to write the +guest vmcore to. + +This GDB command reimplements the dump-guest-memory QMP command in +python, using the representation of guest memory as captured in the qemu +coredump. The qemu process that has been dumped must have had the +command line option "-machine dump-guest-core=on". + +For simplicity, the "paging", "begin" and "end" parameters of the QMP +command are not supported -- no attempt is made to get the guest's +internal paging structures (ie. paging=false is hard-wired), and guest +memory is always fully dumped. + +Only x86_64 guests are supported. + +The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are +not written to the vmcore. Preparing these would require context that is +only present in the KVM host kernel module when the guest is alive. A +fake ELF note is written instead, only to keep the ELF parser of "crash" +happy. + +Dependent on how busted the qemu process was at the time of the +coredump, this command might produce unpredictable results. If qemu +deliberately called abort(), or it was dumped in response to a signal at +a halfway fortunate point, then its coredump should be in reasonable +shape and this command should mostly work.""" + + TARGET_PAGE_SIZE = 0x1000 + TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 + + # Various ELF constants + EM_X86_64 = 62 # AMD x86-64 target machine + ELFDATA2LSB = 1 # little endian + ELFCLASS64 = 2 + ELFMAG = "\x7FELF" + EV_CURRENT = 1 + ET_CORE = 4 + PT_LOAD = 1 + PT_NOTE = 4 + + # Special value for e_phnum. This indicates that the real number of + # program headers is too large to fit into e_phnum. Instead the real + # value is in the field sh_info of section 0. + PN_XNUM = 0xFFFF + + # Format strings for packing and header size calculation. + ELF64_EHDR = ("4s" # e_ident/magic + "B" # e_ident/class + "B" # e_ident/data + "B" # e_ident/version + "B" # e_ident/osabi + "8s" # e_ident/pad + "H" # e_type + "H" # e_machine + "I" # e_version + "Q" # e_entry + "Q" # e_phoff + "Q" # e_shoff + "I" # e_flags + "H" # e_ehsize + "H" # e_phentsize + "H" # e_phnum + "H" # e_shentsize + "H" # e_shnum + "H" # e_shstrndx + ) + ELF64_PHDR = ("I" # p_type + "I" # p_flags + "Q" # p_offset + "Q" # p_vaddr + "Q" # p_paddr + "Q" # p_filesz + "Q" # p_memsz + "Q" # p_align + ) + + def __init__(self): + super(DumpGuestMemory, self).__init__("dump-guest-memory", + gdb.COMMAND_DATA, + gdb.COMPLETE_FILENAME) + self.uintptr_t = gdb.lookup_type("uintptr_t") + self.elf64_ehdr_le = struct.Struct("<%s" % self.ELF64_EHDR) + self.elf64_phdr_le = struct.Struct("<%s" % self.ELF64_PHDR) + + def int128_get64(self, val): + assert (val["hi"] == 0) + return val["lo"] + + def qtailq_foreach(self, head, field_str): + var_p = head["tqh_first"] + while (var_p != 0): + var = var_p.dereference() + yield var + var_p = var[field_str]["tqe_next"] + + def qemu_get_ram_block(self, ram_addr): + ram_blocks = gdb.parse_and_eval("ram_list.blocks") + for block in self.qtailq_foreach(ram_blocks, "next"): + if (ram_addr - block["offset"] < block["length"]): + return block + raise gdb.GdbError("Bad ram offset %x" % ram_addr) + + def qemu_get_ram_ptr(self, ram_addr): + block = self.qemu_get_ram_block(ram_addr) + return block["host"] + (ram_addr - block["offset"]) + + def memory_region_get_ram_ptr(self, mr): + if (mr["alias"] != 0): + return (self.memory_region_get_ram_ptr(mr["alias"].dereference()) + + mr["alias_offset"]) + return self.qemu_get_ram_ptr(mr["ram_addr"] & self.TARGET_PAGE_MASK) + + def guest_phys_blocks_init(self): + self.guest_phys_blocks = [] + + def guest_phys_blocks_append(self): + print "guest RAM blocks:" + print ("target_start target_end host_addr message " + "count") + print ("---------------- ---------------- ---------------- ------- " + "-----") + + current_map_p = gdb.parse_and_eval("address_space_memory.current_map") + current_map = current_map_p.dereference() + for cur in range(current_map["nr"]): + flat_range = (current_map["ranges"] + cur).dereference() + mr = flat_range["mr"].dereference() + + # we only care about RAM + if (not mr["ram"]): + continue + + section_size = self.int128_get64(flat_range["addr"]["size"]) + target_start = self.int128_get64(flat_range["addr"]["start"]) + target_end = target_start + section_size + host_addr = (self.memory_region_get_ram_ptr(mr) + + flat_range["offset_in_region"]) + predecessor = None + + # find continuity in guest physical address space + if (len(self.guest_phys_blocks) > 0): + predecessor = self.guest_phys_blocks[-1] + predecessor_size = (predecessor["target_end"] - + predecessor["target_start"]) + + # the memory API guarantees monotonically increasing + # traversal + assert (predecessor["target_end"] <= target_start) + + # we want continuity in both guest-physical and + # host-virtual memory + if (predecessor["target_end"] < target_start or + predecessor["host_addr"] + predecessor_size != host_addr): + predecessor = None + + if (predecessor is None): + # isolated mapping, add it to the list + self.guest_phys_blocks.append({"target_start": target_start, + "target_end" : target_end, + "host_addr" : host_addr}) + message = "added" + else: + # expand predecessor until @target_end; predecessor's + # start doesn't change + predecessor["target_end"] = target_end + message = "joined" + + print ("%016x %016x %016x %-7s %5u" % + (target_start, target_end, host_addr.cast(self.uintptr_t), + message, len(self.guest_phys_blocks))) + + def cpu_get_dump_info(self): + # We can't synchronize the registers with KVM post-mortem, and + # the bits in (first_x86_cpu->env.hflags) seem to be stale; they + # may not reflect long mode for example. Hence just assume the + # most common values. This also means that instruction pointer + # etc. will be bogus in the dump, but at least the RAM contents + # should be valid. + self.dump_info = {"d_machine": self.EM_X86_64, + "d_endian" : self.ELFDATA2LSB, + "d_class" : self.ELFCLASS64} + + def encode_elf64_ehdr_le(self): + return self.elf64_ehdr_le.pack( + self.ELFMAG, # e_ident/magic + self.dump_info["d_class"], # e_ident/class + self.dump_info["d_endian"], # e_ident/data + self.EV_CURRENT, # e_ident/version + 0, # e_ident/osabi + "", # e_ident/pad + self.ET_CORE, # e_type + self.dump_info["d_machine"], # e_machine + self.EV_CURRENT, # e_version + 0, # e_entry + self.elf64_ehdr_le.size, # e_phoff + 0, # e_shoff + 0, # e_flags + self.elf64_ehdr_le.size, # e_ehsize + self.elf64_phdr_le.size, # e_phentsize + self.phdr_num, # e_phnum + 0, # e_shentsize + 0, # e_shnum + 0 # e_shstrndx + ) + + def encode_elf64_note_le(self): + return self.elf64_phdr_le.pack(self.PT_NOTE, # p_type + 0, # p_flags + (self.memory_offset - + len(self.note)), # p_offset + 0, # p_vaddr + 0, # p_paddr + len(self.note), # p_filesz + len(self.note), # p_memsz + 0 # p_align + ) + + def encode_elf64_load_le(self, offset, start_hwaddr, range_size): + return self.elf64_phdr_le.pack(self.PT_LOAD, # p_type + 0, # p_flags + offset, # p_offset + 0, # p_vaddr + start_hwaddr, # p_paddr + range_size, # p_filesz + range_size, # p_memsz + 0 # p_align + ) + + def note_init(self, name, desc, type): + # name must include a trailing NUL + namesz = (len(name) + 1 + 3) / 4 * 4 + descsz = (len(desc) + 3) / 4 * 4 + fmt = ("<" # little endian + "I" # n_namesz + "I" # n_descsz + "I" # n_type + "%us" # name + "%us" # desc + % (namesz, descsz)) + self.note = struct.pack(fmt, + len(name) + 1, len(desc), type, name, desc) + + def dump_init(self): + self.guest_phys_blocks_init() + self.guest_phys_blocks_append() + self.cpu_get_dump_info() + # we have no way to retrieve the VCPU status from KVM + # post-mortem + self.note_init("NONE", "EMPTY", 0) + + # Account for PT_NOTE. + self.phdr_num = 1 + + # We should never reach PN_XNUM for paging=false dumps: there's + # just a handful of discontiguous ranges after merging. + self.phdr_num += len(self.guest_phys_blocks) + assert (self.phdr_num < self.PN_XNUM) + + # Calculate the ELF file offset where the memory dump commences: + # + # ELF header + # PT_NOTE + # PT_LOAD: 1 + # PT_LOAD: 2 + # ... + # PT_LOAD: len(self.guest_phys_blocks) + # ELF note + # memory dump + self.memory_offset = (self.elf64_ehdr_le.size + + self.elf64_phdr_le.size * self.phdr_num + + len(self.note)) + + def dump_begin(self, vmcore): + vmcore.write(self.encode_elf64_ehdr_le()) + vmcore.write(self.encode_elf64_note_le()) + running = self.memory_offset + for block in self.guest_phys_blocks: + range_size = block["target_end"] - block["target_start"] + vmcore.write(self.encode_elf64_load_le(running, + block["target_start"], + range_size)) + running += range_size + vmcore.write(self.note) + + def dump_iterate(self, vmcore): + qemu_core = gdb.inferiors()[0] + for block in self.guest_phys_blocks: + cur = block["host_addr"] + left = block["target_end"] - block["target_start"] + print ("dumping range at %016x for length %016x" % + (cur.cast(self.uintptr_t), left)) + while (left > 0): + chunk_size = min(self.TARGET_PAGE_SIZE, left) + chunk = qemu_core.read_memory(cur, chunk_size) + vmcore.write(chunk) + cur += chunk_size + left -= chunk_size + + def create_vmcore(self, filename): + vmcore = open(filename, "wb") + self.dump_begin(vmcore) + self.dump_iterate(vmcore) + vmcore.close() + + def invoke(self, args, from_tty): + # Unwittingly pressing the Enter key after the command should + # not dump the same multi-gig coredump to the same file. + self.dont_repeat() + + argv = gdb.string_to_argv(args) + if (len(argv) != 1): + raise gdb.GdbError("usage: dump-guest-memory FILE") + + self.dump_init() + self.create_vmcore(argv[0]) + +DumpGuestMemory() diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index d6b420f18a..d374b357aa 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -31,6 +31,7 @@ # (QEMU) import qmp +import json import readline import sys import pprint @@ -107,6 +108,8 @@ class QMPShell(qmp.QEMUMonitorProtocol): value = True elif opt[1] == 'false': value = False + elif opt[1].startswith('{'): + value = json.loads(opt[1]) else: value = opt[1] qmpcmd['arguments'][opt[0]] = value diff --git a/scripts/switch-timer-api b/scripts/switch-timer-api index a369a083d1..b0e230b9f1 100755 --- a/scripts/switch-timer-api +++ b/scripts/switch-timer-api @@ -20,7 +20,7 @@ sub Syntax print STDERR <<STOP; Usage: $FindBin::Script [options] FILE ... -Translate each FILE to the new Qemu timer API. If no files +Translate each FILE to the new QEMU timer API. If no files are passed, a reasonable guess is taken. Options: diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 37ef599324..3dde372e46 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -56,7 +56,7 @@ def c(events): out('', - ' TraceEvent *eventp = trace_event_id(%(event_id)s);', + ' TraceEvent *eventp = trace_event_id(%(event_enum)s);', ' bool _state = trace_event_get_state_dynamic(eventp);', ' if (!_state) {', ' return;', @@ -65,6 +65,7 @@ def c(events): ' if (trace_record_start(&rec, %(event_id)s, %(size_str)s)) {', ' return; /* Trace Buffer Full, Event Dropped ! */', ' }', + event_enum = 'TRACE_' + event.name.upper(), event_id = num, size_str = sizestr, ) @@ -93,9 +94,6 @@ def c(events): def h(events): - out('#include "trace/simple.h"', - '') - for event in events: out('void trace_%(name)s(%(args)s);', name = event.name, diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py index ea36995092..41c1c75b7c 100644 --- a/scripts/tracetool/backend/ust.py +++ b/scripts/tracetool/backend/ust.py @@ -18,76 +18,65 @@ from tracetool import out PUBLIC = True - def c(events): - out('#include <ust/marker.h>', - '#undef mutex_lock', - '#undef mutex_unlock', - '#undef inline', - '#undef wmb', - '#include "trace.h"') + pass + +def h(events): + out('#include <lttng/tracepoint.h>', + '#include "trace/generated-ust-provider.h"', + '') for e in events: argnames = ", ".join(e.args.names()) if len(e.args) > 0: - argnames = ', ' + argnames - - out('DEFINE_TRACE(ust_%(name)s);', - '', - 'static void ust_%(name)s_probe(%(args)s)', - '{', - ' trace_mark(ust, %(name)s, %(fmt)s%(argnames)s);', - '}', - name = e.name, - args = e.args, - fmt = e.fmt, - argnames = argnames, - ) - - else: - out('DEFINE_TRACE(ust_%(name)s);', - '', - 'static void ust_%(name)s_probe(%(args)s)', - '{', - ' trace_mark(ust, %(name)s, UST_MARKER_NOARGS);', - '}', - name = e.name, - args = e.args, - ) - - # register probes - out('', - 'static void __attribute__((constructor)) trace_init(void)', - '{') + argnames = ", " + argnames - for e in events: - out(' register_trace_ust_%(name)s(ust_%(name)s_probe);', + out('static inline void trace_%(name)s(%(args)s)', + '{', + ' tracepoint(qemu, %(name)s%(tp_args)s);', + '}', + '', name = e.name, + args = e.args, + tp_args = argnames, ) - out('}') - - -def h(events): - out('#include <ust/tracepoint.h>', - '#undef mutex_lock', - '#undef mutex_unlock', - '#undef inline', - '#undef wmb') +def ust_events_c(events): + pass +def ust_events_h(events): for e in events: if len(e.args) > 0: - out('DECLARE_TRACE(ust_%(name)s, TP_PROTO(%(args)s), TP_ARGS(%(argnames)s));', - '#define trace_%(name)s trace_ust_%(name)s', + out('TRACEPOINT_EVENT(', + ' qemu,', + ' %(name)s,', + ' TP_ARGS(%(args)s),', + ' TP_FIELDS(', name = e.name, - args = e.args, - argnames = ", ".join(e.args.names()), + args = ", ".join(", ".join(i) for i in e.args), ) + for t,n in e.args: + if ('int' in t) or ('long' in t) or ('unsigned' in t) or ('size_t' in t): + out(' ctf_integer(' + t + ', ' + n + ', ' + n + ')') + elif ('double' in t) or ('float' in t): + out(' ctf_float(' + t + ', ' + n + ', ' + n + ')') + elif ('char *' in t) or ('char*' in t): + out(' ctf_string(' + n + ', ' + n + ')') + elif ('void *' in t) or ('void*' in t): + out(' ctf_integer_hex(unsigned long, ' + n + ', ' + n + ')') + + out(' )', + ')', + '') + else: - out('_DECLARE_TRACEPOINT_NOARGS(ust_%(name)s);', - '#define trace_%(name)s trace_ust_%(name)s', + out('TRACEPOINT_EVENT(', + ' qemu,', + ' %(name)s,', + ' TP_ARGS(void),', + ' TP_FIELDS()', + ')', + '', name = e.name, - ) - - out() + )
\ No newline at end of file diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py new file mode 100644 index 0000000000..116e713225 --- /dev/null +++ b/scripts/tracetool/format/ust_events_c.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .c for LTTng ust event description. +""" + +__author__ = "Mohamad Gebai <mohamad.gebai@polymtl.ca>" +__copyright__ = "Copyright 2012, Mohamad Gebai <mohamad.gebai@polymtl.ca>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@redhat.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */', + '', + '#define TRACEPOINT_DEFINE', + '#define TRACEPOINT_CREATE_PROBES', + '', + '/* If gcc version 4.7 or older is used, LTTng ust gives a warning when compiling with', + ' -Wredundant-decls.', + ' */', + '#pragma GCC diagnostic ignored "-Wredundant-decls"', + '', + '#include "generated-ust-provider.h"') diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py new file mode 100644 index 0000000000..f206eca6ec --- /dev/null +++ b/scripts/tracetool/format/ust_events_h.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .h for LTTng ust event description. +""" + +__author__ = "Mohamad Gebai <mohamad.gebai@polymtl.ca>" +__copyright__ = "Copyright 2012, Mohamad Gebai <mohamad.gebai@polymtl.ca>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@redhat.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */', + '', + '#undef TRACEPOINT_PROVIDER', + '#define TRACEPOINT_PROVIDER qemu', + '', + '#undef TRACEPOINT_INCLUDE_FILE', + '#define TRACEPOINT_INCLUDE_FILE ./generated-ust-provider.h', + '', + '#if !defined (TRACE__GENERATED_UST_H) || defined(TRACEPOINT_HEADER_MULTI_READ)', + '#define TRACE__GENERATED_UST_H', + '', + '#include "qemu-common.h"', + '#include <lttng/tracepoint.h>', + '', + '/*', + ' * LTTng ust 2.0 does not allow you to use TP_ARGS(void) for tracepoints', + ' * requiring no arguments. We define these macros introduced in more recent' + ' * versions of LTTng ust as a workaround', + ' */', + '#ifndef _TP_EXPROTO1', + '#define _TP_EXPROTO1(a) void', + '#endif', + '#ifndef _TP_EXDATA_PROTO1', + '#define _TP_EXDATA_PROTO1(a) void *__tp_data', + '#endif', + '#ifndef _TP_EXDATA_VAR1', + '#define _TP_EXDATA_VAR1(a) __tp_data', + '#endif', + '#ifndef _TP_EXVAR1', + '#define _TP_EXVAR1(a)', + '#endif', + '') + +def end(events): + out('#endif /* TRACE__GENERATED_UST_H */', + '', + '/* This part must be outside ifdef protection */', + '#include <lttng/tracepoint-event.h>') |