~ubuntu-branches/ubuntu/vivid/qemu/vivid

« back to all changes in this revision

Viewing changes to scripts/dump-guest-memory.py

  • Committer: Package Import Robot
  • Author(s): Serge Hallyn
  • Date: 2014-02-25 22:31:43 UTC
  • mfrom: (1.8.5)
  • Revision ID: package-import@ubuntu.com-20140225223143-odhqxfc60wxrjl15
Tags: 2.0.0~rc1+dfsg-0ubuntu1
* Merge 2.0.0-rc1
* debian/rules: consolidate ppc filter entries.
* Move qemu-system-arch64 into qemu-system-arm
* debian/patches/define-trusty-machine-type.patch: define a trusty machine
  type, currently the same as pc-i440fx-2.0, to put is in a better position
  to enable live migrations from trusty onward.  (LP: #1294823)
* debian/control: build-dep on libfdt >= 1.4.0  (LP: #1295072)
* Merge latest upstream git to commit dc9528f
* Debian/rules:
  - remove -enable-uname-release=2.6.32
  - don't make the aarch64 target Ubuntu-specific.
* Remove patches which are now upstream:
  - fix-smb-security-share.patch
  - slirp-smb-redirect-port-445-too.patch 
  - linux-user-Implement-sendmmsg-syscall.patch (better version is upstream)
  - signal-added-a-wrapper-for-sigprocmask-function.patch
  - ubuntu/signal-sigsegv-protection-on-do_sigprocmask.patch
  - ubuntu/Don-t-block-SIGSEGV-at-more-places.patch
  - ubuntu/ppc-force-cpu-threads-count-to-be-power-of-2.patch
* add link for /usr/share/qemu/bios-256k.bin
* Remove all linaro patches.
* Remove all arm64/ patches.  Many but not all are upstream.
* Remove CVE-2013-4377.patch which is upstream.
* debian/control-in: don't make qemu-system-aarch64 ubuntu-specific

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This python script adds a new gdb command, "dump-guest-memory". It
 
2
# should be loaded with "source dump-guest-memory.py" at the (gdb)
 
3
# prompt.
 
4
#
 
5
# Copyright (C) 2013, Red Hat, Inc.
 
6
#
 
7
# Authors:
 
8
#   Laszlo Ersek <lersek@redhat.com>
 
9
#
 
10
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
 
11
# the COPYING file in the top-level directory.
 
12
#
 
13
# The leading docstring doesn't have idiomatic Python formatting. It is
 
14
# printed by gdb's "help" command (the first line is printed in the
 
15
# "help data" summary), and it should match how other help texts look in
 
16
# gdb.
 
17
 
 
18
import struct
 
19
 
 
20
class DumpGuestMemory(gdb.Command):
 
21
    """Extract guest vmcore from qemu process coredump.
 
22
 
 
23
The sole argument is FILE, identifying the target file to write the
 
24
guest vmcore to.
 
25
 
 
26
This GDB command reimplements the dump-guest-memory QMP command in
 
27
python, using the representation of guest memory as captured in the qemu
 
28
coredump. The qemu process that has been dumped must have had the
 
29
command line option "-machine dump-guest-core=on".
 
30
 
 
31
For simplicity, the "paging", "begin" and "end" parameters of the QMP
 
32
command are not supported -- no attempt is made to get the guest's
 
33
internal paging structures (ie. paging=false is hard-wired), and guest
 
34
memory is always fully dumped.
 
35
 
 
36
Only x86_64 guests are supported.
 
37
 
 
38
The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are
 
39
not written to the vmcore. Preparing these would require context that is
 
40
only present in the KVM host kernel module when the guest is alive. A
 
41
fake ELF note is written instead, only to keep the ELF parser of "crash"
 
42
happy.
 
43
 
 
44
Dependent on how busted the qemu process was at the time of the
 
45
coredump, this command might produce unpredictable results. If qemu
 
46
deliberately called abort(), or it was dumped in response to a signal at
 
47
a halfway fortunate point, then its coredump should be in reasonable
 
48
shape and this command should mostly work."""
 
49
 
 
50
    TARGET_PAGE_SIZE = 0x1000
 
51
    TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
 
52
 
 
53
    # Various ELF constants
 
54
    EM_X86_64   = 62        # AMD x86-64 target machine
 
55
    ELFDATA2LSB = 1         # little endian
 
56
    ELFCLASS64  = 2
 
57
    ELFMAG      = "\x7FELF"
 
58
    EV_CURRENT  = 1
 
59
    ET_CORE     = 4
 
60
    PT_LOAD     = 1
 
61
    PT_NOTE     = 4
 
62
 
 
63
    # Special value for e_phnum. This indicates that the real number of
 
64
    # program headers is too large to fit into e_phnum. Instead the real
 
65
    # value is in the field sh_info of section 0.
 
66
    PN_XNUM = 0xFFFF
 
67
 
 
68
    # Format strings for packing and header size calculation.
 
69
    ELF64_EHDR = ("4s" # e_ident/magic
 
70
                  "B"  # e_ident/class
 
71
                  "B"  # e_ident/data
 
72
                  "B"  # e_ident/version
 
73
                  "B"  # e_ident/osabi
 
74
                  "8s" # e_ident/pad
 
75
                  "H"  # e_type
 
76
                  "H"  # e_machine
 
77
                  "I"  # e_version
 
78
                  "Q"  # e_entry
 
79
                  "Q"  # e_phoff
 
80
                  "Q"  # e_shoff
 
81
                  "I"  # e_flags
 
82
                  "H"  # e_ehsize
 
83
                  "H"  # e_phentsize
 
84
                  "H"  # e_phnum
 
85
                  "H"  # e_shentsize
 
86
                  "H"  # e_shnum
 
87
                  "H"  # e_shstrndx
 
88
                 )
 
89
    ELF64_PHDR = ("I"  # p_type
 
90
                  "I"  # p_flags
 
91
                  "Q"  # p_offset
 
92
                  "Q"  # p_vaddr
 
93
                  "Q"  # p_paddr
 
94
                  "Q"  # p_filesz
 
95
                  "Q"  # p_memsz
 
96
                  "Q"  # p_align
 
97
                 )
 
98
 
 
99
    def __init__(self):
 
100
        super(DumpGuestMemory, self).__init__("dump-guest-memory",
 
101
                                              gdb.COMMAND_DATA,
 
102
                                              gdb.COMPLETE_FILENAME)
 
103
        self.uintptr_t     = gdb.lookup_type("uintptr_t")
 
104
        self.elf64_ehdr_le = struct.Struct("<%s" % self.ELF64_EHDR)
 
105
        self.elf64_phdr_le = struct.Struct("<%s" % self.ELF64_PHDR)
 
106
 
 
107
    def int128_get64(self, val):
 
108
        assert (val["hi"] == 0)
 
109
        return val["lo"]
 
110
 
 
111
    def qtailq_foreach(self, head, field_str):
 
112
        var_p = head["tqh_first"]
 
113
        while (var_p != 0):
 
114
            var = var_p.dereference()
 
115
            yield var
 
116
            var_p = var[field_str]["tqe_next"]
 
117
 
 
118
    def qemu_get_ram_block(self, ram_addr):
 
119
        ram_blocks = gdb.parse_and_eval("ram_list.blocks")
 
120
        for block in self.qtailq_foreach(ram_blocks, "next"):
 
121
            if (ram_addr - block["offset"] < block["length"]):
 
122
                return block
 
123
        raise gdb.GdbError("Bad ram offset %x" % ram_addr)
 
124
 
 
125
    def qemu_get_ram_ptr(self, ram_addr):
 
126
        block = self.qemu_get_ram_block(ram_addr)
 
127
        return block["host"] + (ram_addr - block["offset"])
 
128
 
 
129
    def memory_region_get_ram_ptr(self, mr):
 
130
        if (mr["alias"] != 0):
 
131
            return (self.memory_region_get_ram_ptr(mr["alias"].dereference()) +
 
132
                    mr["alias_offset"])
 
133
        return self.qemu_get_ram_ptr(mr["ram_addr"] & self.TARGET_PAGE_MASK)
 
134
 
 
135
    def guest_phys_blocks_init(self):
 
136
        self.guest_phys_blocks = []
 
137
 
 
138
    def guest_phys_blocks_append(self):
 
139
        print "guest RAM blocks:"
 
140
        print ("target_start     target_end       host_addr        message "
 
141
               "count")
 
142
        print ("---------------- ---------------- ---------------- ------- "
 
143
               "-----")
 
144
 
 
145
        current_map_p = gdb.parse_and_eval("address_space_memory.current_map")
 
146
        current_map = current_map_p.dereference()
 
147
        for cur in range(current_map["nr"]):
 
148
            flat_range   = (current_map["ranges"] + cur).dereference()
 
149
            mr           = flat_range["mr"].dereference()
 
150
 
 
151
            # we only care about RAM
 
152
            if (not mr["ram"]):
 
153
                continue
 
154
 
 
155
            section_size = self.int128_get64(flat_range["addr"]["size"])
 
156
            target_start = self.int128_get64(flat_range["addr"]["start"])
 
157
            target_end   = target_start + section_size
 
158
            host_addr    = (self.memory_region_get_ram_ptr(mr) +
 
159
                            flat_range["offset_in_region"])
 
160
            predecessor = None
 
161
 
 
162
            # find continuity in guest physical address space
 
163
            if (len(self.guest_phys_blocks) > 0):
 
164
                predecessor = self.guest_phys_blocks[-1]
 
165
                predecessor_size = (predecessor["target_end"] -
 
166
                                    predecessor["target_start"])
 
167
 
 
168
                # the memory API guarantees monotonically increasing
 
169
                # traversal
 
170
                assert (predecessor["target_end"] <= target_start)
 
171
 
 
172
                # we want continuity in both guest-physical and
 
173
                # host-virtual memory
 
174
                if (predecessor["target_end"] < target_start or
 
175
                    predecessor["host_addr"] + predecessor_size != host_addr):
 
176
                    predecessor = None
 
177
 
 
178
            if (predecessor is None):
 
179
                # isolated mapping, add it to the list
 
180
                self.guest_phys_blocks.append({"target_start": target_start,
 
181
                                               "target_end"  : target_end,
 
182
                                               "host_addr"   : host_addr})
 
183
                message = "added"
 
184
            else:
 
185
                # expand predecessor until @target_end; predecessor's
 
186
                # start doesn't change
 
187
                predecessor["target_end"] = target_end
 
188
                message = "joined"
 
189
 
 
190
            print ("%016x %016x %016x %-7s %5u" %
 
191
                   (target_start, target_end, host_addr.cast(self.uintptr_t),
 
192
                    message, len(self.guest_phys_blocks)))
 
193
 
 
194
    def cpu_get_dump_info(self):
 
195
        # We can't synchronize the registers with KVM post-mortem, and
 
196
        # the bits in (first_x86_cpu->env.hflags) seem to be stale; they
 
197
        # may not reflect long mode for example. Hence just assume the
 
198
        # most common values. This also means that instruction pointer
 
199
        # etc. will be bogus in the dump, but at least the RAM contents
 
200
        # should be valid.
 
201
        self.dump_info = {"d_machine": self.EM_X86_64,
 
202
                          "d_endian" : self.ELFDATA2LSB,
 
203
                          "d_class"  : self.ELFCLASS64}
 
204
 
 
205
    def encode_elf64_ehdr_le(self):
 
206
        return self.elf64_ehdr_le.pack(
 
207
                                 self.ELFMAG,                 # e_ident/magic
 
208
                                 self.dump_info["d_class"],   # e_ident/class
 
209
                                 self.dump_info["d_endian"],  # e_ident/data
 
210
                                 self.EV_CURRENT,             # e_ident/version
 
211
                                 0,                           # e_ident/osabi
 
212
                                 "",                          # e_ident/pad
 
213
                                 self.ET_CORE,                # e_type
 
214
                                 self.dump_info["d_machine"], # e_machine
 
215
                                 self.EV_CURRENT,             # e_version
 
216
                                 0,                           # e_entry
 
217
                                 self.elf64_ehdr_le.size,     # e_phoff
 
218
                                 0,                           # e_shoff
 
219
                                 0,                           # e_flags
 
220
                                 self.elf64_ehdr_le.size,     # e_ehsize
 
221
                                 self.elf64_phdr_le.size,     # e_phentsize
 
222
                                 self.phdr_num,               # e_phnum
 
223
                                 0,                           # e_shentsize
 
224
                                 0,                           # e_shnum
 
225
                                 0                            # e_shstrndx
 
226
                                )
 
227
 
 
228
    def encode_elf64_note_le(self):
 
229
        return self.elf64_phdr_le.pack(self.PT_NOTE,         # p_type
 
230
                                       0,                    # p_flags
 
231
                                       (self.memory_offset -
 
232
                                        len(self.note)),     # p_offset
 
233
                                       0,                    # p_vaddr
 
234
                                       0,                    # p_paddr
 
235
                                       len(self.note),       # p_filesz
 
236
                                       len(self.note),       # p_memsz
 
237
                                       0                     # p_align
 
238
                                      )
 
239
 
 
240
    def encode_elf64_load_le(self, offset, start_hwaddr, range_size):
 
241
        return self.elf64_phdr_le.pack(self.PT_LOAD, # p_type
 
242
                                       0,            # p_flags
 
243
                                       offset,       # p_offset
 
244
                                       0,            # p_vaddr
 
245
                                       start_hwaddr, # p_paddr
 
246
                                       range_size,   # p_filesz
 
247
                                       range_size,   # p_memsz
 
248
                                       0             # p_align
 
249
                                      )
 
250
 
 
251
    def note_init(self, name, desc, type):
 
252
        # name must include a trailing NUL
 
253
        namesz = (len(name) + 1 + 3) / 4 * 4
 
254
        descsz = (len(desc)     + 3) / 4 * 4
 
255
        fmt = ("<"   # little endian
 
256
               "I"   # n_namesz
 
257
               "I"   # n_descsz
 
258
               "I"   # n_type
 
259
               "%us" # name
 
260
               "%us" # desc
 
261
               % (namesz, descsz))
 
262
        self.note = struct.pack(fmt,
 
263
                                len(name) + 1, len(desc), type, name, desc)
 
264
 
 
265
    def dump_init(self):
 
266
        self.guest_phys_blocks_init()
 
267
        self.guest_phys_blocks_append()
 
268
        self.cpu_get_dump_info()
 
269
        # we have no way to retrieve the VCPU status from KVM
 
270
        # post-mortem
 
271
        self.note_init("NONE", "EMPTY", 0)
 
272
 
 
273
        # Account for PT_NOTE.
 
274
        self.phdr_num = 1
 
275
 
 
276
        # We should never reach PN_XNUM for paging=false dumps: there's
 
277
        # just a handful of discontiguous ranges after merging.
 
278
        self.phdr_num += len(self.guest_phys_blocks)
 
279
        assert (self.phdr_num < self.PN_XNUM)
 
280
 
 
281
        # Calculate the ELF file offset where the memory dump commences:
 
282
        #
 
283
        #   ELF header
 
284
        #   PT_NOTE
 
285
        #   PT_LOAD: 1
 
286
        #   PT_LOAD: 2
 
287
        #   ...
 
288
        #   PT_LOAD: len(self.guest_phys_blocks)
 
289
        #   ELF note
 
290
        #   memory dump
 
291
        self.memory_offset = (self.elf64_ehdr_le.size +
 
292
                              self.elf64_phdr_le.size * self.phdr_num +
 
293
                              len(self.note))
 
294
 
 
295
    def dump_begin(self, vmcore):
 
296
        vmcore.write(self.encode_elf64_ehdr_le())
 
297
        vmcore.write(self.encode_elf64_note_le())
 
298
        running = self.memory_offset
 
299
        for block in self.guest_phys_blocks:
 
300
            range_size = block["target_end"] - block["target_start"]
 
301
            vmcore.write(self.encode_elf64_load_le(running,
 
302
                                                   block["target_start"],
 
303
                                                   range_size))
 
304
            running += range_size
 
305
        vmcore.write(self.note)
 
306
 
 
307
    def dump_iterate(self, vmcore):
 
308
        qemu_core = gdb.inferiors()[0]
 
309
        for block in self.guest_phys_blocks:
 
310
            cur  = block["host_addr"]
 
311
            left = block["target_end"] - block["target_start"]
 
312
            print ("dumping range at %016x for length %016x" %
 
313
                   (cur.cast(self.uintptr_t), left))
 
314
            while (left > 0):
 
315
                chunk_size = min(self.TARGET_PAGE_SIZE, left)
 
316
                chunk = qemu_core.read_memory(cur, chunk_size)
 
317
                vmcore.write(chunk)
 
318
                cur  += chunk_size
 
319
                left -= chunk_size
 
320
 
 
321
    def create_vmcore(self, filename):
 
322
        vmcore = open(filename, "wb")
 
323
        self.dump_begin(vmcore)
 
324
        self.dump_iterate(vmcore)
 
325
        vmcore.close()
 
326
 
 
327
    def invoke(self, args, from_tty):
 
328
        # Unwittingly pressing the Enter key after the command should
 
329
        # not dump the same multi-gig coredump to the same file.
 
330
        self.dont_repeat()
 
331
 
 
332
        argv = gdb.string_to_argv(args)
 
333
        if (len(argv) != 1):
 
334
            raise gdb.GdbError("usage: dump-guest-memory FILE")
 
335
 
 
336
        self.dump_init()
 
337
        self.create_vmcore(argv[0])
 
338
 
 
339
DumpGuestMemory()